test/testatomic.c
author Alex Baines <alex@abaines.me.uk>
Tue, 19 Aug 2014 23:31:50 +0100
changeset 9097 56d712662a82
parent 8981 68e0819f0f56
child 9356 e87d6e1e812a
permissions -rw-r--r--
Add a SDL_IM_INTERNAL_EDITING event to make IMs like iBus render editing text in its own UI instead of sending TEXTEDITING events.
This is useful for applications that handle TEXTINPUT events but not TEXTEDITING events.
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"
slouken@5006
    15
#include "SDL_atomic.h"
slouken@5004
    16
#include "SDL_assert.h"
slouken@5116
    17
#include "SDL_cpuinfo.h"
bob@3180
    18
bob@3201
    19
/*
bob@3202
    20
  Absolutely basic tests just to see if we get the expected value
bob@3202
    21
  after calling each function.
bob@3201
    22
*/
bob@3201
    23
slouken@5004
    24
static
slouken@3338
    25
char *
bob@3202
    26
tf(SDL_bool tf)
bob@3202
    27
{
slouken@5003
    28
    static char *t = "TRUE";
slouken@5003
    29
    static char *f = "FALSE";
bob@3202
    30
slouken@5003
    31
    if (tf)
slouken@5003
    32
    {
slouken@5003
    33
       return t;
slouken@5003
    34
    }
bob@3202
    35
slouken@5003
    36
    return f;
bob@3202
    37
}
slouken@3338
    38
slouken@5004
    39
static
slouken@5004
    40
