adding Multithreading pthread.h
CIRCLE

I have been messing around for some time with a small game and now trying to add multithreading to the equation. Sure it is not needed but I just wanted to test it out this is what I got. Problem lies with in where at game start if just freezes my computer and I have to hit the reset button. I am unsure to what I am missing or making my mistake on, but right at program start it tries to do the multithreading area of the program which it shouldn't. Even removing the calls doesn't stop it from running.

skipping most of the non needed stuff Full program link can be added if needed

#SelectExpand
1#include <pthread.h> 2pthread_mutex_t threadsafe = PTHREAD_MUTEX_INITIALIZER; 3for (a = 1; a < maxa; a++){ 4 xi[a] = rand() % 5 + 1; 5 yi[a] = rand() % 5 + 1; 6 x[a] = rand() % 500 + 20; 7 y[a] = rand() %400 + 20; 8 c[a] = makecol(rand() % 253 + 1, rand() % 253 + 1, rand() % 253 + 1); 9 } 10void* thread0(void* data) 11{ 12 int my_thread_id = *((int*)data); 13 //For Pixelate Demo Addition 14 while(lives > 0) 15 { 16 if (pthread_mutex_lock(&threadsafe)) 17 textprintf_ex(screen,font,SCREEN_W/2 - (4*19),SCREEN_H/2,makecol(255,255,255),-1,"Error: Thread mutex was locked"); 18 for (a = 1; a < 100; a++){ 19 if (x[a] > SCREEN_W - 4 - xi[a] || x[a] < 3 - xi[a]) 20 xi[a] = -xi[a]; 21 if (y[a] > Floors.y - 4 - yi[a] || y[a] < 13 - yi[a]) 22 yi[a] = -yi[a]; 23 x[a] = x[a] + xi[a]; 24 y[a] = y[a] + yi[a]; 25 //putpixel(active_page,x[a],y[a],c[a]); 26 circlefill(active_page,x[a],y[a],BallSize / 5,c[a]); 27 //putpixel(active_page,x[a] - xi[a],y[a] - yi[a],makecol(0,0,0)); 28 } 29 if (pthread_mutex_unlock(&threadsafe)) 30 textprintf_ex(screen,font,SCREEN_W/2 - (4*19),SCREEN_H/2,makecol(255,255,255),-1,"Error: Thread mutex was unlocked"); 31 //For Pixelate demo addition 32 } 33 pthread_exit(NULL); 34 return NULL; 35} 36int id; 37 pthread_t pthread0; 38 int threadid0 = 0; 39 id = pthread_create(&pthread0, NULL, thread0, (void*)&threadid0); 40while(!key[KEY_ESC] && lives > 0) 41 { 42 pthread_mutex_lock(&threadsafe); 43 multicheck(); 44 singlecheck(); 45 movemouse(); 46 mousehold(); 47 drawback(); 48 checkdead(); 49 50 51 show_video_bitmap(active_page); 52 if (active_page == buffer) 53 active_page = buffer2; 54 else 55 active_page = buffer; 56 clear_to_color(active_page, makecol(0, 0, 0)); 57 pthread_mutex_unlock(&threadsafe); 58 rest(1); 59 60 } 61pthread_mutex_destroy(&threadsafe); 62 //destroy_bitmap(active_page); 63 64 65 allegro_exit(); 66 return; 67 68} 69END_OF_MAIN()

Most of the multithreading was taken directly from a book I was reading a while back. Game Programming All In One pg 405-413 for those who have the book.

