test/testatomic.c
author Sam Lantinga <slouken@libsdl.org>
Thu, 27 Jan 2011 16:46:15 -0800
changeset 5115 427998ff3bcf
parent 5114 e337f792c6a7
child 5116 02b860cbc7ce
permissions -rw-r--r--
Added cache line size info in SDL_cpuinfo.h
I also added an implementation to dynamically query it, but didn't expose it since most x86 CPUs have an L1 cache line size of 64 bytes.
     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 /* The number of entries must be a power of 2 */
   247 #define MAX_ENTRIES 256
   248 #define WRAP_MASK   (MAX_ENTRIES-1)
   249 
   250 typedef struct
   251 {
   252     SDL_atomic_t sequence;
   253     SDL_Event event;
   254 } SDL_EventQueueEntry;
   255 
   256 typedef struct
   257 {
   258     SDL_EventQueueEntry entries[MAX_ENTRIES];
   259 
   260     char cache_pad1[SDL_CACHELINE_SIZE-((sizeof(SDL_EventQueueEntry)*MAX_ENTRIES)%SDL_CACHELINE_SIZE)];
   261 
   262     SDL_atomic_t enqueue_pos;
   263 
   264     char cache_pad2[SDL_CACHELINE_SIZE-sizeof(SDL_atomic_t)];
   265 
   266     SDL_atomic_t dequeue_pos;
   267 
   268     char cache_pad3[SDL_CACHELINE_SIZE-sizeof(SDL_atomic_t)];
   269 
   270 #ifdef TEST_SPINLOCK_FIFO
   271     SDL_SpinLock lock;
   272     SDL_atomic_t rwcount;
   273     SDL_atomic_t watcher;
   274 
   275     char cache_pad4[SDL_CACHELINE_SIZE-sizeof(SDL_SpinLock)-2*sizeof(SDL_atomic_t)];
   276 #endif
   277 
   278     volatile SDL_bool active;
   279 
   280     /* Only needed for the mutex test */
   281     SDL_mutex *mutex;
   282 
   283 } SDL_EventQueue;
   284 
   285 static void InitEventQueue(SDL_EventQueue *queue)
   286 {
   287     int i;
   288 
   289     for (i = 0; i < MAX_ENTRIES; ++i) {
   290         SDL_AtomicSet(&queue->entries[i].sequence, i);
   291     }
   292     SDL_AtomicSet(&queue->enqueue_pos, 0);
   293     SDL_AtomicSet(&queue->dequeue_pos, 0);
   294 #ifdef TEST_SPINLOCK_FIFO
   295     queue->lock = 0;
   296     SDL_AtomicSet(&queue->rwcount, 0);
   297 #endif
   298     queue->active = SDL_TRUE;
   299 }
   300 
   301 static SDL_bool EnqueueEvent_LockFree(SDL_EventQueue *queue, const SDL_Event *event)
   302 {
   303     SDL_EventQueueEntry *entry;
   304     unsigned queue_pos;
   305     unsigned entry_seq;
   306     int delta;
   307     SDL_bool status;
   308 
   309 #ifdef TEST_SPINLOCK_FIFO
   310     /* This is a gate so an external thread can lock the queue */
   311     SDL_AtomicLock(&queue->lock);
   312     SDL_assert(SDL_AtomicGet(&queue->watcher) == 0);
   313     SDL_AtomicIncRef(&queue->rwcount);
   314     SDL_AtomicUnlock(&queue->lock);
   315 #endif
   316 
   317     queue_pos = (unsigned)SDL_AtomicGet(&queue->enqueue_pos);
   318     for ( ; ; ) {
   319         entry = &queue->entries[queue_pos & WRAP_MASK];
   320         entry_seq = (unsigned)SDL_AtomicGet(&entry->sequence);
   321 
   322         delta = (int)(entry_seq - queue_pos);
   323         if (delta == 0) {
   324             /* The entry and the queue position match, try to increment the queue position */
   325             if (SDL_AtomicCAS(&queue->enqueue_pos, (int)queue_pos, (int)(queue_pos+1))) {
   326                 /* We own the object, fill it! */
   327                 entry->event = *event;
   328                 SDL_AtomicSet(&entry->sequence, (int)(queue_pos + 1));
   329                 status = SDL_TRUE;
   330                 break;
   331             }
   332         } else if (delta < 0) {
   333             /* We ran into an old queue entry, which means it still needs to be dequeued */
   334             status = SDL_FALSE;
   335             break;
   336         } else {
   337             /* We ran into a new queue entry, get the new queue position */
   338             queue_pos = (unsigned)SDL_AtomicGet(&queue->enqueue_pos);
   339         }
   340     }
   341 
   342 #ifdef TEST_SPINLOCK_FIFO
   343     SDL_AtomicDecRef(&queue->rwcount);
   344 #endif
   345     return status;
   346 }
   347 
   348 static SDL_bool DequeueEvent_LockFree(SDL_EventQueue *queue, SDL_Event *event)
   349 {
   350     SDL_EventQueueEntry *entry;
   351     unsigned queue_pos;
   352     unsigned entry_seq;
   353     int delta;
   354     SDL_bool status;
   355 
   356 #ifdef TEST_SPINLOCK_FIFO
   357     /* This is a gate so an external thread can lock the queue */
   358     SDL_AtomicLock(&queue->lock);
   359     SDL_assert(SDL_AtomicGet(&queue->watcher) == 0);
   360     SDL_AtomicIncRef(&queue->rwcount);
   361     SDL_AtomicUnlock(&queue->lock);
   362 #endif
   363 
   364     queue_pos = (unsigned)SDL_AtomicGet(&queue->dequeue_pos);
   365     for ( ; ; ) {
   366         entry = &queue->entries[queue_pos & WRAP_MASK];
   367         entry_seq = (unsigned)SDL_AtomicGet(&entry->sequence);
   368 
   369         delta = (int)(entry_seq - (queue_pos + 1));
   370         if (delta == 0) {
   371             /* The entry and the queue position match, try to increment the queue position */
   372             if (SDL_AtomicCAS(&queue->dequeue_pos, (int)queue_pos, (int)(queue_pos+1))) {
   373                 /* We own the object, fill it! */
   374                 *event = entry->event;
   375                 SDL_AtomicSet(&entry->sequence, (int)(queue_pos+MAX_ENTRIES));
   376                 status = SDL_TRUE;
   377                 break;
   378             }
   379         } else if (delta < 0) {
   380             /* We ran into an old queue entry, which means we've hit empty */
   381             status = SDL_FALSE;
   382             break;
   383         } else {
   384             /* We ran into a new queue entry, get the new queue position */
   385             queue_pos = (unsigned)SDL_AtomicGet(&queue->dequeue_pos);
   386         }
   387     }
   388 
   389 #ifdef TEST_SPINLOCK_FIFO
   390     SDL_AtomicDecRef(&queue->rwcount);
   391 #endif
   392     return status;
   393 }
   394 
   395 static SDL_bool EnqueueEvent_Mutex(SDL_EventQueue *queue, const SDL_Event *event)
   396 {
   397     SDL_EventQueueEntry *entry;
   398     unsigned queue_pos;
   399     unsigned entry_seq;
   400     int delta;
   401     SDL_bool status = SDL_FALSE;
   402 
   403     SDL_mutexP(queue->mutex);
   404 
   405     queue_pos = (unsigned)queue->enqueue_pos.value;
   406     entry = &queue->entries[queue_pos & WRAP_MASK];
   407     entry_seq = (unsigned)entry->sequence.value;
   408 
   409     delta = (int)(entry_seq - queue_pos);
   410     if (delta == 0) {
   411         ++queue->enqueue_pos.value;
   412 
   413         /* We own the object, fill it! */
   414         entry->event = *event;
   415         entry->sequence.value = (int)(queue_pos + 1);
   416         status = SDL_TRUE;
   417     } else if (delta < 0) {
   418         /* We ran into an old queue entry, which means it still needs to be dequeued */
   419     } else {
   420         printf("ERROR: mutex failed!\n");
   421     }
   422 
   423     SDL_mutexV(queue->mutex);
   424 
   425     return status;
   426 }
   427 
   428 static SDL_bool DequeueEvent_Mutex(SDL_EventQueue *queue, SDL_Event *event)
   429 {
   430     SDL_EventQueueEntry *entry;
   431     unsigned queue_pos;
   432     unsigned entry_seq;
   433     int delta;
   434     SDL_bool status = SDL_FALSE;
   435 
   436     SDL_mutexP(queue->mutex);
   437 
   438     queue_pos = (unsigned)queue->dequeue_pos.value;
   439     entry = &queue->entries[queue_pos & WRAP_MASK];
   440     entry_seq = (unsigned)entry->sequence.value;
   441 
   442     delta = (int)(entry_seq - (queue_pos + 1));
   443     if (delta == 0) {
   444         ++queue->dequeue_pos.value;
   445 
   446         /* We own the object, fill it! */
   447         *event = entry->event;
   448         entry->sequence.value = (int)(queue_pos + MAX_ENTRIES);
   449         status = SDL_TRUE;
   450     } else if (delta < 0) {
   451         /* We ran into an old queue entry, which means we've hit empty */
   452     } else {
   453         printf("ERROR: mutex failed!\n");
   454     }
   455 
   456     SDL_mutexV(queue->mutex);
   457 
   458     return status;
   459 }
   460 
   461 static SDL_sem *writersDone;
   462 static SDL_sem *readersDone;
   463 static SDL_atomic_t writersRunning;
   464 static SDL_atomic_t readersRunning;
   465 
   466 typedef struct
   467 {
   468     SDL_EventQueue *queue;
   469     int index;
   470     char padding1[SDL_CACHELINE_SIZE-(sizeof(SDL_EventQueue*)+sizeof(int))%SDL_CACHELINE_SIZE];
   471     int waits;
   472     SDL_bool lock_free;
   473     char padding2[SDL_CACHELINE_SIZE-sizeof(int)-sizeof(SDL_bool)];
   474 } WriterData;
   475 
   476 typedef struct
   477 {
   478     SDL_EventQueue *queue;
   479     int counters[NUM_WRITERS];
   480     int waits;
   481     SDL_bool lock_free;
   482     char padding[SDL_CACHELINE_SIZE-(sizeof(SDL_EventQueue*)+sizeof(int)*NUM_WRITERS+sizeof(int)+sizeof(SDL_bool))%SDL_CACHELINE_SIZE];
   483 } ReaderData;
   484 
   485 static int FIFO_Writer(void* _data)
   486 {
   487     WriterData *data = (WriterData *)_data;
   488     SDL_EventQueue *queue = data->queue;
   489     int index = data->index;
   490     int i;
   491     SDL_Event event;
   492 
   493     event.type = SDL_USEREVENT;
   494     event.user.windowID = 0;
   495     event.user.code = 0;
   496     event.user.data1 = data;
   497     event.user.data2 = NULL;
   498 
   499     if (data->lock_free) {
   500         for (i = 0; i < EVENTS_PER_WRITER; ++i) {
   501             event.user.code = i;
   502             while (!EnqueueEvent_LockFree(queue, &event)) {
   503                 ++data->waits;
   504                 SDL_Delay(0);
   505             }
   506         }
   507     } else {
   508         for (i = 0; i < EVENTS_PER_WRITER; ++i) {
   509             event.user.code = i;
   510             while (!EnqueueEvent_Mutex(queue, &event)) {
   511                 ++data->waits;
   512                 SDL_Delay(0);
   513             }
   514         }
   515     }
   516     SDL_AtomicAdd(&writersRunning, -1);
   517     SDL_SemPost(writersDone);
   518     return 0;
   519 }
   520 
   521 static int FIFO_Reader(void* _data)
   522 {
   523     ReaderData *data = (ReaderData *)_data;
   524     SDL_EventQueue *queue = data->queue;
   525     SDL_Event event;
   526 
   527     if (data->lock_free) {
   528         for ( ; ; ) {
   529             if (DequeueEvent_LockFree(queue, &event)) {
   530                 WriterData *writer = (WriterData*)event.user.data1;
   531                 ++data->counters[writer->index];
   532             } else if (queue->active) {
   533                 ++data->waits;
   534                 SDL_Delay(0);
   535             } else {
   536                 /* We drained the queue, we're done! */
   537                 break;
   538             }
   539         }
   540     } else {
   541         for ( ; ; ) {
   542             if (DequeueEvent_Mutex(queue, &event)) {
   543                 WriterData *writer = (WriterData*)event.user.data1;
   544                 ++data->counters[writer->index];
   545             } else if (queue->active) {
   546                 ++data->waits;
   547                 SDL_Delay(0);
   548             } else {
   549                 /* We drained the queue, we're done! */
   550                 break;
   551             }
   552         }
   553     }
   554     SDL_AtomicAdd(&readersRunning, -1);
   555     SDL_SemPost(readersDone);
   556     return 0;
   557 }
   558 
   559 #ifdef TEST_SPINLOCK_FIFO
   560 /* This thread periodically locks the queue for no particular reason */
   561 static int FIFO_Watcher(void* _data)
   562 {
   563     SDL_EventQueue *queue = (SDL_EventQueue *)_data;
   564 
   565     while (queue->active) {
   566         SDL_AtomicLock(&queue->lock);
   567         SDL_AtomicIncRef(&queue->watcher);
   568         while (SDL_AtomicGet(&queue->rwcount) > 0) {
   569             SDL_Delay(0);
   570         }
   571         /* Do queue manipulation here... */
   572         SDL_AtomicDecRef(&queue->watcher);
   573         SDL_AtomicUnlock(&queue->lock);
   574 
   575         /* Wait a bit... */
   576         SDL_Delay(1);
   577     }
   578     return 0;
   579 }
   580 #endif /* TEST_SPINLOCK_FIFO */
   581 
   582 static void RunFIFOTest(SDL_bool lock_free)
   583 {
   584     SDL_EventQueue queue;
   585     WriterData writerData[NUM_WRITERS];
   586     ReaderData readerData[NUM_READERS];
   587     Uint32 start, end;
   588     int i, j;
   589     int grand_total;
   590  
   591     printf("\nFIFO test---------------------------------------\n\n");
   592     printf("Mode: %s\n", lock_free ? "LockFree" : "Mutex");
   593 
   594     readersDone = SDL_CreateSemaphore(0);
   595     writersDone = SDL_CreateSemaphore(0);
   596 
   597     SDL_memset(&queue, 0xff, sizeof(queue));
   598 
   599     InitEventQueue(&queue);
   600     if (!lock_free) {
   601         queue.mutex = SDL_CreateMutex();
   602     }
   603 
   604     start = SDL_GetTicks();
   605  
   606 #ifdef TEST_SPINLOCK_FIFO
   607     /* Start a monitoring thread */
   608     if (lock_free) {
   609         SDL_CreateThread(FIFO_Watcher, &queue);
   610     }
   611 #endif
   612 
   613     /* Start the readers first */
   614     printf("Starting %d readers\n", NUM_READERS);
   615     SDL_zero(readerData);
   616     SDL_AtomicSet(&readersRunning, NUM_READERS);
   617     for (i = 0; i < NUM_READERS; ++i) {
   618         readerData[i].queue = &queue;
   619         readerData[i].lock_free = lock_free;
   620         SDL_CreateThread(FIFO_Reader, &readerData[i]);
   621     }
   622 
   623     /* Start up the writers */
   624     printf("Starting %d writers\n", NUM_WRITERS);
   625     SDL_zero(writerData);
   626     SDL_AtomicSet(&writersRunning, NUM_WRITERS);
   627     for (i = 0; i < NUM_WRITERS; ++i) {
   628         writerData[i].queue = &queue;
   629         writerData[i].index = i;
   630         writerData[i].lock_free = lock_free;
   631         SDL_CreateThread(FIFO_Writer, &writerData[i]);
   632     }
   633  
   634     /* Wait for the writers */
   635     while (SDL_AtomicGet(&writersRunning) > 0) {
   636         SDL_SemWait(writersDone);
   637     }
   638  
   639     /* Shut down the queue so readers exit */
   640     queue.active = SDL_FALSE;
   641 
   642     /* Wait for the readers */
   643     while (SDL_AtomicGet(&readersRunning) > 0) {
   644         SDL_SemWait(readersDone);
   645     }
   646 
   647     end = SDL_GetTicks();
   648  
   649     SDL_DestroySemaphore(readersDone);
   650     SDL_DestroySemaphore(writersDone);
   651 
   652     if (!lock_free) {
   653         SDL_DestroyMutex(queue.mutex);
   654     }
   655  
   656     printf("Finished in %f sec\n", (end - start) / 1000.f);
   657 
   658     printf("\n");
   659     for (i = 0; i < NUM_WRITERS; ++i) {
   660         printf("Writer %d wrote %d events, had %d waits\n", i, EVENTS_PER_WRITER, writerData[i].waits);
   661     }
   662     printf("Writers wrote %d total events\n", NUM_WRITERS*EVENTS_PER_WRITER);
   663 
   664     /* Print a breakdown of which readers read messages from which writer */
   665     printf("\n");
   666     grand_total = 0;
   667     for (i = 0; i < NUM_READERS; ++i) {
   668         int total = 0;
   669         for (j = 0; j < NUM_WRITERS; ++j) {
   670             total += readerData[i].counters[j];
   671         }
   672         grand_total += total;
   673         printf("Reader %d read %d events, had %d waits\n", i, total, readerData[i].waits);
   674         printf("  { ");
   675         for (j = 0; j < NUM_WRITERS; ++j) {
   676             if (j > 0) {
   677                 printf(", ");
   678             }
   679             printf("%d", readerData[i].counters[j]);
   680         }
   681         printf(" }\n");
   682     }
   683     printf("Readers read %d total events\n", grand_total);
   684 }
   685 
   686 /* End FIFO test */
   687 /**************************************************************************/
   688 
   689 int
   690 main(int argc, char *argv[])
   691 {
   692     RunBasicTest();
   693     RunEpicTest();
   694 /* This test is really slow, so don't run it by default */
   695 #if 0
   696     RunFIFOTest(SDL_FALSE);
   697 #endif
   698     RunFIFOTest(SDL_TRUE);
   699     return 0;
   700 }
   701 
   702 /* vi: set ts=4 sw=4 expandtab: */