You don't explain well why you should check for the is_ready flag, you skip explaining lock_cond, there were some typo's and I still can't log into the wiki[1].Oh yeah, and good job Personally I'd like to squeeze out the C++ bits, and mash it all into a smooth Allegro example. It's gonna be loovely. </ tvchefimpersonation>

In that loose {}, you should be checking the return values of al_create_mutex() and al_create_cond(), if they failed to be created, then either throwing an exception, or set a value to mark the class as bad.

____________________________________________________________________________________________"c is much better than c++ if you don't need OOP simply because it's smaller and requires less load time." - alethiophileOMG my sides are hurting from laughing so hard... :D

Some things I don't understand, some things I think should be improved, but don't know how, and some things I know are wrong but-what-the-hey are marked with triple question marks \???I also think this should be either god-grieving C++ or satanlicious C: not a mix.You know what would be kinky? If the parent thread used a (second) condition to start the two child threads.<insert worst profanity /> It seems file upload I broken too. (But only for me of course. And, yes, I know about the new backwards editing procedure.)

#SelectExpand

1'''Under Construction''' 2 3In this section we'll learn how to use the Allegro 5 threading interface. 4=== Abstract === 5 6In this example we're going to use the same bitmap created in the previous example, but instead of changing its axis with the mouse, we're going to let two thread to modified them. 7 8<source lang="c"> 9#include <stdio.h> 10#include <allegro5/allegro.h> 11 12class DATA{ 13 14 public: 15 16 ALLEGRO_MUTEX *mutex; 17 ALLEGRO_COND *cond; 18 float posiX; 19 float posiY; 20 bool modi_X; 21 bool ready; 22 23 DATA() : mutex(al_create_mutex()), 24 cond(al_create_cond()), 25 posiX (0), 26 posiY (0), 27 modi_X(false), 28 ready (false) {} 29 30 ~DATA(){ 31 32 al_destroy_mutex(mutex); 33 al_destroy_cond(cond); 34 35 } 36 37}; 38 39const float FPS = 30; 40const int SCREEN_W = 640; 41const int SCREEN_H = 480; 42const int BOUNCER_SIZE = 32; 43 44static void *Func_Thread(ALLEGRO_THREAD *thr, void *arg); 45 46int main(int argc, char **argv) 47{ 48 ALLEGRO_DISPLAY *display = NULL; 49 ALLEGRO_EVENT_QUEUE *event_queue = NULL; 50 ALLEGRO_TIMER *timer = NULL; 51 ALLEGRO_BITMAP *bouncer = NULL; 52 ALLEGRO_THREAD *thread_1 = NULL; 53 ALLEGRO_THREAD *thread_2 = NULL; 54 55 bool redraw = true; 56 57 if(!al_init()) { 58 fprintf(stderr, "failed to initialize allegro!\n"); 59 return -1; 60 } 61 62 if(!al_install_mouse()) { 63 fprintf(stderr, "failed to initialize the mouse!\n"); 64 return -1; 65 } 66 67 timer = al_create_timer(1.0 / FPS); 68 if(!timer) { 69 fprintf(stderr, "failed to create timer!\n"); 70 return -1; 71 } 72 73 display = al_create_display(SCREEN_W, SCREEN_H); 74 if(!display) { 75 fprintf(stderr, "failed to create display!\n"); 76 al_destroy_timer(timer); 77 return -1; 78 } 79 80 bouncer = al_create_bitmap(BOUNCER_SIZE, BOUNCER_SIZE); 81 if(!bouncer) { 82 fprintf(stderr, "failed to create bouncer bitmap!\n"); 83 al_destroy_display(display); 84 al_destroy_timer(timer); 85 return -1; 86 } 87 88 al_set_target_bitmap(bouncer); 89 al_clear_to_color(al_map_rgb(255, 0, 255)); 90 al_set_target_bitmap(al_get_backbuffer(display)); 91 event_queue = al_create_event_queue(); 92 93 if(!event_queue) { 94 fprintf(stderr, "failed to create event_queue!\n"); 95 al_destroy_bitmap(bouncer); 96 al_destroy_display(display); 97 al_destroy_timer(timer); 98 return -1; 99 } 100 101 al_register_event_source(event_queue, al_get_display_event_source(display)); 102 al_register_event_source(event_queue, al_get_timer_event_source(timer)); 103 al_register_event_source(event_queue, al_get_mouse_event_source()); 104 al_clear_to_color(al_map_rgb(0,0,0)); 105 al_flip_display(); 106 al_start_timer(timer); 107 108 DATA data; 109 110 thread_1 = al_create_thread(Func_Thread, &data); 111 al_start_thread(thread_1); 112 113 al_lock_mutex(data.mutex); 114 while (!data.ready){ 115 116 al_wait_cond(data.cond, data.mutex); 117 118 } 119 al_unlock_mutex(data.mutex); 120 121 al_lock_mutex(data.mutex); 122 data.modi_X = true; 123 data.ready = false; 124 al_unlock_mutex(data.mutex); 125 126 thread_2 = al_create_thread(Func_Thread, &data); 127 al_start_thread(thread_2); 128 129 al_lock_mutex(data.mutex); 130 while (!data.ready){ 131 132 al_wait_cond(data.cond, data.mutex); 133 134 } 135 al_unlock_mutex(data.mutex); 136 137 138 while(1) 139 { 140 ALLEGRO_EVENT ev; 141 al_wait_for_event(event_queue, &ev); 142 143 if(ev.type == ALLEGRO_EVENT_TIMER) { 144 redraw = true; 145 } 146 else if(ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) { 147 break; 148 } 149 else if(ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN) { 150 break; 151 } 152 if(redraw && al_is_event_queue_empty(event_queue)) { 153 redraw = false; 154 155 al_lock_mutex(data.mutex); 156 data.modi_X = true; 157 data.ready = false; 158 al_unlock_mutex(data.mutex); 159 160 al_draw_bitmap(bouncer, data.posiX, data.posiY, 0); 161 162 163 al_flip_display(); 164 } 165 } 166 al_destroy_thread(thread_1); 167 al_destroy_thread(thread_2); 168 169 al_destroy_bitmap(bouncer); 170 al_destroy_timer(timer); 171 al_destroy_display(display); 172 al_destroy_event_queue(event_queue); 173 174 return 0; 175} 176 177 static void *Func_Thread(ALLEGRO_THREAD *thr, void *arg){ 178 179 DATA *data = (DATA*) arg; 180 float num = 0.1; 181 182 al_lock_mutex(data->mutex); 183 184 bool modi_X = data->modi_X; 185 data->ready = true; 186 al_broadcast_cond(data->cond); 187 188 al_unlock_mutex(data->mutex); 189 190 while(!al_get_thread_should_stop(thr)){ 191 192 al_lock_mutex(data->mutex); 193 if(modi_X) 194 data->posiX += num; 195 else 196 data->posiY += num; 197 al_unlock_mutex(data->mutex); 198 199 al_rest(0.01); 200 201 } 202 203 204 return NULL; 205 } 206</source> 207 208=== Let's get started === 209 210 211<source lang="c"> 212class DATA{ 213 214 public:
215 216ALLEGRO_MUTEX*mutex; 217ALLEGRO_COND*cond; 218float posiX; 219float posiY; 220bool modi_X; 221bool ready; 222 223 DATA(): mutex(al_create_mutex()),
224 cond(al_create_cond()),
225 posiX (0),
226 posiY (0),
227 modi_X(false),
228 ready (false){} 229 230 ~DATA(){ 231 232al_destroy_mutex(mutex); 233al_destroy_cond(cond); 234 235} 236 237}; 238</source>
239 240First we create a class, which is going to contain the data we want to exchange between threads.
241 242<source lang="c">ALLEGRO_MUTEX*mutex</source>
243 244Here we declare a /mutex/(mutual exclusion). In essence it's a flag that's supposed to tell other threads to leave some data alone. You can't set it directly, though. This should become clear why. 245 246<source lang="c">ALLEGRO_COND *cond;</source> 247 248Here we declare a /condition/. It is sort of a sign that can asynchrously be picked up by other threads. Again you'll have to use functions to use it.
249 250<source lang="c"> 251float posiX; 252float posiY; 253bool modi_X; 254bool ready;</source>
255 256<code>posiX</code>, and<code>posiY</code> are the X, and Y coordinates of our bitmap, <code>modi_X</code> and<code>ready</code> are two flags that we're going to use later on. 257 258<source lang="c"> 259 260 DATA() : mutex(al_create_mutex()), 261 cond(al_create_cond()), 262 posiX (0), 263 posiY (0), 264 modi_X(false), 265 ready (false) {} 266 267 ~DATA(){ 268 269 al_destroy_mutex(mutex); 270 al_destroy_cond(cond); 271 272 } 273 274</source> 275 276Class's constructor, using a initialization list. Followed by the destructor. '''Note:''' you cannot make an instance of this object on global scope, because <code>al_create_mutex</code> and<code>al_create_cond</code> would be called before <code>al_init</code>.
277 278<source lang="c">ALLEGRO_THREAD*thread_1 = NULL; 279ALLEGRO_THREAD*thread_2 = NULL;</source>
280 281A /thread/ is like a program running _separately_. Separately means you have no idea how far it has run, and neither do they know about eachothers globals???.
282 283<source lang="c"> 284 285 DATA data; 286 287 thread_1 =al_create_thread(Func_Thread, &data); 288</source>
289<code>data</code> is neatly created after <code>al_init</code>. <code>thread_1</code> is allocated. Don't forget to deallocate it afterwards. 290???Shouldn't you check it's value? The thread will be running the function <code>Func_thread</code> and getting the pointer <code>*data</code> as its argument when it starts. ???(Or only after it started it head hurts) Because this thread holds all the control information of <code>thread_1</code>, this thread is called the /parent/ thread and <code>thread_1</code> is called a /child/ thread. 291<source lang="c"> 292 al_start_thread(thread_1); 293</source> 294Now, after god knows how much time Func_Thread will be called god knows how fast, under the nominator of <code>thread_1</code>. 295Next lines get technical, but can be seen as 1 unit: 296<source lang="c"> 297 al_lock_mutex(data.mutex); 298 while (!data.ready){ 299 300 al_wait_cond(data.cond, data.mutex); 301 302 } 303 al_unlock_mutex(data.mutex); 304<\source> 305Firstly, we /lock/ a /mutex/. An analogy would be shouting "it's my turn now!". If this thread is the first one to shout, it gets the mutex. If an other thread /locked/ the /mutex/ first, this thread will have to wait until that particular thread /unlocks/ it. 306<code>al_wait_cond</code> takes a /locked mutex/ as an argument??? and then unlocks it. Meanwhile it pauses this thread until the /condition/ is /signaled/. There are two reasons you can't use this /condition/ directly to signal something has happened. Number one is: multiple threads could have been waiting for the same condition and have responded to that condition, before you get the chance to do anything.??? Number two is: the thread can also continue whenever the OS feels like kicking it. 307Now you should understand the reason a <code>while</code> loop was put around <code>al_wait_cond</code>; and a variable named <code>ready</code> was put into the <code>data</code> structure. 308<source lang="c"> 309 al_lock_mutex(data.mutex); 310 data.modi_X = true; //???Don't you actually want to run these lines before the previous al_unlock_mutex 311 data.ready = false; //???Don't you actually want to run these lines before the previous al_unlock_mutex 312 al_unlock_mutex(data.mutex); 313 314 thread_2 = al_create_thread(Func_Thread, &data); 315 al_start_thread(thread_2); 316 317 al_lock_mutex(data.mutex); 318 while (!data.ready){ 319 320 al_wait_cond(data.cond, data.mutex); 321 322 } 323 al_unlock_mutex(data.mutex); 324 325</source> 326 327Everything until here should be already clear. 328 329# ??? In what order are you discussing the file? In what order was I discussing the file? I already discussed this??? 330<source lang="c">DATA data;</source> 331 332Very obvious, we create our object '''after''' initializing allegro since it's using some allegro functions. 333 334<source lang="c">thread_1 = al_create_thread(Func_Thread, &data); 335al_start_thread(thread_1);</source> 336 337Here we create our thread and immediately the data is sent. <code>Func_Thread</code> it's a pointer to a function and <code>&data</code> it's the address of our recently created object. And since when a thread is created, it is initially in a suspended state, we need to call <code>al_start_thread(thread_1)</code> to call its actual execution. 338 339So, you write your own function that matches this prototype: 340 341<source lang="c"> static void *Func_Thread(ALLEGRO_THREAD *thr, void *arg) </source> 342 343Whatever you do inside this functions it's going to be running on a different thread. 344 345<source lang="c"> 346 347static void *Func_Thread(ALLEGRO_THREAD *thr, void *arg){ 348 349 DATA *data = (DATA*) arg; 350 float num = 0.1; 351 352 al_lock_mutex(data->mutex); 353 354 bool modi_X = data->modi_X; 355 data->ready = true; 356 al_broadcast_cond(data->cond); 357 358 al_unlock_mutex(data->mutex); 359 360 while(!al_get_thread_should_stop(thr)){ 361 362 al_lock_mutex(data->mutex); 363 if(modi_X) 364 data->posiX += num; 365 else 366 data->posiY += num; 367 al_unlock_mutex(data->mutex); 368 369 al_rest(0.01); 370 371 } 372 373 374 return NULL; 375 } 376</source> 377 378The data you sent using <code>al_create_thread()</code> it's received by this function as a void pointer data type <code>(void *arg)</code>. So the first thing we need to do here inside, is convert our object back to a DATA data type, which is our original class. 379 380<source lang="c"> DATA *data = (DATA*) arg; </source> 381 382With some typecasting it's done. Remember that data now it's a pointer to a DATA object so we'll use <code>data->example</code> instead of <code>data.example</code>. 383 384<source lang="c"> float num = 0.1; </source> 385 386This is the value we're going to add to our X or Y axis. 387 388<source lang="c"> al_lock_mutex(data->mutex); </source> 389 390And WTH is this?, well, this is how we tell allegro that one of our many threads it's going to use a shared resource. Our data object is also available to our main thread, and it will be available to two more threads. Remember: whichever thread shouts: "My turn!" first, should get sole access to this sort of shared data... for as long as its turn lasts. 391 392So for that reason each time we use our data in each thread we need to lock it first. And when we're done we can unlock it. 393 394<source lang="c"> bool modi_X = data->modi_X; 395 data->ready = true; 396 al_broadcast_cond(data->cond); </source> 397 398So, here we're creating a bool variable , and assign it a value, as you can see when we created our DATA object this value is initialized as false, so the <code>bool modi_X</code> variable of this thread the first time it is run, is going to be false. We're doing this because we need to tell the parent thread to modified the X value and the other to modified the Y value, so the thread which receive <code>modi_X</code> (Modify X) as true, is going to do exactly that, modified the X Axi, and the thread which receive <code>modi_X</code> as false, is going to modified the Y axis. 399 400And since this is the first thread ???true but actually not very generic case:(we haven't create/started the second thread) it's going to recive the <code>modi_X</code> value as false. 401 402<source lang="c"> data->ready = true; </source> 403 404This is the second flag of our data object, we need to use this, because our parent thread needs to wait until we have assigned the <code>modi_X</code> value. Since we're working with threads we can never assume the order or speed in which two things in parallel happen. So this flag along with the cond struct, allow us to tell our parent thread, when it can move on. 405 406 407 408<source lang="c">al_lock_mutex(data.mutex); 409 if (!data.ready) 410 al_wait_cond(data.cond, data.mutex); 411 412 al_unlock_mutex(data.mutex) 413 </source> 414 415This is what we have in our parent thread, basically if data.ready isn't true that means that the second tread haven't initialized the modi_X variable, so we need to wait. and how do we know when the second thread it's done?, how much do we need to wait? here is where conditions come in. 416 417<source lang="c">al_wait_cond(data.cond, data.mutex);</source> 418 419<code>al_wait_cond</code> it's waiting until a signal, a broadcast signal it's sent, and as you can see: 420 421<source lang="c"> al_broadcast_cond(data->cond) </source> 422 423Our thread function (<code>Func_Thread()</code>) 6th line does exactly that. 424 425So if for some reason our parent thread arrives to: 426 427<source lang="c">while (!data.ready){</source> 428 429and ready isn't true, that means we need to wait, so we enter to the loop, and immediately <code>al_wait_cond(data.cond, data.mutex)</code> is called. 430 431On the other hand if when the parent thread arrives to: 432 433<source lang="c">while (!data.ready){</source> 434 435and ready is true, that means that we can move on, so we're not going to wait for any condition. 436 437<source lang="c">al_broadcast_cond(data->cond); </source> 438 439So for that reason, after assign the threads modi_X to false, we change the ready flag to true, to tell to the parent thread that he doesn't need to wait, because the first thread it's ready, but in case he is waiting, we also broadcast our condition so he can move on with this line. 440 441<source lang="c"> al_unlock_mutex(data->mutex); </source> 442 443Finally we unlock our mutex to tell Allegro that we're not going to be using the data object anymore so other threads can start using it. 444 445Ok, we need to go back to our parent thread, and make a little review: 446 447 448 449<source lang="c" line start=0> 450 thread_1 = al_create_thread(Func_Thread, &data); 451 al_start_thread(thread_1); 452 453 al_lock_mutex(data.mutex); 454 if (!data.ready) 455 456 al_wait_cond(data.cond, data.mutex); 457 458 al_unlock_mutex(data.mutex); 459 460 al_lock_mutex(data.mutex); 461 data.modi_X = true; 462 data.ready = false; 463 al_unlock_mutex(data.mutex); 464 465 thread_2 = al_create_thread(Func_Thread, &data); 466 al_start_thread(thread_2); 467 468 al_lock_mutex(data.mutex); 469 if (!data.ready) 470 471 al_wait_cond(data.cond, data.mutex); 472 473 al_unlock_mutex(data.mutex); 474 475</source> 476 477=== Quick Review === 478 479* In line 1 we create the thread. 480* In line 2 we start the thread. 481* in line 4 we lock our mutex because we're going to check if <code>ready</code> it's true 482* In case it's true that means our second tread for some reason haven't initialized its <code>mosi_X</code> bool variable, so we need to wait. 483* In line 6 we start waiting. 484* Ok our second thread has broadcast the conditional that means he is ready. 485* In line 8 we unlock the mutex because we're not going to use it anymore. 486* In line 10 we lock the mutex again, we need to change our variables because we're about to create another thread, and if we don't change the flags the second thread would be a complete mess. 487* In line 11 we change our <code>modi_X</code> variable to true, that way our second thread it's going to modified the X value of our bitmap. 488* In line 12 we change back our ready variable to false again, because now we need to do the same with the second thread, we need to know when it's ready. 489* In line 13 we unlock everything again. 490 491And the next lines does everything again, but for the second thread. 492=== Two Threads Running=== 493At this point we have two threads running! 494 495Now what? 496 497Well those threas are changing the posi_X and posi_Y variables, so if you don't hurry up and draw the bitmap you're not going to see anything. 498 499But before drawing anything let's see how these threads are changing these variables: 500 501<source lang="c"> 502 503 while(!al_get_thread_should_stop(thr)){ 504 505 al_lock_mutex(data->mutex); 506 if(modi_X) 507 data->posiX += num; 508 else 509 data->posiY += num; 510 al_unlock_mutex(data->mutex); 511 512 al_rest(0.01); 513 514 } 515 516 517 return NULL; 518 519 </source> 520 521We're back to our <code>Func_Thread()</code> function. 522 523After initializing some values and telling to our parent thread that we're ready, the thread enter in a loop. A very simple loop that you should understand very well. To keep it simple we're not going to create another timer but just use <code>al_rest()</code> which allow us to rest the thread some seconds, otherwise this loop would run so fast that we wouldn't be able to see our bitmap on the screen. 524 525<source lang="c">while(!al_get_thread_should_stop(thr)) </source> 526 527The same way this thread talked with the parent thread to tell it was ready, this function <code></code> allows the parent thread to talk with this thread, but this time we use it to tell our thread that must stop. 528 529As you can see each time we modified the data object in all threads we need to lock it and unlock it using <code>al_luck_mutex()</code> and <code>al_luck_mutex()</code> respectively. 530 531If <code>al_get_thread_should_stop(thr)</code> returns true, the thread will stop. <code>thr</code> it's the other value that <code>Func_Thread</code> receives. It's nothing more than a pointer to the current thread. 532 533So we're done with the thread function. Now let's advance in our parent thread. 534 535 536 537 538<source lang="c" line start="0"> while(1) 539 { 540 ALLEGRO_EVENT ev; 541 al_wait_for_event(event_queue, &ev); 542 543 if(ev.type == ALLEGRO_EVENT_TIMER) { 544 redraw = true; 545 } 546 else if(ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) { 547 break; 548 } 549 else if(ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN) { 550 break; 551 } 552 if(redraw && al_is_event_queue_empty(event_queue)) { 553 redraw = false; 554 555 al_lock_mutex(data.mutex); 556 float X = data.posiX; 557 float Y = data.posiY; 558 al_unlock_mutex(data.mutex); 559 560 al_draw_bitmap(bouncer, X, Y, 0); 561 562 al_flip_display(); 563 } 564 } 565 al_destroy_thread(thread_1); 566 al_destroy_thread(thread_2); 567 568 al_destroy_bitmap(bouncer); 569 al_destroy_timer(timer); 570 al_destroy_display(display); 571 al_destroy_event_queue(event_queue); 572 573</source> 574 575This shouldn't be nothing new. As you can see in line 17, 18, 19 and 20, we are creating two variables and initializing them with the corresponding value, this is to clarify, that you should keep locked the mutex of determined object the less posible. 576 577<source lang="c"> al_destroy_thread(thread_1); 578 al_destroy_thread(thread_2) </source> 579 580And finally we're destroying the threads when we exit the program. This implicitly performs <code>al_join_thread</code> which at the same time implicitly calls <code>al_set_thread_should_stop</code> first, so the while that is running in our threads are going to stop. 581 582The End.