#SelectExpand
1 2#include <pthread.h> 3#include "allegro.h" 4 5#define MODE GFX_AUTODETECT_FULLSCREEN 6#define WIDTH 640 7#define HEIGHT 480 8#define BLACK makecol(0,0,0) 9#define WHITE makecol(255,255,255) 10 11//define the sprite structure 12typedef struct SPRITE 13{ 14 int dir, alive; 15 int x,y; 16 int width,height; 17 int xspeed,yspeed; 18 int xdelay,ydelay; 19 int xcount,ycount; 20 int curframe,maxframe,animdir; 21 int framecount,framedelay; 22}SPRITE; 23 24//variables 25BITMAP *buffer; 26BITMAP *ballimg[32]; 27SPRITE theballs[2]; 28SPRITE *balls[2]; 29int done; 30int n; 31 32//create a new thread mutex to protect variables 33pthread_mutex_t threadsafe = PTHREAD_MUTEX_INITIALIZER; 34 35 36void erasesprite(BITMAP *dest, SPRITE *spr) 37{ 38 //erase the sprite 39 rectfill(dest, spr->x, spr->y, spr->x + spr->width, 40 spr->y + spr->height, BLACK); 41} 42 43void updatesprite(SPRITE *spr) 44{ 45 //update x position 46 if (++spr->xcount > spr->xdelay) 47 { 48 spr->xcount = 0; 49 spr->x += spr->xspeed; 50 } 51 52 //update y position 53 if (++spr->ycount > spr->ydelay) 54 { 55 spr->ycount = 0; 56 spr->y += spr->yspeed; 57 } 58 59 //update frame based on animdir 60 if (++spr->framecount > spr->framedelay) 61 { 62 spr->framecount = 0; 63 if (spr->animdir == -1) 64 { 65 if (--spr->curframe < 0) 66 spr->curframe = spr->maxframe; 67 } 68 else if (spr->animdir == 1) 69 { 70 if (++spr->curframe > spr->maxframe) 71 spr->curframe = 0; 72 } 73 } 74} 75 76//this version doesn't change speed, just direction 77void bouncesprite(SPRITE *spr) 78{ 79 //simple screen bouncing behavior 80 if (spr->x < 0) 81 { 82 spr->x = 0; 83 spr->xspeed = -spr->xspeed; 84 spr->animdir *= -1; 85 } 86 87 else if (spr->x > SCREEN_W - spr->width) 88 { 89 spr->x = SCREEN_W - spr->width; 90 spr->xspeed = -spr->xspeed; 91 spr->animdir *= -1; 92 } 93 94 if (spr->y < 0) 95 { 96 spr->y = 0; 97 spr->yspeed = -spr->yspeed; 98 spr->animdir *= -1; 99 } 100 101 else if (spr->y > SCREEN_H - spr->height) 102 { 103 spr->y = SCREEN_H - spr->height; 104 spr->yspeed = -spr->yspeed; 105 spr->animdir *= -1; 106 } 107} 108 109BITMAP *grabframe(BITMAP *source, 110 int width, int height, 111 int startx, int starty, 112 int columns, int frame) 113{ 114 BITMAP *temp = create_bitmap(width,height); 115 int x = startx + (frame % columns) * width; 116 int y = starty + (frame / columns) * height; 117 blit(source,temp,x,y,0,0,width,height); 118 return temp; 119} 120 121void loadsprites() 122{ 123 BITMAP *temp; 124 125 //load sprite images 126 temp = load_bitmap("sphere.bmp", NULL); 127 for (n=0; n<32; n++) 128 ballimg[n] = grabframe(temp,64,64,0,0,8,n); 129 destroy_bitmap(temp); 130 131 //initialize the sprite 132 for (n=0; n<2; n++) 133 { 134 balls[n] = &theballs[n]; 135 balls[n]->x = rand() % (SCREEN_W - ballimg[0]->w); 136 balls[n]->y = rand() % (SCREEN_H - ballimg[0]->h); 137 balls[n]->width = ballimg[0]->w; 138 balls[n]->height = ballimg[0]->h; 139 balls[n]->xdelay = 0; 140 balls[n]->ydelay = 0; 141 balls[n]->xcount = 0; 142 balls[n]->ycount = 0; 143 balls[n]->xspeed = 5; 144 balls[n]->yspeed = 5; 145 balls[n]->curframe = rand() % 32; 146 balls[n]->maxframe = 31; 147 balls[n]->framecount = 0; 148 balls[n]->framedelay = 0; 149 balls[n]->animdir = 1; 150 } 151} 152 153//this thread updates sprite 0 154void* thread0(void* data) 155{ 156 //get this thread id 157 int my_thread_id = *((int*)data); 158 159 //thread's main loop 160 while(!done) 161 { 162 //lock the mutex to protect variables 163 if (pthread_mutex_lock(&threadsafe)) 164 textout(buffer,font,"ERROR: thread mutex was locked",0,0,WHITE); 165 166 //erase sprite 0 167 erasesprite(buffer, balls[0]); 168 169 //update sprite 0 170 updatesprite(balls[0]); 171 172 //bounce sprite 0 173 bouncesprite(balls[0]); 174 175 //draw sprite 0 176 draw_sprite(buffer, ballimg[balls[0]->curframe], 177 balls[0]->x, balls[0]->y); 178 179 //print sprite number 180 textout(buffer, font, "0", balls[0]->x, balls[0]->y,WHITE); 181 182 //display sprite position 183 textprintf(buffer,font,0,10,WHITE,"THREAD ID %d, SPRITE (%3d,%3d)", 184 my_thread_id, balls[0]->x, balls[0]->y); 185 186 //unlock the mutex 187 if (pthread_mutex_unlock(&threadsafe)) 188 textout(buffer,font,"ERROR: thread mutex unlock error",0,0,WHITE); 189 190 //slow down (this thread only!) 191 rest(10); 192 } 193 194 // terminate the thread 195 pthread_exit(NULL); 196 197 return NULL; 198} 199 200//this thread updates sprite 1 201void* thread1(void* data) 202{ 203 //get this thread id 204 int my_thread_id = *((int*)data); 205 206 //thread's main loop 207 while(!done) 208 { 209 //lock the mutex to protect variables 210 if (pthread_mutex_lock(&threadsafe)) 211 textout(buffer,font,"ERROR: thread mutex was locked",0,0,WHITE); 212 213 //erase sprite 1 214 erasesprite(buffer, balls[1]); 215 216 //update sprite 1 217 updatesprite(balls[1]); 218 219 //bounce sprite 1 220 bouncesprite(balls[1]); 221 222 //draw sprite 1 223 draw_sprite(buffer, ballimg[balls[1]->curframe], 224 balls[1]->x, balls[1]->y); 225 226 //print sprite number 227 textout(buffer, font, "1", balls[1]->x, balls[1]->y,WHITE); 228 229 //display sprite position 230 textprintf(buffer,font,0,20,WHITE,"THREAD ID %d, SPRITE (%3d,%3d)", 231 my_thread_id, balls[1]->x, balls[1]->y); 232 233 //unlock the mutex 234 if (pthread_mutex_unlock(&threadsafe)) 235 textout(buffer,font,"ERROR: thread mutex unlock error",0,0,WHITE); 236 237 //slow down (this thread only!) 238 rest(20); 239 } 240 241 // terminate the thread 242 pthread_exit(NULL); 243 244 return NULL; 245} 246 247//program's primary thread 248void main(void) 249{ 250 int id; 251 pthread_t pthread0; 252 pthread_t pthread1; 253 int threadid0 = 0; 254 int threadid1 = 1; 255 256 //initialize 257 allegro_init(); 258 set_color_depth(16); 259 set_gfx_mode(MODE, WIDTH, HEIGHT, 0, 0); 260 srand(time(NULL)); 261 install_keyboard(); 262 install_timer(); 263 264 //create double buffer 265 buffer = create_bitmap(SCREEN_W,SCREEN_H); 266 267 //load ball sprite 268 loadsprites(); 269 270 //create the thread for sprite 0 271 id = pthread_create(&pthread0, NULL, thread0, (void*)&threadid0); 272 273 //create the thread for sprite 1 274 id = pthread_create(&pthread1, NULL, thread1, (void*)&threadid1); 275 276 //main loop 277 while (!key[KEY_ESC]) 278 { 279 //lock the mutex to protect double buffer 280 pthread_mutex_lock(&threadsafe); 281 282 //display title 283 textout(buffer, font, "MultiThread Program (ESC to quit)", 0, 0, WHITE); 284 285 //update the screen 286 acquire_screen(); 287 blit(buffer,screen,0,0,0,0,SCREEN_W-1,SCREEN_H-1); 288 release_screen(); 289 290 //unlock the mutex 291 pthread_mutex_unlock(&threadsafe); 292 293 //note there is no delay 294 } 295 296 //tell threads it's time to quit 297 done++; 298 rest(100); 299 300 //kill the mutex (thread protection) 301 pthread_mutex_destroy(&threadsafe); 302 303 //remove objects from memory 304 destroy_bitmap(buffer); 305 306 //delete sprites 307 for (n=0; n<32; n++) 308 destroy_bitmap(ballimg[n]); 309 310 return; 311} 312 313END_OF_MAIN()

