test/testatomic.c
author David Ludwig <dludwig@pobox.com>
Wed, 25 Dec 2013 21:39:48 -0500
changeset 8563 c0e68f3b6bbb
parent 7639 9406b7dd2f2d
child 8149 681eb46b8ac4
permissions -rw-r--r--
WinRT: compiled the d3d11 renderer's shaders into SDL itself

Previously, the shaders would get compiled separately, the output of which would need to be packaged into the app. This change should make SDL's dll be the only binary needed to include SDL in a WinRT app.
     1 /*
     2   Copyright (C) 1997-2013 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     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: */