cscg22-gearboy

CSCG 2022 Challenge 'Gearboy'
git clone https://git.sinitax.com/sinitax/cscg22-gearboy
Log | Files | Refs | sfeed.txt

testatomic.c (20556B)


      1/*
      2  Copyright (C) 1997-2014 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
     24static
     25char *
     26tf(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
     39static
     40void 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
    109SDL_COMPILE_TIME_ASSERT(size, CountTo>0); /* check for rollover */
    110
    111static SDL_atomic_t good = { 42 };
    112
    113static atomicValue bad = 42;
    114
    115static SDL_atomic_t threadsRunning;
    116
    117static SDL_sem *threadDone;
    118
    119static
    120int 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
    133static
    134void 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
    158static
    159void 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
    262typedef struct
    263{
    264    SDL_atomic_t sequence;
    265    SDL_Event event;
    266} SDL_EventQueueEntry;
    267
    268typedef 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
    297static 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    queue->active = SDL_TRUE;
    312}
    313
    314static 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
    361static 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
    408static 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
    441static 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
    474static SDL_sem *writersDone;
    475static SDL_sem *readersDone;
    476static SDL_atomic_t writersRunning;
    477static SDL_atomic_t readersRunning;
    478
    479typedef 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
    489typedef 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
    498static int 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
    533static 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 */
    573static 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
    594static 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	int 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    queue.active = SDL_FALSE;
    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
    711int
    712main(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: */