test/testatomic.c
author Cole Campbell <cole.campbell@twistedlogik.net>
Tue, 09 Jan 2018 19:11:34 -0500
changeset 11854 934d77867168
parent 11811 5d94cb6b24d3
child 12503 806492103856
permissions -rw-r--r--
Allow Android Java shim to be built as an AAR
slouken@5535
     1
/*
slouken@11811
     2
  Copyright (C) 1997-2018 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@10679
   106
enum {
slouken@10679
   107
   CountTo_GreaterThanZero = CountTo > 0,
slouken@10679
   108
};
slouken@10679
   109
SDL_COMPILE_TIME_ASSERT(size, CountTo_GreaterThanZero); /* check for rollover */
slouken@7191
   110
slouken@5004
   111
static SDL_atomic_t good = { 42 };
slouken@7191
   112
slouken@5004
   113
static atomicValue bad = 42;
slouken@7191
   114
slouken@5004
   115
static SDL_atomic_t threadsRunning;
slouken@5004
   116
slouken@5004
   117
static SDL_sem *threadDone;
slouken@7191
   118
slouken@5004
   119
static
slouken@11272
   120
int SDLCALL adder(void* junk)
slouken@5004
   121
{
slouken@5004
   122
    unsigned long N=NInter;
aschiffler@7639
   123
    SDL_Log("Thread subtracting %d %lu times\n",CountInc,N);
slouken@5004
   124
    while (N--) {
slouken@5004
   125
        SDL_AtomicAdd(&good, -CountInc);
slouken@5004
   126
        bad-=CountInc;
slouken@5004
   127
    }
slouken@5004
   128
    SDL_AtomicAdd(&threadsRunning, -1);
slouken@5004
   129
    SDL_SemPost(threadDone);
slouken@5003
   130
    return 0;
slouken@5003
   131
}
slouken@7191
   132
slouken@5004
   133
static
slouken@5004
   134
void runAdder(void)
slouken@5004
   135
{
slouken@5004
   136
    Uint32 start, end;
slouken@5004
   137
    int T=NThreads;
slouken@7191
   138
slouken@5004
   139
    start = SDL_GetTicks();
slouken@7191
   140
slouken@5004
   141
    threadDone = SDL_CreateSemaphore(0);
slouken@5004
   142
slouken@5004
   143
    SDL_AtomicSet(&threadsRunning, NThreads);
slouken@5004
   144
slouken@5004
   145
    while (T--)
icculus@5969
   146
        SDL_CreateThread(adder, "Adder", NULL);
slouken@7191
   147
slouken@5004
   148
    while (SDL_AtomicGet(&threadsRunning) > 0)
slouken@5004
   149
        SDL_SemWait(threadDone);
slouken@7191
   150
slouken@5004
   151
    SDL_DestroySemaphore(threadDone);
slouken@5004
   152
slouken@5004
   153
    end = SDL_GetTicks();
slouken@7191
   154
aschiffler@7639
   155
    SDL_Log("Finished in %f sec\n", (end - start) / 1000.f);
slouken@5004
   156
}
slouken@7191
   157
slouken@5004
   158
static
slouken@5004
   159
