test/testatomic.c
author Philipp Wiesemann <philipp.wiesemann@arcor.de>
Wed, 18 Feb 2015 21:31:21 +0100
changeset 9356 e87d6e1e812a
parent 8981 68e0819f0f56
child 9619 b94b6d0bff0f
permissions -rw-r--r--
Removed redundant include statements from test programs.

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