http://www.geocities.com/jensen_305/Fallingballerr.zip

CGamesPlay
  • pthread_mutex_lock and pthread_mutex_unlock return 0 if they were successful but you treat that as an error condition. This is wrong, you don't treat it as an error condition.

  • Not to mention that your textprintf'ing to the screen when the mutex fails to lock defeats the purpose of using the mutex (you are using a mutex to say that it is okay to use Allegro's resources, so if the mutex locking fails, you can't use Allegro's resources).

  • Also, note that pthread_mutex_lock will not fail because the mutex was already locked. It will only fail if the mutex was destroyed. pthread_mutex_unlock will also only ever fail if the mutex was destroyed.

  • Your call to pthread_exit isn't needed, also.

Quote:

Game Programming All In One pg 405-413 for those who have the book.

We hear a ton of horrible programming practices gotten from this book.

[edit]
Edited for correctness.

GullRaDriel

Nothing else to say than what CGamesPlay said. Too slow :p

CIRCLE

Fixed a few things. But still remains that right at start the multithreaded part of the program is trying to start and just freezes. I used a graphical feature to show what was being multithreaded. At the start of the game is a main menu :) the "bouncing balls" should be applied when the game is started but instead they are starting at the main screen it gets (guessing) 3-5 loops and locks up. What I do not understand is what am I doing that starts the calls for the multithreaded parts of my program.