void RunEpicTest()
slouken@5004
   160
{
slouken@5004
   161
    int b;
slouken@5004
   162
    atomicValue v;
slouken@7191
   163
aschiffler@7639
   164
    SDL_Log("\nepic test---------------------------------------\n\n");
slouken@5004
   165
aschiffler@7639
   166
    SDL_Log("Size asserted to be >= 32-bit\n");
slouken@5004
   167
    SDL_assert(sizeof(atomicValue)>=4);
slouken@7191
   168
aschiffler@7639
   169
    SDL_Log("Check static initializer\n");
slouken@5004
   170
    v=SDL_AtomicGet(&good);
slouken@5004
   171
    SDL_assert(v==42);
slouken@7191
   172
slouken@5004
   173
    SDL_assert(bad==42);
slouken@7191
   174
aschiffler@7639
   175
    SDL_Log("Test negative values\n");
slouken@5004
   176
    SDL_AtomicSet(&good, -5);
slouken@5004
   177
    v=SDL_AtomicGet(&good);
slouken@5004
   178
    SDL_assert(v==-5);
slouken@7191
   179
aschiffler@7639
   180
    SDL_Log("Verify maximum value\n");
slouken@5004
   181
    SDL_AtomicSet(&good, CountTo);
slouken@5004
   182
    v=SDL_AtomicGet(&good);
slouken@5004
   183
    SDL_assert(v==CountTo);
slouken@7191
   184
aschiffler@7639
   185
    SDL_Log("Test compare and exchange\n");
slouken@7191
   186
slouken@5004
   187
    b=SDL_AtomicCAS(&good, 500, 43);
slouken@5004
   188
    SDL_assert(!b); /* no swap since CountTo!=500 */
slouken@5004
   189
    v=SDL_AtomicGet(&good);
slouken@5004
   190
    SDL_assert(v==CountTo); /* ensure no swap */
slouken@7191
   191
slouken@5004
   192
    b=SDL_AtomicCAS(&good, CountTo, 44);
slouken@5004
   193
    SDL_assert(!!b); /* will swap */
slouken@5004
   194
    v=SDL_AtomicGet(&good);
slouken@5004
   195
    SDL_assert(v==44);
slouken@7191
   196
aschiffler@7639
   197
    SDL_Log("Test Add\n");
slouken@7191
   198
slouken@5004
   199
    v=SDL_AtomicAdd(&good, 1);
slouken@5004
   200
    SDL_assert(v==44);
slouken@5004
   201
    v=SDL_AtomicGet(&good);
slouken@5004
   202
    SDL_assert(v==45);
slouken@7191
   203
slouken@5004
   204
    v=SDL_AtomicAdd(&good, 10);
slouken@5004
   205
    SDL_assert(v==45);
slouken@5004
   206
    v=SDL_AtomicGet(&good);
slouken@5004
   207
    SDL_assert(v==55);
slouken@7191
   208
aschiffler@7639
   209
    SDL_Log("Test Add (Negative values)\n");
slouken@7191
   210
slouken@5004
   211
    v=SDL_AtomicAdd(&good, -20);
slouken@5004
   212
    SDL_assert(v==55);
slouken@5004
   213
    v=SDL_AtomicGet(&good);
slouken@5004
   214
    SDL_assert(v==35);
slouken@7191
   215
slouken@5004
   216
    v=SDL_AtomicAdd(&good, -50); /* crossing zero down */
slouken@5004
   217
    SDL_assert(v==35);
slouken@5004
   218
    v=SDL_AtomicGet(&good);
slouken@5004
   219
    SDL_assert(v==-15);
slouken@7191
   220
slouken@5004
   221
    v=SDL_AtomicAdd(&good, 30); /* crossing zero up */
slouken@5004
   222
    SDL_assert(v==-15);
slouken@5004
   223
    v=SDL_AtomicGet(&good);
slouken@5004
   224
    SDL_assert(v==15);
slouken@7191
   225
aschiffler@7639
   226
    SDL_Log("Reset before count down test\n");
slouken@5004
   227
    SDL_AtomicSet(&good, CountTo);
slouken@5004
   228
    v=SDL_AtomicGet(&good);
slouken@5004
   229
    SDL_assert(v==CountTo);
slouken@7191
   230
slouken@5004
   231
    bad=CountTo;
slouken@5004
   232
    SDL_assert(bad==CountTo);
slouken@7191
   233
aschiffler@7639
   234
    SDL_Log("Counting down from %d, Expect %d remaining\n",CountTo,Expect);
slouken@5004
   235
    runAdder();
slouken@7191
   236
slouken@5004
   237
    v=SDL_AtomicGet(&good);
aschiffler@7639
   238
    SDL_Log("Atomic %d Non-Atomic %d\n",v,bad);
slouken@5004
   239
    SDL_assert(v==Expect);
slouken@5004
   240
    SDL_assert(bad!=Expect);
slouken@5004
   241
}
slouken@5004
   242
slouken@5018
   243
/* End atomic operation test */
slouken@5018
   244
/**************************************************************************/
slouken@5018
   245
slouken@5098
   246
/**************************************************************************/
slouken@5098
   247
/* Lock-free FIFO test */
slouken@5098
   248
slouken@5099
   249
/* This is useful to test the impact of another thread locking the queue
slouken@5099
   250
   entirely for heavy-weight manipulation.
slouken@5099
   251
 */
slouken@5099
   252
#define TEST_SPINLOCK_FIFO
slouken@5099
   253
slouken@5098
   254
#define NUM_READERS 4
slouken@5098
   255
#define NUM_WRITERS 4
slouken@5098
   256
#define EVENTS_PER_WRITER   1000000
slouken@5098
   257
slouken@5098
   258
/* The number of entries must be a power of 2 */
slouken@5098
   259
#define MAX_ENTRIES 256
slouken@5098
   260
#define WRAP_MASK   (MAX_ENTRIES-1)
slouken@5098
   261
slouken@5098
   262
typedef struct
slouken@5098
   263
{
slouken@5098
   264
    SDL_atomic_t sequence;
slouken@5098
   265
    SDL_Event event;
slouken@5098
   266
} SDL_EventQueueEntry;
slouken@5098
   267
