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