Compiles no errors just locks up on run.

GullRaDriel

Yeah, you must have a dead lock somewhere in your code. That is typically why threaded program froze.

CIRCLE

What should I be looking for in my program. What kind of things might cause this. It really doesn't effect the game play to have it out now, but perhaps later when there is more going on I may want to have it implemented just in case.

I code in a simple form "Newbish" so to speak.

1if (Balls[n].y > Floors.y - BallSize)
2 {
3 spls = 0;
4 splashed[n].x = Balls[n].x;
5 splashed[n].y = Balls[n].y;
6 s = n; //Mark location of Splash
7 play_sample((SAMPLE *)data[EXPLODE2_WAV].dat, volume, pan, pitch, 0);
8 splashes();
9 dead[n] = 1; //mark ball as dead
10 Balls[n].y = 0 + BallSize;
11 dropball();
12 lives--;
13 FrC = FrC - 25; //Changes the floor color on dropped ball
14 FbC = FbC + 25;
15 FgC = FgC;
16 }

Showing how simplistic I keep things.

Maybe I should add everything that adds multithreading until I get the error. Let me see what causes it exactly.

GullRaDriel

You should add some TRACE(...) in your code to see where you stop.

Step by step debugging could help too.

CIRCLE

Trace is new to me should try it out. I am fairly new to the programming thing and havent done any debugging stuff with my compiler yet will have to try that aswell.

hitting the post button to test

ok with just this line commented out the program works.

circlefill(active_page,x[a],y[a],BallSize / 5,c[a]);

if I add
textprintf_ex(screen,font,0,10,makecol(255,255,255),-1,"%i",x[1]);

instead of the circle movements it shows that it works but again starts right off the bat before it should. sorry I edit a lot.

Marco Radaelli
Quote:

What kind of things might cause this.

A deadlock happens when two subjects cross they need for objects. Example, you have P1, P2 which are processes and R1, R2 which are resources.

P1 owns R1 and needs R2, P2 owns R2 and needs R1. You have a deadlock because none of the processes can go on until the other releases the resource it's owning.

Dustin Dettmer

I would suggest you skip threading entirely and then come back to it later.

If you can't do that, then I would start off with some dead simple thread programs and work your way up. This app is a tad complicated.

CIRCLE

Taking the easy way is never my thing. I got it working and have isolated the problem. Now working around the problem is something else.

If I use...

circlefill(screen,xx[z],yy[z],BallSize / 5,c[z]);

instead of...

circlefill(active_page,xx[z],yy[z],BallSize / 5,c[z]);

So my next question is how do I tell the multithread to hold off and wait till the main program is done. I assumed that would be done with pthread_mutex_lock and pthread_mutex_unlock but I guess I am wrong.