slouken@5098
   268
typedef struct
slouken@5098
   269
{
slouken@5098
   270
    SDL_EventQueueEntry entries[MAX_ENTRIES];
slouken@5098
   271
slouken@5115
   272
    char cache_pad1[SDL_CACHELINE_SIZE-((sizeof(SDL_EventQueueEntry)*MAX_ENTRIES)%SDL_CACHELINE_SIZE)];
slouken@5098
   273
slouken@5098
   274
    SDL_atomic_t enqueue_pos;
slouken@5098
   275
slouken@5115
   276
    char cache_pad2[SDL_CACHELINE_SIZE-sizeof(SDL_atomic_t)];
slouken@5098
   277
slouken@5098
   278
    SDL_atomic_t dequeue_pos;
slouken@5098
   279
slouken@5115
   280
    char cache_pad3[SDL_CACHELINE_SIZE-sizeof(SDL_atomic_t)];
slouken@5098
   281
slouken@5099
   282
#ifdef TEST_SPINLOCK_FIFO
slouken@5099
   283
    SDL_SpinLock lock;
slouken@5099
   284
    SDL_atomic_t rwcount;
slouken@5099
   285
    SDL_atomic_t watcher;
slouken@5099
   286
slouken@5115
   287
    char cache_pad4[SDL_CACHELINE_SIZE-sizeof(SDL_SpinLock)-2*sizeof(SDL_atomic_t)];
slouken@5099
   288
#endif
slouken@5099
   289
icculus@10003
   290
    SDL_atomic_t active;
slouken@5098
   291
slouken@5098
   292
    /* Only needed for the mutex test */
slouken@5098
   293
    SDL_mutex *mutex;
slouken@5098
   294
slouken@5098
   295
} SDL_EventQueue;
slouken@5098
   296
slouken@5098
   297
static void InitEventQueue(SDL_EventQueue *queue)
slouken@5098
   298
{
slouken@5098
   299
    int i;
slouken@5098
   300
slouken@5098
   301
    for (i = 0; i < MAX_ENTRIES; ++i) {
slouken@5098
   302
        SDL_AtomicSet(&queue->entries[i].sequence, i);
slouken@5098
   303
    }
slouken@5098
   304
    SDL_AtomicSet(&queue->enqueue_pos, 0);
slouken@5098
   305
    SDL_AtomicSet(&queue->dequeue_pos, 0);
slouken@5099
   306
#ifdef TEST_SPINLOCK_FIFO
slouken@5099
   307
    queue->lock = 0;
slouken@5099
   308
    SDL_AtomicSet(&queue->rwcount, 0);
slouken@8838
   309
    SDL_AtomicSet(&queue->watcher, 0);
slouken@5099
   310
#endif
icculus@10003
   311
    SDL_AtomicSet(&queue->active, 1);
slouken@5098
   312
}
slouken@5098
   313
slouken@5098
   314
static SDL_bool EnqueueEvent_LockFree(SDL_EventQueue *queue, const SDL_Event *event)
slouken@5098
   315
{
slouken@5098
   316
    SDL_EventQueueEntry *entry;
slouken@5098
   317
    unsigned queue_pos;
slouken@5098
   318
    unsigned entry_seq;
slouken@5098
   319
    int delta;
slouken@5099
   320
    SDL_bool status;
slouken@5099
   321
slouken@5099
   322
#ifdef TEST_SPINLOCK_FIFO
slouken@5099
   323
    /* This is a gate so an external thread can lock the queue */
slouken@5099
   324
    SDL_AtomicLock(&queue->lock);
slouken@5099
   325
    SDL_assert(SDL_AtomicGet(&queue->watcher) == 0);
slouken@5099
   326
    SDL_AtomicIncRef(&queue->rwcount);
slouken@5099
   327
    SDL_AtomicUnlock(&queue->lock);
slouken@5099
   328
#endif
slouken@5098
   329
slouken@5098
   330
    queue_pos = (unsigned)SDL_AtomicGet(&queue->enqueue_pos);
slouken@5098
   331
    for ( ; ; ) {
slouken@5098
   332
        entry = &queue->entries[queue_pos & WRAP_MASK];
slouken@5098
   333
        entry_seq = (unsigned)SDL_AtomicGet(&entry->sequence);
slouken@5098
   334
slouken@5098
   335
        delta = (int)(entry_seq - queue_pos);
slouken@5098
   336
        if (delta == 0) {
slouken@5098
   337
            /* The entry and the queue position match, try to increment the queue position */
slouken@5098
   338
            if (SDL_AtomicCAS(&queue->enqueue_pos, (int)queue_pos, (int)(queue_pos+1))) {
slouken@5099
   339
                /* We own the object, fill it! */
slouken@5099
   340
                entry->event = *event;
slouken@5099
   341
                SDL_AtomicSet(&entry->sequence, (int)(queue_pos + 1));
slouken@5099
   342
                status = SDL_TRUE;
slouken@5098
   343
                break;
slouken@5098
   344
            }
slouken@5098
   345
        } else if (delta < 0) {
slouken@5098
   346
            /* We ran into an old queue entry, which means it still needs to be dequeued */
slouken@5099
   347
            status = SDL_FALSE;
slouken@5099
   348
            break;
slouken@5098
   349
        } else {
slouken@5098
   350
            /* We ran into a new queue entry, get the new queue position */
slouken@5098
   351
            queue_pos = (unsigned)SDL_AtomicGet(&queue->enqueue_pos);
slouken@5098
   352
        }
slouken@5098
   353
    }
slouken@5098
   354
slouken@5099
   355
#ifdef TEST_SPINLOCK_FIFO
slouken@5099
   356
    SDL_AtomicDecRef(&queue->rwcount);
slouken@5099
   357
#endif
slouken@5099
   358
    return status;
slouken@5098
   359
}
slouken@5098
   360
