test/testatomic.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 11 Nov 2017 17:21:47 -0800
changeset 11700 57a894f4028f
parent 11416 7e7d8a125d6a
child 11811 5d94cb6b24d3
permissions -rw-r--r--
Fixed typo in comment
     1 /*
     2   Copyright (C) 1997-2017 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_zero(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_zero(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: */