sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

testatomic.c (20680B)


      1 /*
      2   Copyright (C) 1997-2020 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 
     16 /*
     17   Absolutely basic tests just to see if we get the expected value
     18   after calling each function.
     19 */
     20 
     21 static
     22 char *
     23 tf(SDL_bool tf)
     24 {
     25     static char *t = "TRUE";
     26     static char *f = "FALSE";
     27 
     28     if (tf)
     29     {
     30        return t;
     31     }
     32 
     33     return f;
     34 }
     35 
     36 static
     37 void RunBasicTest()
     38 {
     39     int value;
     40     SDL_SpinLock lock = 0;
     41 
     42     SDL_atomic_t v;
     43     SDL_bool tfret = SDL_FALSE;
     44 
     45     SDL_Log("\nspin lock---------------------------------------\n\n");
     46 
     47     SDL_AtomicLock(&lock);
     48     SDL_Log("AtomicLock                   lock=%d\n", lock);
     49     SDL_AtomicUnlock(&lock);
     50     SDL_Log("AtomicUnlock                 lock=%d\n", lock);
     51 
     52     SDL_Log("\natomic -----------------------------------------\n\n");
     53 
     54     SDL_AtomicSet(&v, 0);
     55     tfret = SDL_AtomicSet(&v, 10) == 0 ? SDL_TRUE : SDL_FALSE;
     56     SDL_Log("AtomicSet(10)        tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
     57     tfret = SDL_AtomicAdd(&v, 10) == 10 ? SDL_TRUE : SDL_FALSE;
     58     SDL_Log("AtomicAdd(10)        tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
     59 
     60     SDL_AtomicSet(&v, 0);
     61     SDL_AtomicIncRef(&v);
     62     tfret = (SDL_AtomicGet(&v) == 1) ? SDL_TRUE : SDL_FALSE;
     63     SDL_Log("AtomicIncRef()       tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
     64     SDL_AtomicIncRef(&v);
     65     tfret = (SDL_AtomicGet(&v) == 2) ? SDL_TRUE : SDL_FALSE;
     66     SDL_Log("AtomicIncRef()       tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
     67     tfret = (SDL_AtomicDecRef(&v) == SDL_FALSE) ? SDL_TRUE : SDL_FALSE;
     68     SDL_Log("AtomicDecRef()       tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
     69     tfret = (SDL_AtomicDecRef(&v) == SDL_TRUE) ? SDL_TRUE : SDL_FALSE;
     70     SDL_Log("AtomicDecRef()       tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
     71 
     72     SDL_AtomicSet(&v, 10);
     73     tfret = (SDL_AtomicCAS(&v, 0, 20) == SDL_FALSE) ? SDL_TRUE : SDL_FALSE;
     74     SDL_Log("AtomicCAS()          tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
     75     value = SDL_AtomicGet(&v);
     76     tfret = (SDL_AtomicCAS(&v, value, 20) == SDL_TRUE) ? SDL_TRUE : SDL_FALSE;
     77     SDL_Log("AtomicCAS()          tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
     78 }
     79 
     80 /**************************************************************************/
     81 /* Atomic operation test
     82  * Adapted with permission from code by Michael Davidsaver at:
     83  *  http://bazaar.launchpad.net/~mdavidsaver/epics-base/atomic/revision/12105#src/libCom/test/epicsAtomicTest.c
     84  * Original copyright 2010 Brookhaven Science Associates as operator of Brookhaven National Lab
     85  * http://www.aps.anl.gov/epics/license/open.php
     86  */
     87 
     88 /* Tests semantics of atomic operations.  Also a stress test
     89  * to see if they are really atomic.
     90  *
     91  * Several threads adding to the same variable.
     92  * at the end the value is compared with the expected
     93  * and with a non-atomic counter.
     94  */
     95 
     96 /* Number of concurrent incrementers */
     97 #define NThreads 2
     98 #define CountInc 100
     99 #define VALBITS (sizeof(atomicValue)*8)
    100 
    101 #define atomicValue int
    102 #define CountTo ((atomicValue)((unsigned int)(1<<(VALBITS-1))-1))
    103 #define NInter (CountTo/CountInc/NThreads)
    104 #define Expect (CountTo-NInter*CountInc*NThreads)
    105 
    106 enum {
    107    CountTo_GreaterThanZero = CountTo > 0,
    108 };
    109 SDL_COMPILE_TIME_ASSERT(size, CountTo_GreaterThanZero); /* 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 SDLCALL 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     SDL_atomic_t 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     SDL_AtomicSet(&queue->watcher, 0);
    310 #endif
    311     SDL_AtomicSet(&queue->active, 1);
    312 }
    313 
    314 static SDL_bool EnqueueEvent_LockFree(SDL_EventQueue *queue, const SDL_Event *event)
    315 {
    316     SDL_EventQueueEntry *entry;
    317     unsigned queue_pos;
    318     unsigned entry_seq;
    319     int delta;
    320     SDL_bool status;
    321 
    322 #ifdef TEST_SPINLOCK_FIFO
    323     /* This is a gate so an external thread can lock the queue */
    324     SDL_AtomicLock(&queue->lock);
    325     SDL_assert(SDL_AtomicGet(&queue->watcher) == 0);
    326     SDL_AtomicIncRef(&queue->rwcount);
    327     SDL_AtomicUnlock(&queue->lock);
    328 #endif
    329 
    330     queue_pos = (unsigned)SDL_AtomicGet(&queue->enqueue_pos);
    331     for ( ; ; ) {
    332         entry = &queue->entries[queue_pos & WRAP_MASK];
    333         entry_seq = (unsigned)SDL_AtomicGet(&entry->sequence);
    334 
    335         delta = (int)(entry_seq - queue_pos);
    336         if (delta == 0) {
    337             /* The entry and the queue position match, try to increment the queue position */
    338             if (SDL_AtomicCAS(&queue->enqueue_pos, (int)queue_pos, (int)(queue_pos+1))) {
    339                 /* We own the object, fill it! */
    340                 entry->event = *event;
    341                 SDL_AtomicSet(&entry->sequence, (int)(queue_pos + 1));
    342                 status = SDL_TRUE;
    343                 break;
    344             }
    345         } else if (delta < 0) {
    346             /* We ran into an old queue entry, which means it still needs to be dequeued */
    347             status = SDL_FALSE;
    348             break;
    349         } else {
    350             /* We ran into a new queue entry, get the new queue position */
    351             queue_pos = (unsigned)SDL_AtomicGet(&queue->enqueue_pos);
    352         }
    353     }
    354 
    355 #ifdef TEST_SPINLOCK_FIFO
    356     SDL_AtomicDecRef(&queue->rwcount);
    357 #endif
    358     return status;
    359 }
    360 
    361 static SDL_bool DequeueEvent_LockFree(SDL_EventQueue *queue, SDL_Event *event)
    362 {
    363     SDL_EventQueueEntry *entry;
    364     unsigned queue_pos;
    365     unsigned entry_seq;
    366     int delta;
    367     SDL_bool status;
    368 
    369 #ifdef TEST_SPINLOCK_FIFO
    370     /* This is a gate so an external thread can lock the queue */
    371     SDL_AtomicLock(&queue->lock);
    372     SDL_assert(SDL_AtomicGet(&queue->watcher) == 0);
    373     SDL_AtomicIncRef(&queue->rwcount);
    374     SDL_AtomicUnlock(&queue->lock);
    375 #endif
    376 
    377     queue_pos = (unsigned)SDL_AtomicGet(&queue->dequeue_pos);
    378     for ( ; ; ) {
    379         entry = &queue->entries[queue_pos & WRAP_MASK];
    380         entry_seq = (unsigned)SDL_AtomicGet(&entry->sequence);
    381 
    382         delta = (int)(entry_seq - (queue_pos + 1));
    383         if (delta == 0) {
    384             /* The entry and the queue position match, try to increment the queue position */
    385             if (SDL_AtomicCAS(&queue->dequeue_pos, (int)queue_pos, (int)(queue_pos+1))) {
    386                 /* We own the object, fill it! */
    387                 *event = entry->event;
    388                 SDL_AtomicSet(&entry->sequence, (int)(queue_pos+MAX_ENTRIES));
    389                 status = SDL_TRUE;
    390                 break;
    391             }
    392         } else if (delta < 0) {
    393             /* We ran into an old queue entry, which means we've hit empty */
    394             status = SDL_FALSE;
    395             break;
    396         } else {
    397             /* We ran into a new queue entry, get the new queue position */
    398             queue_pos = (unsigned)SDL_AtomicGet(&queue->dequeue_pos);
    399         }
    400     }
    401 
    402 #ifdef TEST_SPINLOCK_FIFO
    403     SDL_AtomicDecRef(&queue->rwcount);
    404 #endif
    405     return status;
    406 }
    407 
    408 static SDL_bool EnqueueEvent_Mutex(SDL_EventQueue *queue, const SDL_Event *event)
    409 {
    410     SDL_EventQueueEntry *entry;
    411     unsigned queue_pos;
    412     unsigned entry_seq;
    413     int delta;
    414     SDL_bool status = SDL_FALSE;
    415 
    416     SDL_LockMutex(queue->mutex);
    417 
    418     queue_pos = (unsigned)queue->enqueue_pos.value;
    419     entry = &queue->entries[queue_pos & WRAP_MASK];
    420     entry_seq = (unsigned)entry->sequence.value;
    421 
    422     delta = (int)(entry_seq - queue_pos);
    423     if (delta == 0) {
    424         ++queue->enqueue_pos.value;
    425 
    426         /* We own the object, fill it! */
    427         entry->event = *event;
    428         entry->sequence.value = (int)(queue_pos + 1);
    429         status = SDL_TRUE;
    430     } else if (delta < 0) {
    431         /* We ran into an old queue entry, which means it still needs to be dequeued */
    432     } else {
    433         SDL_Log("ERROR: mutex failed!\n");
    434     }
    435 
    436     SDL_UnlockMutex(queue->mutex);
    437 
    438     return status;
    439 }
    440 
    441 static SDL_bool DequeueEvent_Mutex(SDL_EventQueue *queue, SDL_Event *event)
    442 {
    443     SDL_EventQueueEntry *entry;
    444     unsigned queue_pos;
    445     unsigned entry_seq;
    446     int delta;
    447     SDL_bool status = SDL_FALSE;
    448 
    449     SDL_LockMutex(queue->mutex);
    450 
    451     queue_pos = (unsigned)queue->dequeue_pos.value;
    452     entry = &queue->entries[queue_pos & WRAP_MASK];
    453     entry_seq = (unsigned)entry->sequence.value;
    454 
    455     delta = (int)(entry_seq - (queue_pos + 1));
    456     if (delta == 0) {
    457         ++queue->dequeue_pos.value;
    458 
    459         /* We own the object, fill it! */
    460         *event = entry->event;
    461         entry->sequence.value = (int)(queue_pos + MAX_ENTRIES);
    462         status = SDL_TRUE;
    463     } else if (delta < 0) {
    464         /* We ran into an old queue entry, which means we've hit empty */
    465     } else {
    466         SDL_Log("ERROR: mutex failed!\n");
    467     }
    468 
    469     SDL_UnlockMutex(queue->mutex);
    470 
    471     return status;
    472 }
    473 
    474 static SDL_sem *writersDone;
    475 static SDL_sem *readersDone;
    476 static SDL_atomic_t writersRunning;
    477 static SDL_atomic_t readersRunning;
    478 
    479 typedef struct
    480 {
    481     SDL_EventQueue *queue;
    482     int index;
    483     char padding1[SDL_CACHELINE_SIZE-(sizeof(SDL_EventQueue*)+sizeof(int))%SDL_CACHELINE_SIZE];
    484     int waits;
    485     SDL_bool lock_free;
    486     char padding2[SDL_CACHELINE_SIZE-sizeof(int)-sizeof(SDL_bool)];
    487 } WriterData;
    488 
    489 typedef struct
    490 {
    491     SDL_EventQueue *queue;
    492     int counters[NUM_WRITERS];
    493     int waits;
    494     SDL_bool lock_free;
    495     char padding[SDL_CACHELINE_SIZE-(sizeof(SDL_EventQueue*)+sizeof(int)*NUM_WRITERS+sizeof(int)+sizeof(SDL_bool))%SDL_CACHELINE_SIZE];
    496 } ReaderData;
    497 
    498 static int SDLCALL FIFO_Writer(void* _data)
    499 {
    500     WriterData *data = (WriterData *)_data;
    501     SDL_EventQueue *queue = data->queue;
    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 SDLCALL 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 (SDL_AtomicGet(&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 (SDL_AtomicGet(&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 SDLCALL FIFO_Watcher(void* _data)
    574 {
    575     SDL_EventQueue *queue = (SDL_EventQueue *)_data;
    576 
    577     while (SDL_AtomicGet(&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     char textBuffer[1024];
    603     size_t len;
    604 
    605     SDL_Log("\nFIFO test---------------------------------------\n\n");
    606     SDL_Log("Mode: %s\n", lock_free ? "LockFree" : "Mutex");
    607 
    608     readersDone = SDL_CreateSemaphore(0);
    609     writersDone = SDL_CreateSemaphore(0);
    610 
    611     SDL_memset(&queue, 0xff, sizeof(queue));
    612 
    613     InitEventQueue(&queue);
    614     if (!lock_free) {
    615         queue.mutex = SDL_CreateMutex();
    616     }
    617 
    618     start = SDL_GetTicks();
    619 
    620 #ifdef TEST_SPINLOCK_FIFO
    621     /* Start a monitoring thread */
    622     if (lock_free) {
    623         SDL_CreateThread(FIFO_Watcher, "FIFOWatcher", &queue);
    624     }
    625 #endif
    626 
    627     /* Start the readers first */
    628     SDL_Log("Starting %d readers\n", NUM_READERS);
    629     SDL_zeroa(readerData);
    630     SDL_AtomicSet(&readersRunning, NUM_READERS);
    631     for (i = 0; i < NUM_READERS; ++i) {
    632         char name[64];
    633         SDL_snprintf(name, sizeof (name), "FIFOReader%d", i);
    634         readerData[i].queue = &queue;
    635         readerData[i].lock_free = lock_free;
    636         SDL_CreateThread(FIFO_Reader, name, &readerData[i]);
    637     }
    638 
    639     /* Start up the writers */
    640     SDL_Log("Starting %d writers\n", NUM_WRITERS);
    641     SDL_zeroa(writerData);
    642     SDL_AtomicSet(&writersRunning, NUM_WRITERS);
    643     for (i = 0; i < NUM_WRITERS; ++i) {
    644         char name[64];
    645         SDL_snprintf(name, sizeof (name), "FIFOWriter%d", i);
    646         writerData[i].queue = &queue;
    647         writerData[i].index = i;
    648         writerData[i].lock_free = lock_free;
    649         SDL_CreateThread(FIFO_Writer, name, &writerData[i]);
    650     }
    651 
    652     /* Wait for the writers */
    653     while (SDL_AtomicGet(&writersRunning) > 0) {
    654         SDL_SemWait(writersDone);
    655     }
    656 
    657     /* Shut down the queue so readers exit */
    658     SDL_AtomicSet(&queue.active, 0);
    659 
    660     /* Wait for the readers */
    661     while (SDL_AtomicGet(&readersRunning) > 0) {
    662         SDL_SemWait(readersDone);
    663     }
    664 
    665     end = SDL_GetTicks();
    666 
    667     SDL_DestroySemaphore(readersDone);
    668     SDL_DestroySemaphore(writersDone);
    669 
    670     if (!lock_free) {
    671         SDL_DestroyMutex(queue.mutex);
    672     }
    673 
    674     SDL_Log("Finished in %f sec\n", (end - start) / 1000.f);
    675 
    676     SDL_Log("\n");
    677     for (i = 0; i < NUM_WRITERS; ++i) {
    678         SDL_Log("Writer %d wrote %d events, had %d waits\n", i, EVENTS_PER_WRITER, writerData[i].waits);
    679     }
    680     SDL_Log("Writers wrote %d total events\n", NUM_WRITERS*EVENTS_PER_WRITER);
    681 
    682     /* Print a breakdown of which readers read messages from which writer */
    683     SDL_Log("\n");
    684     grand_total = 0;
    685     for (i = 0; i < NUM_READERS; ++i) {
    686         int total = 0;
    687         for (j = 0; j < NUM_WRITERS; ++j) {
    688             total += readerData[i].counters[j];
    689         }
    690         grand_total += total;
    691         SDL_Log("Reader %d read %d events, had %d waits\n", i, total, readerData[i].waits);
    692         SDL_snprintf(textBuffer, sizeof(textBuffer), "  { ");
    693         for (j = 0; j < NUM_WRITERS; ++j) {
    694             if (j > 0) {
    695                 len = SDL_strlen(textBuffer);
    696                 SDL_snprintf(textBuffer + len, sizeof(textBuffer) - len, ", ");
    697             }
    698             len = SDL_strlen(textBuffer);
    699             SDL_snprintf(textBuffer + len, sizeof(textBuffer) - len, "%d", readerData[i].counters[j]);
    700         }
    701         len = SDL_strlen(textBuffer);
    702         SDL_snprintf(textBuffer + len, sizeof(textBuffer) - len, " }\n");
    703         SDL_Log("%s", textBuffer);
    704     }
    705     SDL_Log("Readers read %d total events\n", grand_total);
    706 }
    707 
    708 /* End FIFO test */
    709 /**************************************************************************/
    710 
    711 int
    712 main(int argc, char *argv[])
    713 {
    714     /* Enable standard application logging */
    715     SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
    716 
    717     RunBasicTest();
    718     RunEpicTest();
    719 /* This test is really slow, so don't run it by default */
    720 #if 0
    721     RunFIFOTest(SDL_FALSE);
    722 #endif
    723     RunFIFOTest(SDL_TRUE);
    724     return 0;
    725 }
    726 
    727 /* vi: set ts=4 sw=4 expandtab: */