slouken@5098
   361
static SDL_bool DequeueEvent_LockFree(SDL_EventQueue *queue, SDL_Event *event)
slouken@5098
   362
{
slouken@5098
   363
    SDL_EventQueueEntry *entry;
slouken@5098
   364
    unsigned queue_pos;
slouken@5098
   365
    unsigned entry_seq;
slouken@5098
   366
    int delta;
slouken@5099
   367
    SDL_bool status;
slouken@5099
   368
slouken@5099
   369
#ifdef TEST_SPINLOCK_FIFO
slouken@5099
   370
    /* This is a gate so an external thread can lock the queue */
slouken@5099
   371
    SDL_AtomicLock(&queue->lock);
slouken@5099
   372
    SDL_assert(SDL_AtomicGet(&queue->watcher) == 0);
slouken@5099
   373
    SDL_AtomicIncRef(&queue->rwcount);
slouken@5099
   374
    SDL_AtomicUnlock(&queue->lock);
slouken@5099
   375
#endif
slouken@5098
   376
slouken@5098
   377
    queue_pos = (unsigned)SDL_AtomicGet(&queue->dequeue_pos);
slouken@5098
   378
    for ( ; ; ) {
slouken@5098
   379
        entry = &queue->entries[queue_pos & WRAP_MASK];
slouken@5098
   380
        entry_seq = (unsigned)SDL_AtomicGet(&entry->sequence);
slouken@5098
   381
slouken@5098
   382
        delta = (int)(entry_seq - (queue_pos + 1));
slouken@5098
   383
        if (delta == 0) {
slouken@5098
   384
            /* The entry and the queue position match, try to increment the queue position */
slouken@5098
   385
            if (SDL_AtomicCAS(&queue->dequeue_pos, (int)queue_pos, (int)(queue_pos+1))) {
slouken@5099
   386
                /* We own the object, fill it! */
slouken@5099
   387
                *event = entry->event;
slouken@5099
   388
                SDL_AtomicSet(&entry->sequence, (int)(queue_pos+MAX_ENTRIES));
slouken@5099
   389
                status = SDL_TRUE;
slouken@5098
   390
                break;
slouken@5098
   391
            }
slouken@5098
   392
        } else if (delta < 0) {
slouken@5098
   393
            /* We ran into an old queue entry, which means we've hit empty */
slouken@5099
   394
            status = SDL_FALSE;
slouken@5099
   395
            break;
slouken@5098
   396
        } else {
slouken@5098
   397
            /* We ran into a new queue entry, get the new queue position */
slouken@5098
   398
            queue_pos = (unsigned)SDL_AtomicGet(&queue->dequeue_pos);
slouken@5098
   399
        }
slouken@5098
   400
    }
slouken@5098
   401
slouken@5099
   402
#ifdef TEST_SPINLOCK_FIFO
slouken@5099
   403
    SDL_AtomicDecRef(&queue->rwcount);
slouken@5099
   404
#endif
slouken@5099
   405
    return status;
slouken@5098
   406
}
slouken@5098
   407
slouken@5098
   408