3 - The thread will be running the function Func_thread and getting the pointer *data as its argument when it starts. (Or only after it started it head hurts)

To be honest I don't understand the question.

4 - al_wait_cond takes a locked mutex as an argument???

On entering this function, mutex must be locked by the calling thread. The function will atomically release mutex and block on cond. That is what the manual says, I don't know if you was referring to another thing.

5 - In what order are you discussing the file? In what order was I discussing the file? I already discussed this???

When I said "Everything until here should be already clear" I was referring to the rest of the code the initialization of allegro, mouse etc, I guess I can remove it, though.

Ohhhh it's true... , know I think I understand it very well... al_wait_cond() unlock the mutex and block on the cond (the same that the manua says, I couldn't understand it the first time I read it ). I'm thinking in many threads using the same data, it's a good idea having just one cond and many flag to check if all the threads have done their job? something like:

Edit: I have another question... What is the point of having multiple threads if I can't use them to load bitmaps, fonts since they're loaded as memory bitmaps? I guess I have no problem by loading sound on another thread right?

Yhea it's done... Someone has some ideas to create another simple one? I thought in one which multiple threads load a lot of images and make the comparison between using or not threads... But I don't want to load all those images to the wiki, I was thinking if there would be another idea like that, which shows the difference between multi-thread and just one thread very clear.