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