static SDL_bool EnqueueEvent_Mutex(SDL_EventQueue *queue, const SDL_Event *event)
slouken@5098
   409
{
slouken@5098
   410
    SDL_EventQueueEntry *entry;
slouken@5098
   411
    unsigned queue_pos;
slouken@5098
   412
    unsigned entry_seq;
slouken@5098
   413
    int delta;
slouken@5099
   414
    SDL_bool status = SDL_FALSE;
slouken@5098
   415
slouken@6977
   416
    SDL_LockMutex(queue->mutex);
slouken@5098
   417
slouken@5098
   418
    queue_pos = (unsigned)queue->enqueue_pos.value;
slouken@5098
   419
    entry = &queue->entries[queue_pos & WRAP_MASK];
slouken@5098
   420
    entry_seq = (unsigned)entry->sequence.value;
slouken@5098
   421
slouken@5098
   422
    delta = (int)(entry_seq - queue_pos);
slouken@5098
   423
    if (delta == 0) {
slouken@5098
   424
        ++queue->enqueue_pos.value;
slouken@5099
   425
slouken@5099
   426
        /* We own the object, fill it! */
slouken@5099
   427
        entry->event = *event;
slouken@5099
   428
        entry->sequence.value = (int)(queue_pos + 1);
slouken@5099
   429
        status = SDL_TRUE;
slouken@5098
   430
    } else if (delta < 0) {
slouken@5098
   431
        /* We ran into an old queue entry, which means it still needs to be dequeued */
slouken@5098
   432
    } else {
aschiffler@7639
   433
        SDL_Log("ERROR: mutex failed!\n");
slouken@5098
   434
    }
slouken@5098
   435
slouken@6977
   436
    SDL_UnlockMutex(queue->mutex);
slouken@5098
   437
slouken@5099
   438
    return status;
slouken@5098
   439
}
slouken@5098
   440
slouken@5098
   441
static SDL_bool DequeueEvent_Mutex(SDL_EventQueue *queue, SDL_Event *event)
slouken@5098
   442
{
slouken@5098
   443
    SDL_EventQueueEntry *entry;
slouken@5098
   444
    unsigned queue_pos;
slouken@5098
   445
    unsigned entry_seq;
slouken@5098
   446
    int delta;
slouken@5099
   447
    SDL_bool status = SDL_FALSE;
slouken@5098
   448
slouken@6977
   449
    SDL_LockMutex(queue->mutex);
slouken@5098
   450
slouken@5098
   451
    queue_pos = (unsigned)queue->dequeue_pos.value;
slouken@5098
   452
    entry = &queue->entries[queue_pos & WRAP_MASK];
slouken@5098
   453
    entry_seq = (unsigned)entry->sequence.value;
slouken@5098
   454
slouken@5098
   455
    delta = (int)(entry_seq - (queue_pos + 1));
slouken@5098
   456
    if (delta == 0) {
slouken@5098
   457
        ++queue->dequeue_pos.value;
slouken@5099
   458
slouken@5099
   459
        /* We own the object, fill it! */
slouken@5099
   460
        *event = entry->event;
slouken@5099
   461
        entry->sequence.value = (int)(queue_pos + MAX_ENTRIES);
slouken@5099
   462
        status = SDL_TRUE;
slouken@5098
   463
    } else if (delta < 0) {
slouken@5098
   464
        /* We ran into an old queue entry, which means we've hit empty */
slouken@5098
   465
    } else {
aschiffler@7639
   466
        SDL_Log("ERROR: mutex failed!\n");
slouken@5098
   467
    }
slouken@5098
   468
slouken@6977
   469
    SDL_UnlockMutex(queue->mutex);
slouken@5098
   470
slouken@5099
   471
    return status;
slouken@5098
   472
}
slouken@5098
   473
slouken@5098
   474
static SDL_sem *writersDone;
slouken@5098
   475
static SDL_sem *readersDone;
slouken@5098
   476
static SDL_atomic_t writersRunning;
slouken@5098
   477
static SDL_atomic_t readersRunning;
slouken@5098
   478
slouken@5098
   479
typedef struct
slouken@5098
   480
{
slouken@5098
   481
    SDL_EventQueue *queue;
slouken@5098
   482
    int index;
slouken@5115
   483
    char padding1[SDL_CACHELINE_SIZE-(sizeof(SDL_EventQueue*)+sizeof(int))%SDL_CACHELINE_SIZE];
slouken@5098
   484
    int waits;
slouken@5098
   485
    SDL_bool lock_free;
slouken@5115
   486
    char padding2[SDL_CACHELINE_SIZE-sizeof(int)-sizeof(SDL_bool)];
slouken@5098
   487
} WriterData;
slouken@5098
   488
slouken@5098
   489
