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