test/testatomic.c
author Andreas Schiffler <aschiffler@ferzkopp.net>
Mon, 03 Jun 2013 19:24:18 -0700
changeset 7268 b399d0441810
parent 7191 75360622e65f
child 7435 63e300e4bd26
permissions -rw-r--r--
Add missing VS2012 test projects; update VS2010 and VS2012 solutions; update keybord suite for VS compiler warnings
     1 /*
     2   Copyright (C) 1997-2011 Sam Lantinga <slouken@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     printf("\nspin lock---------------------------------------\n\n");
    49 
    50     SDL_AtomicLock(&lock);
    51     printf("AtomicLock                   lock=%d\n", lock);
    52     SDL_AtomicUnlock(&lock);
    53     printf("AtomicUnlock                 lock=%d\n", lock);
    54 
    55     printf("\natomic -----------------------------------------\n\n");
    56 
    57     SDL_AtomicSet(&v, 0);
    58     tfret = SDL_AtomicSet(&v, 10) == 0;
    59     printf("AtomicSet(10)        tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
    60     tfret = SDL_AtomicAdd(&v, 10) == 10;
    61     printf("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);
    66     printf("AtomicIncRef()       tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
    67     SDL_AtomicIncRef(&v);
    68     tfret = (SDL_AtomicGet(&v) == 2);
    69     printf("AtomicIncRef()       tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
    70     tfret = (SDL_AtomicDecRef(&v) == SDL_FALSE);
    71     printf("AtomicDecRef()       tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
    72     tfret = (SDL_AtomicDecRef(&v) == SDL_TRUE);
    73     printf("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);
    77     printf("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);
    80     printf("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  * Serveral 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     printf("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     printf("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     printf("\nepic test---------------------------------------\n\n");
   165 
   166     printf("Size asserted to be >= 32-bit\n");
   167     SDL_assert(sizeof(atomicValue)>=4);
   168 
   169     printf("Check static initializer\n");
   170     v=SDL_AtomicGet(&good);
   171     SDL_assert(v==42);
   172 
   173     SDL_assert(bad==42);
   174 
   175     printf("Test negative values\n");
   176     SDL_AtomicSet(&good, -5);
   177     v=SDL_AtomicGet(&good);
   178     SDL_assert(v==-5);
   179 
   180     printf("Verify maximum value\n");
   181     SDL_AtomicSet(&good, CountTo);
   182     v=SDL_AtomicGet(&good);
   183     SDL_assert(v==CountTo);
   184 
   185     printf("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     printf("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     printf("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     printf("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     printf("Counting down from %d, Expect %d remaining\n",CountTo,Expect);
   235     runAdder();
   236 
   237     v=SDL_AtomicGet(&good);
   238     printf("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         printf("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         printf("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 index = data->index;
   502     int i;
   503     SDL_Event event;
   504 
   505     event.type = SDL_USEREVENT;
   506     event.user.windowID = 0;
   507     event.user.code = 0;
   508     event.user.data1 = data;
   509     event.user.data2 = NULL;
   510 
   511     if (data->lock_free) {
   512         for (i = 0; i < EVENTS_PER_WRITER; ++i) {
   513             event.user.code = i;
   514             while (!EnqueueEvent_LockFree(queue, &event)) {
   515                 ++data->waits;
   516                 SDL_Delay(0);
   517             }
   518         }
   519     } else {
   520         for (i = 0; i < EVENTS_PER_WRITER; ++i) {
   521             event.user.code = i;
   522             while (!EnqueueEvent_Mutex(queue, &event)) {
   523                 ++data->waits;
   524                 SDL_Delay(0);
   525             }
   526         }
   527     }
   528     SDL_AtomicAdd(&writersRunning, -1);
   529     SDL_SemPost(writersDone);
   530     return 0;
   531 }
   532 
   533 static int FIFO_Reader(void* _data)
   534 {
   535     ReaderData *data = (ReaderData *)_data;
   536     SDL_EventQueue *queue = data->queue;
   537     SDL_Event event;
   538 
   539     if (data->lock_free) {
   540         for ( ; ; ) {
   541             if (DequeueEvent_LockFree(queue, &event)) {
   542                 WriterData *writer = (WriterData*)event.user.data1;
   543                 ++data->counters[writer->index];
   544             } else if (queue->active) {
   545                 ++data->waits;
   546                 SDL_Delay(0);
   547             } else {
   548                 /* We drained the queue, we're done! */
   549                 break;
   550             }
   551         }
   552     } else {
   553         for ( ; ; ) {
   554             if (DequeueEvent_Mutex(queue, &event)) {
   555                 WriterData *writer = (WriterData*)event.user.data1;
   556                 ++data->counters[writer->index];
   557             } else if (queue->active) {
   558                 ++data->waits;
   559                 SDL_Delay(0);
   560             } else {
   561                 /* We drained the queue, we're done! */
   562                 break;
   563             }
   564         }
   565     }
   566     SDL_AtomicAdd(&readersRunning, -1);
   567     SDL_SemPost(readersDone);
   568     return 0;
   569 }
   570 
   571 #ifdef TEST_SPINLOCK_FIFO
   572 /* This thread periodically locks the queue for no particular reason */
   573 static int FIFO_Watcher(void* _data)
   574 {
   575     SDL_EventQueue *queue = (SDL_EventQueue *)_data;
   576 
   577     while (queue->active) {
   578         SDL_AtomicLock(&queue->lock);
   579         SDL_AtomicIncRef(&queue->watcher);
   580         while (SDL_AtomicGet(&queue->rwcount) > 0) {
   581             SDL_Delay(0);
   582         }
   583         /* Do queue manipulation here... */
   584         SDL_AtomicDecRef(&queue->watcher);
   585         SDL_AtomicUnlock(&queue->lock);
   586 
   587         /* Wait a bit... */
   588         SDL_Delay(1);
   589     }
   590     return 0;
   591 }
   592 #endif /* TEST_SPINLOCK_FIFO */
   593 
   594 static void RunFIFOTest(SDL_bool lock_free)
   595 {
   596     SDL_EventQueue queue;
   597     WriterData writerData[NUM_WRITERS];
   598     ReaderData readerData[NUM_READERS];
   599     Uint32 start, end;
   600     int i, j;
   601     int grand_total;
   602 
   603     printf("\nFIFO test---------------------------------------\n\n");
   604     printf("Mode: %s\n", lock_free ? "LockFree" : "Mutex");
   605 
   606     readersDone = SDL_CreateSemaphore(0);
   607     writersDone = SDL_CreateSemaphore(0);
   608 
   609     SDL_memset(&queue, 0xff, sizeof(queue));
   610 
   611     InitEventQueue(&queue);
   612     if (!lock_free) {
   613         queue.mutex = SDL_CreateMutex();
   614     }
   615 
   616     start = SDL_GetTicks();
   617 
   618 #ifdef TEST_SPINLOCK_FIFO
   619     /* Start a monitoring thread */
   620     if (lock_free) {
   621         SDL_CreateThread(FIFO_Watcher, "FIFOWatcher", &queue);
   622     }
   623 #endif
   624 
   625     /* Start the readers first */
   626     printf("Starting %d readers\n", NUM_READERS);
   627     SDL_zero(readerData);
   628     SDL_AtomicSet(&readersRunning, NUM_READERS);
   629     for (i = 0; i < NUM_READERS; ++i) {
   630         char name[64];
   631         SDL_snprintf(name, sizeof (name), "FIFOReader%d", i);
   632         readerData[i].queue = &queue;
   633         readerData[i].lock_free = lock_free;
   634         SDL_CreateThread(FIFO_Reader, name, &readerData[i]);
   635     }
   636 
   637     /* Start up the writers */
   638     printf("Starting %d writers\n", NUM_WRITERS);
   639     SDL_zero(writerData);
   640     SDL_AtomicSet(&writersRunning, NUM_WRITERS);
   641     for (i = 0; i < NUM_WRITERS; ++i) {
   642         char name[64];
   643         SDL_snprintf(name, sizeof (name), "FIFOWriter%d", i);
   644         writerData[i].queue = &queue;
   645         writerData[i].index = i;
   646         writerData[i].lock_free = lock_free;
   647         SDL_CreateThread(FIFO_Writer, name, &writerData[i]);
   648     }
   649 
   650     /* Wait for the writers */
   651     while (SDL_AtomicGet(&writersRunning) > 0) {
   652         SDL_SemWait(writersDone);
   653     }
   654 
   655     /* Shut down the queue so readers exit */
   656     queue.active = SDL_FALSE;
   657 
   658     /* Wait for the readers */
   659     while (SDL_AtomicGet(&readersRunning) > 0) {
   660         SDL_SemWait(readersDone);
   661     }
   662 
   663     end = SDL_GetTicks();
   664 
   665     SDL_DestroySemaphore(readersDone);
   666     SDL_DestroySemaphore(writersDone);
   667 
   668     if (!lock_free) {
   669         SDL_DestroyMutex(queue.mutex);
   670     }
   671 
   672     printf("Finished in %f sec\n", (end - start) / 1000.f);
   673 
   674     printf("\n");
   675     for (i = 0; i < NUM_WRITERS; ++i) {
   676         printf("Writer %d wrote %d events, had %d waits\n", i, EVENTS_PER_WRITER, writerData[i].waits);
   677     }
   678     printf("Writers wrote %d total events\n", NUM_WRITERS*EVENTS_PER_WRITER);
   679 
   680     /* Print a breakdown of which readers read messages from which writer */
   681     printf("\n");
   682     grand_total = 0;
   683     for (i = 0; i < NUM_READERS; ++i) {
   684         int total = 0;
   685         for (j = 0; j < NUM_WRITERS; ++j) {
   686             total += readerData[i].counters[j];
   687         }
   688         grand_total += total;
   689         printf("Reader %d read %d events, had %d waits\n", i, total, readerData[i].waits);
   690         printf("  { ");
   691         for (j = 0; j < NUM_WRITERS; ++j) {
   692             if (j > 0) {
   693                 printf(", ");
   694             }
   695             printf("%d", readerData[i].counters[j]);
   696         }
   697         printf(" }\n");
   698     }
   699     printf("Readers read %d total events\n", grand_total);
   700 }
   701 
   702 /* End FIFO test */
   703 /**************************************************************************/
   704 
   705 int
   706 main(int argc, char *argv[])
   707 {
   708     RunBasicTest();
   709     RunEpicTest();
   710 /* This test is really slow, so don't run it by default */
   711 #if 0
   712     RunFIFOTest(SDL_FALSE);
   713 #endif
   714     RunFIFOTest(SDL_TRUE);
   715     return 0;
   716 }
   717 
   718 /* vi: set ts=4 sw=4 expandtab: */