typedef struct
slouken@5098
   490
{
slouken@5098
   491
    SDL_EventQueue *queue;
slouken@5098
   492
    int counters[NUM_WRITERS];
slouken@5098
   493
    int waits;
slouken@5098
   494
    SDL_bool lock_free;
slouken@5115
   495
    char padding[SDL_CACHELINE_SIZE-(sizeof(SDL_EventQueue*)+sizeof(int)*NUM_WRITERS+sizeof(int)+sizeof(SDL_bool))%SDL_CACHELINE_SIZE];
slouken@5098
   496
} ReaderData;
slouken@5098
   497
slouken@11272
   498
static int SDLCALL FIFO_Writer(void* _data)
slouken@5098
   499
{
slouken@5098
   500
    WriterData *data = (WriterData *)_data;
slouken@5098
   501
    SDL_EventQueue *queue = data->queue;
slouken@5098
   502
    int i;
slouken@5098
   503
    SDL_Event event;
slouken@5098
   504
slouken@5098
   505
    event.type = SDL_USEREVENT;
slouken@5098
   506
    event.user.windowID = 0;
slouken@5098
   507
    event.user.code = 0;
slouken@5098
   508
    event.user.data1 = data;
slouken@5098
   509
    event.user.data2 = NULL;
slouken@5098
   510
slouken@5098
   511
    if (data->lock_free) {
slouken@5098
   512
        for (i = 0; i < EVENTS_PER_WRITER; ++i) {
slouken@5098
   513
            event.user.code = i;
slouken@5098
   514
            while (!EnqueueEvent_LockFree(queue, &event)) {
slouken@5098
   515
                ++data->waits;
slouken@5098
   516
                SDL_Delay(0);
slouken@5098
   517
            }
slouken@5098
   518
        }
slouken@5098
   519
    } else {
slouken@5098
   520
        for (i = 0; i < EVENTS_PER_WRITER; ++i) {
slouken@5098
   521
            event.user.code = i;
slouken@5098
   522
            while (!EnqueueEvent_Mutex(queue, &event)) {
slouken@5098
   523
                ++data->waits;
slouken@5098
   524
                SDL_Delay(0);
slouken@5098
   525
            }
slouken@5098
   526
        }
slouken@5098
   527
    }
slouken@5098
   528
    SDL_AtomicAdd(&writersRunning, -1);
slouken@5098
   529
    SDL_SemPost(writersDone);
slouken@5098
   530
    return 0;
slouken@5098
   531
}
slouken@5098
   532
slouken@11272
   533
static int SDLCALL FIFO_Reader(void* _data)
slouken@5098
   534
{
slouken@5098
   535
    ReaderData *data = (ReaderData *)_data;
slouken@5098
   536
    SDL_EventQueue *queue = data->queue;
slouken@5098
   537
    SDL_Event event;
slouken@5098
   538
slouken@5098
   539
    if (data->lock_free) {
slouken@5098
   540
        for ( ; ; ) {
slouken@5098
   541
            if (DequeueEvent_LockFree(queue, &event)) {
slouken@5098
   542
                WriterData *writer = (WriterData*)event.user.data1;
slouken@5098
   543
                ++data->counters[writer->index];
icculus@10003
   544
            } else if (SDL_AtomicGet(&queue->active)) {
slouken@5098
   545
                ++data->waits;
slouken@5098
   546
                SDL_Delay(0);
slouken@5098
   547
            } else {
slouken@5098
   548
                /* We drained the queue, we're done! */
slouken@5098
   549
                break;
slouken@5098
   550
            }
slouken@5098
   551
        }
slouken@5098
   552
    } else {
slouken@5098
   553
        for ( ; ; ) {
slouken@5098
   554
            if (DequeueEvent_Mutex(queue, &event)) {
slouken@5098
   555
                WriterData *writer = (WriterData*)event.user.data1;
slouken@5098
   556
                ++data->counters[writer->index];
icculus@10003
   557
            } else if (SDL_AtomicGet(&queue->active)) {
slouken@5098
   558
                ++data->waits;
slouken@5098
   559
                SDL_Delay(0);
slouken@5098
   560
            } else {
slouken@5098
   561
                /* We drained the queue, we're done! */
slouken@5098
   562
                break;
slouken@5098
   563
            }
slouken@5098
   564
        }
slouken@5098
   565
    }
slouken@5098
   566
    SDL_AtomicAdd(&readersRunning, -1);
slouken@5098
   567
    SDL_SemPost(readersDone);
slouken@5098
   568
    return 0;
slouken@5098
   569
}
slouken@5098
   570
slouken@5099
   571
#ifdef TEST_SPINLOCK_FIFO
slouken@5099
   572
/* This thread periodically locks the queue for no particular reason */
slouken@11272
   573