Where would be a good place to learn about pthread?

Dustin Dettmer

It makes no sense whatsoever to use threads to handle paging.

CIRCLE

I am using the thread to handle background graphics. Having some fun. The paging is done out side the thread.

        show_video_bitmap(active_page);
        if (active_page == buffer)
         active_page = buffer2;
        else
           active_page = buffer;           
        clear_to_color(active_page, makecol(0, 0, 0));

question was on how to tell the threaded area of my program to hold on while the main program uses a resource.

if I use

for (z = 1; z < 1000; z++){
        circlefill(active_page,xx[z],yy[z],BallSize / 5,c[z]);
        }

outside the threaded program it works fine. But the point is to get it to work inside the threaded area.

nonnus29
Quote:

Taking the easy way is never my thing. I got it working and have isolated the problem. Now working around the problem is something else.

If you're not into 'easy things' then why are you asking these guys to write your code for you instead of you studying the relevant subject ie threads and communicating process with semaphores and mutexes?

CIRCLE

Wow tons of help. I understand as to how they work not to why it is not working.

another example which SHOULD lock variables till it is done but doesn't

1void* thread0(void* data)
2{
3 int my_thread_id = *((int*)data);
4 //For Pixelate Demo Addition
5 while (lives > 0)
6 {
7 pthread_mutex_lock(&threadsafe);
8 for (z = 1; z < 20; z++){
9 if (xx[z] > SCREEN_W - 4 - xi[z] || xx[z] < 3 - xi[z])
10 xi[z] = -xi[z];
11 if (yy[z] > Floors.y - 4 - yi[z] || yy[z] < 13 - yi[z])
12 yi[z] = -yi[z];
13 xx[z] = xx[z] + xi[z];
14 yy[z] = yy[z] + yi[z];
15 //trying to get the active_page to work in here
16 circlefill(active_page,xx[z],yy[z],BallSize / 5,c[z]);
17 }
18 pthread_mutex_unlock(&threadsafe);
19 rest(1);
20 //For Pixelate demo addition
21 }
22 //pthread_exit(NULL);
23 return NULL;

but instead of helping find what seems to everyone else like a small mistake I get the run around.

nonnus29

The point is it's not small, multi threading is really hard. Can anyone gurantee that allegro, or direct x or anything else isn't running a thread in the background that's causing deadlocks with your thread? I can't.

GullRaDriel

I can. I use threads a lot with allegro, and all the deadlock I had were caused by me.

CIRCLE

This also a deadlock caused by me. Simple comment out showed me what was doing it aswell.

If I just use a BITMAP that my main program does not have access to like "screen" then everything works fine. Only problem is it still slows the main program down where to my understanding each thread should have its own speed.

GullRaDriel

Yeah, but if they both access the same critical section, they will wait each other to free the locked ressource before doing some stuff.

CIRCLE

I was also under the impression when I pthread_mutex_lock it would lock the resources it was using till pthread_mutex_unlock was passed both my main loop and my thread use this. While screen works both can not use active_page even when locking for some reason.

If I could lock out the main loop from using the BITMAP active_page when the thread was using it things would be easier and vice versa.

Kitty Cat

Locking a mutex prevents other threads from locking the same mutex. If a thread locks a mutex, and another tries to lock the same mutex, the second will block (wait) until the first thread unlocks the mutex. THis makes it so two threads can't touch the same data at the same time.

Quote:

my understanding each thread should have its own speed.

When two threads handle the same data, they have to do syncronization (usually through mutexes) so they don't step on each others toes. This quickly leads to performance degradation on both parts. Using threads is not a recipe for speed. It's a tool to help make more efficient use of resources when properly handled.

CIRCLE

Thanks to those that helped and gave advice. I was able to get my program to work. Although not entirely the way I wanted but work none the less.

To GullRaDriel for pointing out that I may have a deadlock. Once figured out where was fairly easy to fix. Thanks.

To Marco Radaelli for the explination of exactly what happens when a deadlock occurs. Thanks

To Dustin Dettmer helping to get me to realize I am over my head :)

To nonnus29 with that awsome link with all sorts of information about threads and much more. Thanks.

Thanks for all the help.

-Consider this thread closed.

Thread #591243. Printed from Allegro.cc