1 /* 2 Copyright (C) 1997-2014 Sam Lantinga <slouken (at) libsdl.org> 3 4 This software is provided 'as-is', without any express or implied 5 warranty. In no event will the authors be held liable for any damages 6 arising from the use of this software. 7 8 Permission is granted to anyone to use this software for any purpose, 9 including commercial applications, and to alter it and redistribute it 10 freely. 11 */ 12 #include <stdio.h> 13 14 #include "SDL.h" 15 #include "SDL_atomic.h" 16 #include "SDL_assert.h" 17 #include "SDL_cpuinfo.h" 18 19 /* 20 Absolutely basic tests just to see if we get the expected value 21 after calling each function. 22 */ 23 24 static 25 char * 26 tf(SDL_bool tf) 27 { 28 static char *t = "TRUE"; 29 static char *f = "FALSE"; 30 31 if (tf) 32 { 33 return t; 34 } 35 36 return f; 37 } 38 39 static 40 void RunBasicTest() 41 { 42 int value; 43 SDL_SpinLock lock = 0; 44 45 SDL_atomic_t v; 46 SDL_bool tfret = SDL_FALSE; 47 48 SDL_Log("\nspin lock---------------------------------------\n\n"); 49 50 SDL_AtomicLock(&lock); 51 SDL_Log("AtomicLock lock=%d\n", lock); 52 SDL_AtomicUnlock(&lock); 53 SDL_Log("AtomicUnlock lock=%d\n", lock); 54 55 SDL_Log("\natomic -----------------------------------------\n\n"); 56 57 SDL_AtomicSet(&v, 0); 58 tfret = SDL_AtomicSet(&v, 10) == 0 ? SDL_TRUE : SDL_FALSE; 59 SDL_Log("AtomicSet(10) tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v)); 60 tfret = SDL_AtomicAdd(&v, 10) == 10 ? SDL_TRUE : SDL_FALSE; 61 SDL_Log("AtomicAdd(10) tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v)); 62 63 SDL_AtomicSet(&v, 0); 64 SDL_AtomicIncRef(&v); 65 tfret = (SDL_AtomicGet(&v) == 1) ? SDL_TRUE : SDL_FALSE; 66 SDL_Log("AtomicIncRef() tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v)); 67 SDL_AtomicIncRef(&v); 68 tfret = (SDL_AtomicGet(&v) == 2) ? SDL_TRUE : SDL_FALSE; 69 SDL_Log("AtomicIncRef() tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v)); 70 tfret = (SDL_AtomicDecRef(&v) == SDL_FALSE) ? SDL_TRUE : SDL_FALSE; 71 SDL_Log("AtomicDecRef() tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v)); 72 tfret = (SDL_AtomicDecRef(&v) == SDL_TRUE) ? SDL_TRUE : SDL_FALSE; 73 SDL_Log("AtomicDecRef() tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v)); 74 75 SDL_AtomicSet(&v, 10); 76 tfret = (SDL_AtomicCAS(&v, 0, 20) == SDL_FALSE) ? SDL_TRUE : SDL_FALSE; 77 SDL_Log("AtomicCAS() tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v)); 78 value = SDL_AtomicGet(&v); 79 tfret = (SDL_AtomicCAS(&v, value, 20) == SDL_TRUE) ? SDL_TRUE : SDL_FALSE; 80 SDL_Log("AtomicCAS() tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v)); 81 } 82 83 /**************************************************************************/ 84 /* Atomic operation test 85 * Adapted with permission from code by Michael Davidsaver at: 86 * http://bazaar.launchpad.net/~mdavidsaver/epics-base/atomic/revision/12105#src/libCom/test/epicsAtomicTest.c 87 * Original copyright 2010 Brookhaven Science Associates as operator of Brookhaven National Lab 88 * http://www.aps.anl.gov/epics/license/open.php 89 */ 90 91 /* Tests semantics of atomic operations. Also a stress test 92 * to see if they are really atomic. 93 * 94 * Several threads adding to the same variable. 95 * at the end the value is compared with the expected 96 * and with a non-atomic counter. 97 */ 98 99 /* Number of concurrent incrementers */ 100 #define NThreads 2 101 #define CountInc 100 102 #define VALBITS (sizeof(atomicValue)*8) 103 104 #define atomicValue int 105 #define CountTo ((atomicValue)((unsigned int)(1<<(VALBITS-1))-1)) 106 #define NInter (CountTo/CountInc/NThreads) 107 #define Expect (CountTo-NInter*CountInc*NThreads) 108 109 SDL_COMPILE_TIME_ASSERT(size, CountTo>0); /* check for rollover */ 110 111 static SDL_atomic_t good = { 42 }; 112 113 static atomicValue bad = 42; 114 115 static SDL_atomic_t threadsRunning; 116 117 static SDL_sem *threadDone; 118 119 static 120 int adder(void* junk) 121 { 122 unsigned long N=NInter; 123 SDL_Log("Thread subtracting %d %lu times\n",CountInc,N); 124 while (N--) { 125 SDL_AtomicAdd(&good, -CountInc); 126 bad-=CountInc; 127 } 128 SDL_AtomicAdd(&threadsRunning, -1); 129 SDL_SemPost(threadDone); 130 return 0; 131 } 132 133 static 134 void runAdder(void) 135 { 136 Uint32 start, end; 137 int T=NThreads; 138 139 start = SDL_GetTicks(); 140 141 threadDone = SDL_CreateSemaphore(0); 142 143 SDL_AtomicSet(&threadsRunning, NThreads); 144 145 while (T--) 146 SDL_CreateThread(adder, "Adder", NULL); 147 148 while (SDL_AtomicGet(&threadsRunning) > 0) 149 SDL_SemWait(threadDone); 150 151 SDL_DestroySemaphore(threadDone); 152 153 end = SDL_GetTicks(); 154 155 SDL_Log("Finished in %f sec\n", (end - start) / 1000.f); 156 } 157 158 static 159 void RunEpicTest() 160 { 161 int b; 162 atomicValue v; 163 164 SDL_Log("\nepic test---------------------------------------\n\n"); 165 166 SDL_Log("Size asserted to be >= 32-bit\n"); 167 SDL_assert(sizeof(atomicValue)>=4); 168 169 SDL_Log("Check static initializer\n"); 170 v=SDL_AtomicGet(&good); 171 SDL_assert(v==42); 172 173 SDL_assert(bad==42); 174 175 SDL_Log("Test negative values\n"); 176 SDL_AtomicSet(&good, -5); 177 v=SDL_AtomicGet(&good); 178 SDL_assert(v==-5); 179 180 SDL_Log("Verify maximum value\n"); 181 SDL_AtomicSet(&good, CountTo); 182 v=SDL_AtomicGet(&good); 183 SDL_assert(v==CountTo); 184 185 SDL_Log("Test compare and exchange\n"); 186 187 b=SDL_AtomicCAS(&good, 500, 43); 188 SDL_assert(!b); /* no swap since CountTo!=500 */ 189 v=SDL_AtomicGet(&good); 190 SDL_assert(v==CountTo); /* ensure no swap */ 191 192 b=SDL_AtomicCAS(&good, CountTo, 44); 193 SDL_assert(!!b); /* will swap */ 194 v=SDL_AtomicGet(&good); 195 SDL_assert(v==44); 196 197 SDL_Log("Test Add\n"); 198 199 v=SDL_AtomicAdd(&good, 1); 200 SDL_assert(v==44); 201 v=SDL_AtomicGet(&good); 202 SDL_assert(v==45); 203 204 v=SDL_AtomicAdd(&good, 10); 205 SDL_assert(v==45); 206 v=SDL_AtomicGet(&good); 207 SDL_assert(v==55); 208 209 SDL_Log("Test Add (Negative values)\n"); 210 211 v=SDL_AtomicAdd(&good, -20); 212 SDL_assert(v==55); 213 v=SDL_AtomicGet(&good); 214 SDL_assert(v==35); 215 216 v=SDL_AtomicAdd(&good, -50); /* crossing zero down */ 217 SDL_assert(v==35); 218 v=SDL_AtomicGet(&good); 219 SDL_assert(v==-15); 220 221 v=SDL_AtomicAdd(&good, 30); /* crossing zero up */ 222 SDL_assert(v==-15); 223 v=SDL_AtomicGet(&good); 224 SDL_assert(v==15); 225 226 SDL_Log("Reset before count down test\n"); 227 SDL_AtomicSet(&good, CountTo); 228 v=SDL_AtomicGet(&good); 229 SDL_assert(v==CountTo); 230 231 bad=CountTo; 232 SDL_assert(bad==CountTo); 233 234 SDL_Log("Counting down from %d, Expect %d remaining\n",CountTo,Expect); 235 runAdder(); 236 237 v=SDL_AtomicGet(&good); 238 SDL_Log("Atomic %d Non-Atomic %d\n",v,bad); 239 SDL_assert(v==Expect); 240 SDL_assert(bad!=Expect); 241 } 242 243 /* End atomic operation test */ 244 /**************************************************************************/ 245 246 /**************************************************************************/ 247 /* Lock-free FIFO test */ 248 249 /* This is useful to test the impact of another thread locking the queue 250 entirely for heavy-weight manipulation. 251 */ 252 #define TEST_SPINLOCK_FIFO 253 254 #define NUM_READERS 4 255 #define NUM_WRITERS 4 256 #define EVENTS_PER_WRITER 1000000 257 258 /* The number of entries must be a power of 2 */ 259 #define MAX_ENTRIES 256 260 #define WRAP_MASK (MAX_ENTRIES-1) 261 262 typedef struct 263 { 264 SDL_atomic_t sequence; 265 SDL_Event event; 266 } SDL_EventQueueEntry; 267 268 typedef struct 269 { 270 SDL_EventQueueEntry entries[MAX_ENTRIES]; 271 272 char cache_pad1[SDL_CACHELINE_SIZE-((sizeof(SDL_EventQueueEntry)*MAX_ENTRIES)%SDL_CACHELINE_SIZE)]; 273 274 SDL_atomic_t enqueue_pos; 275 276 char cache_pad2[SDL_CACHELINE_SIZE-sizeof(SDL_atomic_t)]; 277 278 SDL_atomic_t dequeue_pos; 279 280 char cache_pad3[SDL_CACHELINE_SIZE-sizeof(SDL_atomic_t)]; 281 282 #ifdef TEST_SPINLOCK_FIFO 283 SDL_SpinLock lock; 284 SDL_atomic_t rwcount; 285 SDL_atomic_t watcher; 286 287 char cache_pad4[SDL_CACHELINE_SIZE-sizeof(SDL_SpinLock)-2*sizeof(SDL_atomic_t)]; 288 #endif 289 290 volatile SDL_bool active; 291 292 /* Only needed for the mutex test */ 293 SDL_mutex *mutex; 294 295 } SDL_EventQueue; 296 297 static void InitEventQueue(SDL_EventQueue *queue) 298 { 299 int i; 300 301 for (i = 0; i < MAX_ENTRIES; ++i) { 302 SDL_AtomicSet(&queue->entries[i].sequence, i); 303 } 304 SDL_AtomicSet(&queue->enqueue_pos, 0); 305 SDL_AtomicSet(&queue->dequeue_pos, 0); 306 #ifdef TEST_SPINLOCK_FIFO 307 queue->lock = 0; 308 SDL_AtomicSet(&queue->rwcount, 0); 309 #endif 310 queue->active = SDL_TRUE; 311 } 312 313 static SDL_bool EnqueueEvent_LockFree(SDL_EventQueue *queue, const SDL_Event *event) 314 { 315 SDL_EventQueueEntry *entry; 316 unsigned queue_pos; 317 unsigned entry_seq; 318 int delta; 319 SDL_bool status; 320 321 #ifdef TEST_SPINLOCK_FIFO 322 /* This is a gate so an external thread can lock the queue */ 323 SDL_AtomicLock(&queue->lock); 324 SDL_assert(SDL_AtomicGet(&queue->watcher) == 0); 325 SDL_AtomicIncRef(&queue->rwcount); 326 SDL_AtomicUnlock(&queue->lock); 327 #endif 328 329 queue_pos = (unsigned)SDL_AtomicGet(&queue->enqueue_pos); 330 for ( ; ; ) { 331 entry = &queue->entries[queue_pos & WRAP_MASK]; 332 entry_seq = (unsigned)SDL_AtomicGet(&entry->sequence); 333 334 delta = (int)(entry_seq - queue_pos); 335 if (delta == 0) { 336 /* The entry and the queue position match, try to increment the queue position */ 337 if (SDL_AtomicCAS(&queue->enqueue_pos, (int)queue_pos, (int)(queue_pos+1))) { 338 /* We own the object, fill it! */ 339 entry->event = *event; 340 SDL_AtomicSet(&entry->sequence, (int)(queue_pos + 1)); 341 status = SDL_TRUE; 342 break; 343 } 344 } else if (delta < 0) { 345 /* We ran into an old queue entry, which means it still needs to be dequeued */ 346 status = SDL_FALSE; 347 break; 348 } else { 349 /* We ran into a new queue entry, get the new queue position */ 350 queue_pos = (unsigned)SDL_AtomicGet(&queue->enqueue_pos); 351 } 352 } 353 354 #ifdef TEST_SPINLOCK_FIFO 355 SDL_AtomicDecRef(&queue->rwcount); 356 #endif 357 return status; 358 } 359 360 static SDL_bool DequeueEvent_LockFree(SDL_EventQueue *queue, SDL_Event *event) 361 { 362 SDL_EventQueueEntry *entry; 363 unsigned queue_pos; 364 unsigned entry_seq; 365 int delta; 366 SDL_bool status; 367 368 #ifdef TEST_SPINLOCK_FIFO 369 /* This is a gate so an external thread can lock the queue */ 370 SDL_AtomicLock(&queue->lock); 371 SDL_assert(SDL_AtomicGet(&queue->watcher) == 0); 372 SDL_AtomicIncRef(&queue->rwcount); 373 SDL_AtomicUnlock(&queue->lock); 374 #endif 375 376 queue_pos = (unsigned)SDL_AtomicGet(&queue->dequeue_pos); 377 for ( ; ; ) { 378 entry = &queue->entries[queue_pos & WRAP_MASK]; 379 entry_seq = (unsigned)SDL_AtomicGet(&entry->sequence); 380 381 delta = (int)(entry_seq - (queue_pos + 1)); 382 if (delta == 0) { 383 /* The entry and the queue position match, try to increment the queue position */ 384 if (SDL_AtomicCAS(&queue->dequeue_pos, (int)queue_pos, (int)(queue_pos+1))) { 385 /* We own the object, fill it! */ 386 *event = entry->event; 387 SDL_AtomicSet(&entry->sequence, (int)(queue_pos+MAX_ENTRIES)); 388 status = SDL_TRUE; 389 break; 390 } 391 } else if (delta < 0) { 392 /* We ran into an old queue entry, which means we've hit empty */ 393 status = SDL_FALSE; 394 break; 395 } else { 396 /* We ran into a new queue entry, get the new queue position */ 397 queue_pos = (unsigned)SDL_AtomicGet(&queue->dequeue_pos); 398 } 399 } 400 401 #ifdef TEST_SPINLOCK_FIFO 402 SDL_AtomicDecRef(&queue->rwcount); 403 #endif 404 return status; 405 } 406 407 static SDL_bool EnqueueEvent_Mutex(SDL_EventQueue *queue, const SDL_Event *event) 408 { 409 SDL_EventQueueEntry *entry; 410 unsigned queue_pos; 411 unsigned entry_seq; 412 int delta; 413 SDL_bool status = SDL_FALSE; 414 415 SDL_LockMutex(queue->mutex); 416 417 queue_pos = (unsigned)queue->enqueue_pos.value; 418 entry = &queue->entries[queue_pos & WRAP_MASK]; 419 entry_seq = (unsigned)entry->sequence.value; 420 421 delta = (int)(entry_seq - queue_pos); 422 if (delta == 0) { 423 ++queue->enqueue_pos.value; 424 425 /* We own the object, fill it! */ 426 entry->event = *event; 427 entry->sequence.value = (int)(queue_pos + 1); 428 status = SDL_TRUE; 429 } else if (delta < 0) { 430 /* We ran into an old queue entry, which means it still needs to be dequeued */ 431 } else { 432 SDL_Log("ERROR: mutex failed!\n"); 433 } 434 435 SDL_UnlockMutex(queue->mutex); 436 437 return status; 438 } 439 440 static SDL_bool DequeueEvent_Mutex(SDL_EventQueue *queue, SDL_Event *event) 441 { 442 SDL_EventQueueEntry *entry; 443 unsigned queue_pos; 444 unsigned entry_seq; 445 int delta; 446 SDL_bool status = SDL_FALSE; 447 448 SDL_LockMutex(queue->mutex); 449 450 queue_pos = (unsigned)queue->dequeue_pos.value; 451 entry = &queue->entries[queue_pos & WRAP_MASK]; 452 entry_seq = (unsigned)entry->sequence.value; 453 454 delta = (int)(entry_seq - (queue_pos + 1)); 455 if (delta == 0) { 456 ++queue->dequeue_pos.value; 457 458 /* We own the object, fill it! */ 459 *event = entry->event; 460 entry->sequence.value = (int)(queue_pos + MAX_ENTRIES); 461 status = SDL_TRUE; 462 } else if (delta < 0) { 463 /* We ran into an old queue entry, which means we've hit empty */ 464 } else { 465 SDL_Log("ERROR: mutex failed!\n"); 466 } 467 468 SDL_UnlockMutex(queue->mutex); 469 470 return status; 471 } 472 473 static SDL_sem *writersDone; 474 static SDL_sem *readersDone; 475 static SDL_atomic_t writersRunning; 476 static SDL_atomic_t readersRunning; 477 478 typedef struct 479 { 480 SDL_EventQueue *queue; 481 int index; 482 char padding1[SDL_CACHELINE_SIZE-(sizeof(SDL_EventQueue*)+sizeof(int))%SDL_CACHELINE_SIZE]; 483 int waits; 484 SDL_bool lock_free; 485 char padding2[SDL_CACHELINE_SIZE-sizeof(int)-sizeof(SDL_bool)]; 486 } WriterData; 487 488 typedef struct 489 { 490 SDL_EventQueue *queue; 491 int counters[NUM_WRITERS]; 492 int waits; 493 SDL_bool lock_free; 494 char padding[SDL_CACHELINE_SIZE-(sizeof(SDL_EventQueue*)+sizeof(int)*NUM_WRITERS+sizeof(int)+sizeof(SDL_bool))%SDL_CACHELINE_SIZE]; 495 } ReaderData; 496 497 static int FIFO_Writer(void* _data) 498 { 499 WriterData *data = (WriterData *)_data; 500 SDL_EventQueue *queue = data->queue; 501 int i; 502 SDL_Event event; 503 504 event.type = SDL_USEREVENT; 505 event.user.windowID = 0; 506 event.user.code = 0; 507 event.user.data1 = data; 508 event.user.data2 = NULL; 509 510 if (data->lock_free) { 511 for (i = 0; i < EVENTS_PER_WRITER; ++i) { 512 event.user.code = i; 513 while (!EnqueueEvent_LockFree(queue, &event)) { 514 ++data->waits; 515 SDL_Delay(0); 516 } 517 } 518 } else { 519 for (i = 0; i < EVENTS_PER_WRITER; ++i) { 520 event.user.code = i; 521 while (!EnqueueEvent_Mutex(queue, &event)) { 522 ++data->waits; 523 SDL_Delay(0); 524 } 525 } 526 } 527 SDL_AtomicAdd(&writersRunning, -1); 528 SDL_SemPost(writersDone); 529 return 0; 530 } 531 532 static int FIFO_Reader(void* _data) 533 { 534 ReaderData *data = (ReaderData *)_data; 535 SDL_EventQueue *queue = data->queue; 536 SDL_Event event; 537 538 if (data->lock_free) { 539 for ( ; ; ) { 540 if (DequeueEvent_LockFree(queue, &event)) { 541 WriterData *writer = (WriterData*)event.user.data1; 542 ++data->counters[writer->index]; 543 } else if (queue->active) { 544 ++data->waits; 545 SDL_Delay(0); 546 } else { 547 /* We drained the queue, we're done! */ 548 break; 549 } 550 } 551 } else { 552 for ( ; ; ) { 553 if (DequeueEvent_Mutex(queue, &event)) { 554 WriterData *writer = (WriterData*)event.user.data1; 555 ++data->counters[writer->index]; 556 } else if (queue->active) { 557 ++data->waits; 558 SDL_Delay(0); 559 } else { 560 /* We drained the queue, we're done! */ 561 break; 562 } 563 } 564 } 565 SDL_AtomicAdd(&readersRunning, -1); 566 SDL_SemPost(readersDone); 567 return 0; 568 } 569 570 #ifdef TEST_SPINLOCK_FIFO 571 /* This thread periodically locks the queue for no particular reason */ 572 static int FIFO_Watcher(void* _data) 573 { 574 SDL_EventQueue *queue = (SDL_EventQueue *)_data; 575 576 while (queue->active) { 577 SDL_AtomicLock(&queue->lock); 578 SDL_AtomicIncRef(&queue->watcher); 579 while (SDL_AtomicGet(&queue->rwcount) > 0) { 580 SDL_Delay(0); 581 } 582 /* Do queue manipulation here... */ 583 SDL_AtomicDecRef(&queue->watcher); 584 SDL_AtomicUnlock(&queue->lock); 585 586 /* Wait a bit... */ 587 SDL_Delay(1); 588 } 589 return 0; 590 } 591 #endif /* TEST_SPINLOCK_FIFO */ 592 593 static void RunFIFOTest(SDL_bool lock_free) 594 { 595 SDL_EventQueue queue; 596 WriterData writerData[NUM_WRITERS]; 597 ReaderData readerData[NUM_READERS]; 598 Uint32 start, end; 599 int i, j; 600 int grand_total; 601 char textBuffer[1024]; 602 int len; 603 604 SDL_Log("\nFIFO test---------------------------------------\n\n"); 605 SDL_Log("Mode: %s\n", lock_free ? "LockFree" : "Mutex"); 606 607 readersDone = SDL_CreateSemaphore(0); 608 writersDone = SDL_CreateSemaphore(0); 609 610 SDL_memset(&queue, 0xff, sizeof(queue)); 611 612 InitEventQueue(&queue); 613 if (!lock_free) { 614 queue.mutex = SDL_CreateMutex(); 615 } 616 617 start = SDL_GetTicks(); 618 619 #ifdef TEST_SPINLOCK_FIFO 620 /* Start a monitoring thread */ 621 if (lock_free) { 622 SDL_CreateThread(FIFO_Watcher, "FIFOWatcher", &queue); 623 } 624 #endif 625 626 /* Start the readers first */ 627 SDL_Log("Starting %d readers\n", NUM_READERS); 628 SDL_zero(readerData); 629 SDL_AtomicSet(&readersRunning, NUM_READERS); 630 for (i = 0; i < NUM_READERS; ++i) { 631 char name[64]; 632 SDL_snprintf(name, sizeof (name), "FIFOReader%d", i); 633 readerData[i].queue = &queue; 634 readerData[i].lock_free = lock_free; 635 SDL_CreateThread(FIFO_Reader, name, &readerData[i]); 636 } 637 638 /* Start up the writers */ 639 SDL_Log("Starting %d writers\n", NUM_WRITERS); 640 SDL_zero(writerData); 641 SDL_AtomicSet(&writersRunning, NUM_WRITERS); 642 for (i = 0; i < NUM_WRITERS; ++i) { 643 char name[64]; 644 SDL_snprintf(name, sizeof (name), "FIFOWriter%d", i); 645 writerData[i].queue = &queue; 646 writerData[i].index = i; 647 writerData[i].lock_free = lock_free; 648 SDL_CreateThread(FIFO_Writer, name, &writerData[i]); 649 } 650 651 /* Wait for the writers */ 652 while (SDL_AtomicGet(&writersRunning) > 0) { 653 SDL_SemWait(writersDone); 654 } 655 656 /* Shut down the queue so readers exit */ 657 queue.active = SDL_FALSE; 658 659 /* Wait for the readers */ 660 while (SDL_AtomicGet(&readersRunning) > 0) { 661 SDL_SemWait(readersDone); 662 } 663 664 end = SDL_GetTicks(); 665 666 SDL_DestroySemaphore(readersDone); 667 SDL_DestroySemaphore(writersDone); 668 669 if (!lock_free) { 670 SDL_DestroyMutex(queue.mutex); 671 } 672 673 SDL_Log("Finished in %f sec\n", (end - start) / 1000.f); 674 675 SDL_Log("\n"); 676 for (i = 0; i < NUM_WRITERS; ++i) { 677 SDL_Log("Writer %d wrote %d events, had %d waits\n", i, EVENTS_PER_WRITER, writerData[i].waits); 678 } 679 SDL_Log("Writers wrote %d total events\n", NUM_WRITERS*EVENTS_PER_WRITER); 680 681 /* Print a breakdown of which readers read messages from which writer */ 682 SDL_Log("\n"); 683 grand_total = 0; 684 for (i = 0; i < NUM_READERS; ++i) { 685 int total = 0; 686 for (j = 0; j < NUM_WRITERS; ++j) { 687 total += readerData[i].counters[j]; 688 } 689 grand_total += total; 690 SDL_Log("Reader %d read %d events, had %d waits\n", i, total, readerData[i].waits); 691 SDL_snprintf(textBuffer, sizeof(textBuffer), " { "); 692 for (j = 0; j < NUM_WRITERS; ++j) { 693 if (j > 0) { 694 len = SDL_strlen(textBuffer); 695 SDL_snprintf(textBuffer + len, sizeof(textBuffer) - len, ", "); 696 } 697 len = SDL_strlen(textBuffer); 698 SDL_snprintf(textBuffer + len, sizeof(textBuffer) - len, "%d", readerData[i].counters[j]); 699 } 700 len = SDL_strlen(textBuffer); 701 SDL_snprintf(textBuffer + len, sizeof(textBuffer) - len, " }\n"); 702 SDL_Log(textBuffer); 703 } 704 SDL_Log("Readers read %d total events\n", grand_total); 705 } 706 707 /* End FIFO test */ 708 /**************************************************************************/ 709 710 int 711 main(int argc, char *argv[]) 712 { 713 /* Enable standard application logging */ 714 SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); 715 716 RunBasicTest(); 717 RunEpicTest(); 718 /* This test is really slow, so don't run it by default */ 719 #if 0 720 RunFIFOTest(SDL_FALSE); 721 #endif 722 RunFIFOTest(SDL_TRUE); 723 return 0; 724 } 725 726 /* vi: set ts=4 sw=4 expandtab: */ 727