static int SDLCALL FIFO_Watcher(void* _data)
slouken@5099
   574
{
slouken@5099
   575
    SDL_EventQueue *queue = (SDL_EventQueue *)_data;
slouken@5099
   576
icculus@10003
   577
    while (SDL_AtomicGet(&queue->active)) {
slouken@5099
   578
        SDL_AtomicLock(&queue->lock);
slouken@5099
   579
        SDL_AtomicIncRef(&queue->watcher);
slouken@5099
   580
        while (SDL_AtomicGet(&queue->rwcount) > 0) {
slouken@5099
   581
            SDL_Delay(0);
slouken@5099
   582
        }
slouken@5099
   583
        /* Do queue manipulation here... */
slouken@5099
   584
        SDL_AtomicDecRef(&queue->watcher);
slouken@5099
   585
        SDL_AtomicUnlock(&queue->lock);
slouken@5099
   586
slouken@5099
   587
        /* Wait a bit... */
slouken@5099
   588
        SDL_Delay(1);
slouken@5099
   589
    }
slouken@5099
   590
    return 0;
slouken@5099
   591
}
slouken@5099
   592
#endif /* TEST_SPINLOCK_FIFO */
slouken@5099
   593
slouken@5098
   594
static void RunFIFOTest(SDL_bool lock_free)
slouken@5098
   595
{
slouken@5098
   596
    SDL_EventQueue queue;
slouken@5098
   597
    WriterData writerData[NUM_WRITERS];
slouken@5098
   598
    ReaderData readerData[NUM_READERS];
slouken@5098
   599
    Uint32 start, end;
slouken@5098
   600
    int i, j;
slouken@5098
   601
    int grand_total;
philipp@9922
   602
    char textBuffer[1024];
icculus@11416
   603
    size_t len;
slouken@7191
   604
aschiffler@7639
   605
    SDL_Log("\nFIFO test---------------------------------------\n\n");
aschiffler@7639
   606
    SDL_Log("Mode: %s\n", lock_free ? "LockFree" : "Mutex");
slouken@5098
   607
slouken@5098
   608
    readersDone = SDL_CreateSemaphore(0);
slouken@5098
   609
    writersDone = SDL_CreateSemaphore(0);
slouken@5098
   610
slouken@5098
   611
    SDL_memset(&queue, 0xff, sizeof(queue));
slouken@5098
   612
slouken@5098
   613
    InitEventQueue(&queue);
slouken@5098
   614
    if (!lock_free) {
slouken@5098
   615
        queue.mutex = SDL_CreateMutex();
slouken@5098
   616
    }
slouken@5098
   617
slouken@5098
   618
    start = SDL_GetTicks();
slouken@7191
   619
slouken@5099
   620
#ifdef TEST_SPINLOCK_FIFO
slouken@5099
   621
    /* Start a monitoring thread */
slouken@5099
   622
    if (lock_free) {
icculus@5969
   623
        SDL_CreateThread(FIFO_Watcher, "FIFOWatcher", &queue);
slouken@5099
   624
    }
slouken@5099
   625
#endif
slouken@5099
   626
slouken@5098
   627
    /* Start the readers first */
aschiffler@7639
   628
    SDL_Log("Starting %d readers\n", NUM_READERS);
slouken@5098
   629
    SDL_zero(readerData);
slouken@5098
   630
    SDL_AtomicSet(&readersRunning, NUM_READERS);
slouken@5098
   631
    for (i = 0; i < NUM_READERS; ++i) {
icculus@5969
   632
        char name[64];
icculus@5969
   633
        SDL_snprintf(name, sizeof (name), "FIFOReader%d", i);
slouken@5098
   634
        readerData[i].queue = &queue;
slouken@5098
   635
        readerData[i].lock_free = lock_free;
icculus@5969
   636
        SDL_CreateThread(FIFO_Reader, name, &readerData[i]);
slouken@5098
   637
    }
slouken@5098
   638
slouken@5098
   639
    /* Start up the writers */
aschiffler@7639
   640
    SDL_Log("Starting %d writers\n", NUM_WRITERS);
slouken@5098
   641
    SDL_zero(writerData);
slouken@5098
   642
    SDL_AtomicSet(&writersRunning, NUM_WRITERS);
slouken@5098
   643
    for (i = 0; i < NUM_WRITERS; ++i) {
icculus@5969
   644
        char name[64];
icculus@5969
   645
        SDL_snprintf(name, sizeof (name), "FIFOWriter%d", i);
slouken@5098
   646
        writerData[i].queue = &queue;
slouken@5098
   647
        writerData[i].index = i;
slouken@5098
   648
        writerData[i].lock_free = lock_free;
icculus@5969
   649
        SDL_CreateThread(FIFO_Writer, name, &writerData[i]);
slouken@5098
   650
    }
slouken@7191
   651
slouken@5098
   652
    /* Wait for the writers */
slouken@5098
   653
    while (SDL_AtomicGet(&writersRunning) > 0) {
slouken@5098
   654
        SDL_SemWait(writersDone);
slouken@5098
   655
    }
slouken@7191
   656
slouken@5098
   657
    /* Shut down the queue so readers exit */
icculus@10003
   658
    SDL_AtomicSet(&queue.active, 0);
slouken@5098
   659
slouken@5098
   660
    /* Wait for the readers */
slouken@5098
   661
    while (SDL_AtomicGet(&readersRunning) > 0) {
slouken@5098
   662
        SDL_SemWait(readersDone);
slouken@5098
   663
    }
slouken@5098
   664
slouken@5098
   665
    end = SDL_GetTicks();
slouken@7191
   666
slouken@5098
   667
    SDL_DestroySemaphore(readersDone);
slouken@5098
   668
    SDL_DestroySemaphore(writersDone);
slouken@5098
   669
slouken@5098
   670
    if (!lock_free) {
slouken@5098
   671
        SDL_DestroyMutex(queue.mutex);
slouken@5098
   672
    }
slouken@7191
   673
aschiffler@7639
   674
    SDL_Log("Finished in %f sec\n", (end - start) / 1000.f);
slouken@5098
   675
aschiffler@7639
   676
    SDL_Log("\n");
slouken@5098
   677
    for (i = 0; i < NUM_WRITERS; ++i) {
aschiffler@7639
   678
        SDL_Log("Writer %d wrote %d events, had %d waits\n", i, EVENTS_PER_WRITER, writerData[i].waits);
slouken@5098
   679
    }
aschiffler@7639
   680
    SDL_Log("Writers wrote %d total events\n", NUM_WRITERS*EVENTS_PER_WRITER);
slouken@5098
   681
slouken@5098
   682
    /* Print a breakdown of which readers read messages from which writer */
aschiffler@7639
   683
    SDL_Log("\n");
slouken@5098
   684
    grand_total = 0;
slouken@5098
   685
    for (i = 0; i < NUM_READERS; ++i) {
slouken@5098
   686
        int total = 0;
slouken@5098
   687
        for (j = 0; j < NUM_WRITERS; ++j) {
slouken@5098
   688
            total += readerData[i].counters[j];
slouken@5098
   689
        }
slouken@5098
   690
        grand_total += total;
aschiffler@7639
   691
        SDL_Log("Reader %d read %d events, had %d waits\n", i, total, readerData[i].waits);
philipp@9922
   692
        SDL_snprintf(textBuffer, sizeof(textBuffer), "  { ");
slouken@5098
   693
        for (j = 0; j < NUM_WRITERS; ++j) {
slouken@5098
   694
            if (j > 0) {
philipp@9922
   695
                len = SDL_strlen(textBuffer);
aschiffler@7639
   696
                SDL_snprintf(textBuffer + len, sizeof(textBuffer) - len, ", ");
slouken@5098
   697
            }
aschiffler@7639
   698
            len = SDL_strlen(textBuffer);
aschiffler@7639
   699
            SDL_snprintf(textBuffer + len, sizeof(textBuffer) - len, "%d", readerData[i].counters[j]);
slouken@5098
   700
        }
aschiffler@7639
   701
        len = SDL_strlen(textBuffer);
aschiffler@7639
   702
        SDL_snprintf(textBuffer + len, sizeof(textBuffer) - len, " }\n");
philipp@9922
   703
        SDL_Log("%s", textBuffer);
slouken@5098
   704
    }
aschiffler@7639
   705
    SDL_Log("Readers read %d total events\n", grand_total);
slouken@5098
   706
}
slouken@5098
   707
slouken@5098
   708
/* End FIFO test */
slouken@5098
   709
/**************************************************************************/
slouken@5098
   710
slouken@5004
   711
int
slouken@5004
   712
main(int argc, char *argv[])
slouken@5004
   713
{
philipp@9922
   714
    /* Enable standard application logging */
philipp@9922
   715
    SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
aschiffler@7639
   716
slouken@5004
   717
    RunBasicTest();
slouken@5004
   718
    RunEpicTest();
slouken@5098
   719
/* This test is really slow, so don't run it by default */
slouken@5098
   720
#if 0
slouken@5098
   721
    RunFIFOTest(SDL_FALSE);
slouken@5098
   722
#endif
slouken@5098
   723
    RunFIFOTest(SDL_TRUE);
slouken@5004
   724
    return 0;
slouken@5004
   725
}
slouken@5098
   726
slouken@5098
   727
/* vi: set ts=4 sw=4 expandtab: */