void RunBasicTest()
bob@3180
    41
{
slouken@5003
    42
    int value;
slouken@5003
    43
    SDL_SpinLock lock = 0;
bob@3180
    44
slouken@5013
    45
    SDL_atomic_t v;
slouken@5003
    46
    SDL_bool tfret = SDL_FALSE;
bob@3180
    47
aschiffler@7639
    48
    SDL_Log("\nspin lock---------------------------------------\n\n");
bob@3180
    49
slouken@5003
    50
    SDL_AtomicLock(&lock);
aschiffler@7639
    51
    SDL_Log("AtomicLock                   lock=%d\n", lock);
slouken@5003
    52
    SDL_AtomicUnlock(&lock);
aschiffler@7639
    53
    SDL_Log("AtomicUnlock                 lock=%d\n", lock);
bob@3261
    54
aschiffler@7639
    55
    SDL_Log("\natomic -----------------------------------------\n\n");
slouken@7191
    56
slouken@5003
    57
    SDL_AtomicSet(&v, 0);
aschiffler@7639
    58
    tfret = SDL_AtomicSet(&v, 10) == 0 ? SDL_TRUE : SDL_FALSE;
aschiffler@7639
    59
    SDL_Log("AtomicSet(10)        tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
aschiffler@7639
    60
    tfret = SDL_AtomicAdd(&v, 10) == 10 ? SDL_TRUE : SDL_FALSE;
aschiffler@7639
    61
    SDL_Log("AtomicAdd(10)        tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
slouken@3186
    62
slouken@5003
    63
    SDL_AtomicSet(&v, 0);
slouken@5003
    64
    SDL_AtomicIncRef(&v);
aschiffler@7639
    65
    tfret = (SDL_AtomicGet(&v) == 1) ? SDL_TRUE : SDL_FALSE;
aschiffler@7639
    66
    SDL_Log("AtomicIncRef()       tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
slouken@5003
    67
    SDL_AtomicIncRef(&v);
aschiffler@7639
    68
    tfret = (SDL_AtomicGet(&v) == 2) ? SDL_TRUE : SDL_FALSE;
aschiffler@7639
    69
    SDL_Log("AtomicIncRef()       tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
aschiffler@7639
    70
    tfret = (SDL_AtomicDecRef(&v) == SDL_FALSE) ? SDL_TRUE : SDL_FALSE;
aschiffler@7639
    71
    SDL_Log("AtomicDecRef()       tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
aschiffler@7639
    72
    tfret = (SDL_AtomicDecRef(&v) == SDL_TRUE) ? SDL_TRUE : SDL_FALSE;
aschiffler@7639
    73
    SDL_Log("AtomicDecRef()       tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
bob@3202
    74
slouken@5003
    75
    SDL_AtomicSet(&v, 10);
aschiffler@7639
    76
    tfret = (SDL_AtomicCAS(&v, 0, 20) == SDL_FALSE) ? SDL_TRUE : SDL_FALSE;
aschiffler@7639
    77
    SDL_Log("AtomicCAS()          tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
slouken@5003
    78
    value = SDL_AtomicGet(&v);
aschiffler@7639
    79
    tfret = (SDL_AtomicCAS(&v, value, 20) == SDL_TRUE) ? SDL_TRUE : SDL_FALSE;
aschiffler@7639
    80
    SDL_Log("AtomicCAS()          tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
slouken@5004
    81
}
bob@3202
    82
slouken@5018
    83
/**************************************************************************/
slouken@5018
    84
/* Atomic operation test
slouken@5018
    85
 * Adapted with permission from code by Michael Davidsaver at:
slouken@5018
    86
 *  http://bazaar.launchpad.net/~mdavidsaver/epics-base/atomic/revision/12105#src/libCom/test/epicsAtomicTest.c
slouken@5018
    87
 * Original copyright 2010 Brookhaven Science Associates as operator of Brookhaven National Lab
slouken@5018
    88
 * http://www.aps.anl.gov/epics/license/open.php
slouken@5018
    89
 */
slouken@5004
    90
slouken@5004
    91
/* Tests semantics of atomic operations.  Also a stress test
slouken@5004
    92
 * to see if they are really atomic.
slouken@5004
    93
 *
philipp@7538
    94
 * Several threads adding to the same variable.
slouken@5004
    95
 * at the end the value is compared with the expected
slouken@5004
    96
 * and with a non-atomic counter.
slouken@5004
    97
 */
slouken@7191
    98
slouken@5004
    99
/* Number of concurrent incrementers */
slouken@5004
   100
#define NThreads 2
slouken@5004
   101
#define CountInc 100
slouken@5004
   102
#define VALBITS (sizeof(atomicValue)*8)
slouken@7191
   103
slouken@5004
   104
#define atomicValue int
slouken@5004
   105
#define CountTo ((atomicValue)((unsigned int)(1<<(VALBITS-1))-1))
slouken@5004
   106
#define NInter (CountTo/CountInc/NThreads)
slouken@5004
   107
#define Expect (CountTo-NInter*CountInc*NThreads)
slouken@7191
   108
slouken@5004
   109
SDL_COMPILE_TIME_ASSERT(size, CountTo>0); /* 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@5004
   120
int 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
slouken@5102
   290
    volatile SDL_bool 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
slouken@5098
   311
    queue->active = SDL_TRUE;
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@5098
   498
static int 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@5098
   533
static int 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];
slouken@5098
   544
            } else if (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];
slouken@5098
   557
            } else if (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@5099
   573
static int FIFO_Watcher(void* _data)
slouken@5099
   574
{
slouken@5099
   575
    SDL_EventQueue *queue = (SDL_EventQueue *)_data;
slouken@5099
   576
slouken@5099
   577
    while (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;
aschiffler@7639
   602
	char textBuffer[1024];
aschiffler@7639
   603
	int 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 */
slouken@5098
   658
    queue.active = SDL_FALSE;
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);
aschiffler@7639
   692
		SDL_snprintf(textBuffer, sizeof(textBuffer), "  { ");
slouken@5098
   693
        for (j = 0; j < NUM_WRITERS; ++j) {
slouken@5098
   694
            if (j > 0) {
aschiffler@7639
   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");
slouken@8981
   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
{
aschiffler@7639
   714
	/* Enable standard application logging */
aschiffler@7639
   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: */