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