test/testatomic.c
author Jinke Fan <fanjinke@hygon.cn>
Wed, 15 May 2019 19:54:36 +0800
changeset 13082 f77988be3607
parent 12968 0e3948762c96
permissions -rw-r--r--
Add Hygon Dhyana processor support

Background:
Chengdu Haiguang IC Design Co., Ltd (Hygon) is a Joint Venture
between AMD and Haiguang Information Technology Co.,Ltd., aims at
providing high performance x86 processor for China server market.
Its first generation processor codename is Dhyana, which
originates from AMD technology and shares most of the
architecture with AMD's family 17h, but with different CPU Vendor
ID("HygonGenuine")/Family series number(Family 18h).

Related Hygon kernel patch can be found on:
http://lkml.kernel.org/r/5ce86123a7b9dad925ac583d88d2f921040e859b.1538583282.git.puwen@hygon.cn

Best regards.
     1 /*
     2   Copyright (C) 1997-2019 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: */