mirror of
				https://github.com/thunderbrewhq/thunderbrew
				synced 2025-11-04 10:16:02 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			765 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			765 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
  Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
 | 
						|
 | 
						|
  This software is provided 'as-is', without any express or implied
 | 
						|
  warranty.  In no event will the authors be held liable for any damages
 | 
						|
  arising from the use of this software.
 | 
						|
 | 
						|
  Permission is granted to anyone to use this software for any purpose,
 | 
						|
  including commercial applications, and to alter it and redistribute it
 | 
						|
  freely.
 | 
						|
*/
 | 
						|
 | 
						|
#include <SDL3/SDL.h>
 | 
						|
#include <SDL3/SDL_main.h>
 | 
						|
#include <SDL3/SDL_test.h>
 | 
						|
 | 
						|
/*
 | 
						|
  Absolutely basic tests just to see if we get the expected value
 | 
						|
  after calling each function.
 | 
						|
*/
 | 
						|
 | 
						|
static const char *
 | 
						|
tf(bool _tf)
 | 
						|
{
 | 
						|
    static const char *t = "TRUE";
 | 
						|
    static const char *f = "FALSE";
 | 
						|
 | 
						|
    if (_tf) {
 | 
						|
        return t;
 | 
						|
    }
 | 
						|
 | 
						|
    return f;
 | 
						|
}
 | 
						|
 | 
						|
static void RunBasicTest(void)
 | 
						|
{
 | 
						|
    int value;
 | 
						|
    SDL_SpinLock lock = 0;
 | 
						|
 | 
						|
    SDL_AtomicInt v;
 | 
						|
    bool tfret = false;
 | 
						|
 | 
						|
    SDL_Log("%s", "");
 | 
						|
    SDL_Log("spin lock---------------------------------------");
 | 
						|
    SDL_Log("%s", "");
 | 
						|
 | 
						|
    SDL_LockSpinlock(&lock);
 | 
						|
    SDL_Log("AtomicLock                   lock=%d", lock);
 | 
						|
    SDL_UnlockSpinlock(&lock);
 | 
						|
    SDL_Log("AtomicUnlock                 lock=%d", lock);
 | 
						|
 | 
						|
    SDL_Log("%s", "");
 | 
						|
    SDL_Log("atomic -----------------------------------------");
 | 
						|
    SDL_Log("%s", "");
 | 
						|
 | 
						|
    SDL_SetAtomicInt(&v, 0);
 | 
						|
    tfret = SDL_SetAtomicInt(&v, 10) == 0;
 | 
						|
    SDL_Log("AtomicSet(10)        tfret=%s val=%d", tf(tfret), SDL_GetAtomicInt(&v));
 | 
						|
    tfret = SDL_AddAtomicInt(&v, 10) == 10;
 | 
						|
    SDL_Log("AtomicAdd(10)        tfret=%s val=%d", tf(tfret), SDL_GetAtomicInt(&v));
 | 
						|
 | 
						|
    SDL_SetAtomicInt(&v, 0);
 | 
						|
    SDL_AtomicIncRef(&v);
 | 
						|
    tfret = (SDL_GetAtomicInt(&v) == 1);
 | 
						|
    SDL_Log("AtomicIncRef()       tfret=%s val=%d", tf(tfret), SDL_GetAtomicInt(&v));
 | 
						|
    SDL_AtomicIncRef(&v);
 | 
						|
    tfret = (SDL_GetAtomicInt(&v) == 2);
 | 
						|
    SDL_Log("AtomicIncRef()       tfret=%s val=%d", tf(tfret), SDL_GetAtomicInt(&v));
 | 
						|
    tfret = (SDL_AtomicDecRef(&v) == false);
 | 
						|
    SDL_Log("AtomicDecRef()       tfret=%s val=%d", tf(tfret), SDL_GetAtomicInt(&v));
 | 
						|
    tfret = (SDL_AtomicDecRef(&v) == true);
 | 
						|
    SDL_Log("AtomicDecRef()       tfret=%s val=%d", tf(tfret), SDL_GetAtomicInt(&v));
 | 
						|
 | 
						|
    SDL_SetAtomicInt(&v, 10);
 | 
						|
    tfret = (SDL_CompareAndSwapAtomicInt(&v, 0, 20) == false);
 | 
						|
    SDL_Log("AtomicCAS()          tfret=%s val=%d", tf(tfret), SDL_GetAtomicInt(&v));
 | 
						|
    value = SDL_GetAtomicInt(&v);
 | 
						|
    tfret = (SDL_CompareAndSwapAtomicInt(&v, value, 20) == true);
 | 
						|
    SDL_Log("AtomicCAS()          tfret=%s val=%d", tf(tfret), SDL_GetAtomicInt(&v));
 | 
						|
}
 | 
						|
 | 
						|
/**************************************************************************/
 | 
						|
/* Atomic operation test
 | 
						|
 * Adapted with permission from code by Michael Davidsaver at:
 | 
						|
 *  http://bazaar.launchpad.net/~mdavidsaver/epics-base/atomic/revision/12105#src/libCom/test/epicsAtomicTest.c
 | 
						|
 * Original copyright 2010 Brookhaven Science Associates as operator of Brookhaven National Lab
 | 
						|
 * http://www.aps.anl.gov/epics/license/open.php
 | 
						|
 */
 | 
						|
 | 
						|
/* Tests semantics of atomic operations.  Also a stress test
 | 
						|
 * to see if they are really atomic.
 | 
						|
 *
 | 
						|
 * Several threads adding to the same variable.
 | 
						|
 * at the end the value is compared with the expected
 | 
						|
 * and with a non-atomic counter.
 | 
						|
 */
 | 
						|
 | 
						|
/* Number of concurrent incrementers */
 | 
						|
#define NThreads 2
 | 
						|
#define CountInc 100
 | 
						|
#define VALBITS  (sizeof(atomicValue) * 8)
 | 
						|
 | 
						|
#define atomicValue int
 | 
						|
#define CountTo     ((atomicValue)((unsigned int)(1 << (VALBITS - 1)) - 1))
 | 
						|
#define NInter      (CountTo / CountInc / NThreads)
 | 
						|
#define Expect      (CountTo - NInter * CountInc * NThreads)
 | 
						|
 | 
						|
enum
 | 
						|
{
 | 
						|
    CountTo_GreaterThanZero = CountTo > 0,
 | 
						|
};
 | 
						|
SDL_COMPILE_TIME_ASSERT(size, CountTo_GreaterThanZero); /* check for rollover */
 | 
						|
 | 
						|
static SDL_AtomicInt good = { 42 };
 | 
						|
static atomicValue bad = 42;
 | 
						|
static SDL_AtomicInt threadsRunning;
 | 
						|
static SDL_Semaphore *threadDone;
 | 
						|
 | 
						|
static int SDLCALL adder(void *junk)
 | 
						|
{
 | 
						|
    unsigned long N = NInter;
 | 
						|
    SDL_Log("Thread subtracting %d %lu times", CountInc, N);
 | 
						|
    while (N--) {
 | 
						|
        SDL_AddAtomicInt(&good, -CountInc);
 | 
						|
        bad -= CountInc;
 | 
						|
    }
 | 
						|
    SDL_AddAtomicInt(&threadsRunning, -1);
 | 
						|
    SDL_SignalSemaphore(threadDone);
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void runAdder(void)
 | 
						|
{
 | 
						|
    Uint64 start, end;
 | 
						|
    int i;
 | 
						|
    SDL_Thread *threads[NThreads];
 | 
						|
 | 
						|
    start = SDL_GetTicksNS();
 | 
						|
 | 
						|
    threadDone = SDL_CreateSemaphore(0);
 | 
						|
 | 
						|
    SDL_SetAtomicInt(&threadsRunning, NThreads);
 | 
						|
 | 
						|
    for (i = 0; i < NThreads; i++) {
 | 
						|
        threads[i] = SDL_CreateThread(adder, "Adder", NULL);
 | 
						|
    }
 | 
						|
 | 
						|
    while (SDL_GetAtomicInt(&threadsRunning) > 0) {
 | 
						|
        SDL_WaitSemaphore(threadDone);
 | 
						|
    }
 | 
						|
 | 
						|
    for (i = 0; i < NThreads; i++) {
 | 
						|
        SDL_WaitThread(threads[i], NULL);
 | 
						|
    }
 | 
						|
 | 
						|
    SDL_DestroySemaphore(threadDone);
 | 
						|
 | 
						|
    end = SDL_GetTicksNS();
 | 
						|
 | 
						|
    SDL_Log("Finished in %f sec", (end - start) / 1000000000.0);
 | 
						|
}
 | 
						|
 | 
						|
static void RunEpicTest(void)
 | 
						|
{
 | 
						|
    int b;
 | 
						|
    atomicValue v;
 | 
						|
 | 
						|
    SDL_Log("%s", "");
 | 
						|
    SDL_Log("epic test---------------------------------------");
 | 
						|
    SDL_Log("%s", "");
 | 
						|
 | 
						|
    SDL_Log("Size asserted to be >= 32-bit");
 | 
						|
    SDL_assert(sizeof(atomicValue) >= 4);
 | 
						|
 | 
						|
    SDL_Log("Check static initializer");
 | 
						|
    v = SDL_GetAtomicInt(&good);
 | 
						|
    SDL_assert(v == 42);
 | 
						|
 | 
						|
    SDL_assert(bad == 42);
 | 
						|
 | 
						|
    SDL_Log("Test negative values");
 | 
						|
    SDL_SetAtomicInt(&good, -5);
 | 
						|
    v = SDL_GetAtomicInt(&good);
 | 
						|
    SDL_assert(v == -5);
 | 
						|
 | 
						|
    SDL_Log("Verify maximum value");
 | 
						|
    SDL_SetAtomicInt(&good, CountTo);
 | 
						|
    v = SDL_GetAtomicInt(&good);
 | 
						|
    SDL_assert(v == CountTo);
 | 
						|
 | 
						|
    SDL_Log("Test compare and exchange");
 | 
						|
 | 
						|
    b = SDL_CompareAndSwapAtomicInt(&good, 500, 43);
 | 
						|
    SDL_assert(!b); /* no swap since CountTo!=500 */
 | 
						|
    v = SDL_GetAtomicInt(&good);
 | 
						|
    SDL_assert(v == CountTo); /* ensure no swap */
 | 
						|
 | 
						|
    b = SDL_CompareAndSwapAtomicInt(&good, CountTo, 44);
 | 
						|
    SDL_assert(!!b); /* will swap */
 | 
						|
    v = SDL_GetAtomicInt(&good);
 | 
						|
    SDL_assert(v == 44);
 | 
						|
 | 
						|
    SDL_Log("Test Add");
 | 
						|
 | 
						|
    v = SDL_AddAtomicInt(&good, 1);
 | 
						|
    SDL_assert(v == 44);
 | 
						|
    v = SDL_GetAtomicInt(&good);
 | 
						|
    SDL_assert(v == 45);
 | 
						|
 | 
						|
    v = SDL_AddAtomicInt(&good, 10);
 | 
						|
    SDL_assert(v == 45);
 | 
						|
    v = SDL_GetAtomicInt(&good);
 | 
						|
    SDL_assert(v == 55);
 | 
						|
 | 
						|
    SDL_Log("Test Add (Negative values)");
 | 
						|
 | 
						|
    v = SDL_AddAtomicInt(&good, -20);
 | 
						|
    SDL_assert(v == 55);
 | 
						|
    v = SDL_GetAtomicInt(&good);
 | 
						|
    SDL_assert(v == 35);
 | 
						|
 | 
						|
    v = SDL_AddAtomicInt(&good, -50); /* crossing zero down */
 | 
						|
    SDL_assert(v == 35);
 | 
						|
    v = SDL_GetAtomicInt(&good);
 | 
						|
    SDL_assert(v == -15);
 | 
						|
 | 
						|
    v = SDL_AddAtomicInt(&good, 30); /* crossing zero up */
 | 
						|
    SDL_assert(v == -15);
 | 
						|
    v = SDL_GetAtomicInt(&good);
 | 
						|
    SDL_assert(v == 15);
 | 
						|
 | 
						|
    SDL_Log("Reset before count down test");
 | 
						|
    SDL_SetAtomicInt(&good, CountTo);
 | 
						|
    v = SDL_GetAtomicInt(&good);
 | 
						|
    SDL_assert(v == CountTo);
 | 
						|
 | 
						|
    bad = CountTo;
 | 
						|
    SDL_assert(bad == CountTo);
 | 
						|
 | 
						|
    SDL_Log("Counting down from %d, Expect %d remaining", CountTo, Expect);
 | 
						|
    runAdder();
 | 
						|
 | 
						|
    v = SDL_GetAtomicInt(&good);
 | 
						|
    SDL_Log("Atomic %d Non-Atomic %d", v, bad);
 | 
						|
    SDL_assert(v == Expect);
 | 
						|
    /* We can't guarantee that bad != Expect, this would happen on a single core system, for example. */
 | 
						|
    /*SDL_assert(bad != Expect);*/
 | 
						|
}
 | 
						|
 | 
						|
/* End atomic operation test */
 | 
						|
/**************************************************************************/
 | 
						|
 | 
						|
/**************************************************************************/
 | 
						|
/* Lock-free FIFO test */
 | 
						|
 | 
						|
/* This is useful to test the impact of another thread locking the queue
 | 
						|
   entirely for heavy-weight manipulation.
 | 
						|
 */
 | 
						|
#define TEST_SPINLOCK_FIFO
 | 
						|
 | 
						|
#define NUM_READERS       4
 | 
						|
#define NUM_WRITERS       4
 | 
						|
#define EVENTS_PER_WRITER 1000000
 | 
						|
 | 
						|
/* The number of entries must be a power of 2 */
 | 
						|
#define MAX_ENTRIES 256
 | 
						|
#define WRAP_MASK   (MAX_ENTRIES - 1)
 | 
						|
 | 
						|
typedef struct
 | 
						|
{
 | 
						|
    SDL_AtomicInt sequence;
 | 
						|
    SDL_Event event;
 | 
						|
} SDL_EventQueueEntry;
 | 
						|
 | 
						|
typedef struct
 | 
						|
{
 | 
						|
    SDL_EventQueueEntry entries[MAX_ENTRIES];
 | 
						|
 | 
						|
    char cache_pad1[SDL_CACHELINE_SIZE - ((sizeof(SDL_EventQueueEntry) * MAX_ENTRIES) % SDL_CACHELINE_SIZE)];
 | 
						|
 | 
						|
    SDL_AtomicInt enqueue_pos;
 | 
						|
 | 
						|
    char cache_pad2[SDL_CACHELINE_SIZE - sizeof(SDL_AtomicInt)];
 | 
						|
 | 
						|
    SDL_AtomicInt dequeue_pos;
 | 
						|
 | 
						|
    char cache_pad3[SDL_CACHELINE_SIZE - sizeof(SDL_AtomicInt)];
 | 
						|
 | 
						|
#ifdef TEST_SPINLOCK_FIFO
 | 
						|
    SDL_SpinLock lock;
 | 
						|
    SDL_AtomicInt rwcount;
 | 
						|
    SDL_AtomicInt watcher;
 | 
						|
 | 
						|
    char cache_pad4[SDL_CACHELINE_SIZE - sizeof(SDL_SpinLock) - 2 * sizeof(SDL_AtomicInt)];
 | 
						|
#endif
 | 
						|
 | 
						|
    SDL_AtomicInt active;
 | 
						|
 | 
						|
    /* Only needed for the mutex test */
 | 
						|
    SDL_Mutex *mutex;
 | 
						|
 | 
						|
} SDL_EventQueue;
 | 
						|
 | 
						|
static void InitEventQueue(SDL_EventQueue *queue)
 | 
						|
{
 | 
						|
    int i;
 | 
						|
 | 
						|
    for (i = 0; i < MAX_ENTRIES; ++i) {
 | 
						|
        SDL_SetAtomicInt(&queue->entries[i].sequence, i);
 | 
						|
    }
 | 
						|
    SDL_SetAtomicInt(&queue->enqueue_pos, 0);
 | 
						|
    SDL_SetAtomicInt(&queue->dequeue_pos, 0);
 | 
						|
#ifdef TEST_SPINLOCK_FIFO
 | 
						|
    queue->lock = 0;
 | 
						|
    SDL_SetAtomicInt(&queue->rwcount, 0);
 | 
						|
    SDL_SetAtomicInt(&queue->watcher, 0);
 | 
						|
#endif
 | 
						|
    SDL_SetAtomicInt(&queue->active, 1);
 | 
						|
}
 | 
						|
 | 
						|
static bool EnqueueEvent_LockFree(SDL_EventQueue *queue, const SDL_Event *event)
 | 
						|
{
 | 
						|
    SDL_EventQueueEntry *entry;
 | 
						|
    unsigned queue_pos;
 | 
						|
    unsigned entry_seq;
 | 
						|
    int delta;
 | 
						|
    bool status;
 | 
						|
 | 
						|
#ifdef TEST_SPINLOCK_FIFO
 | 
						|
    /* This is a gate so an external thread can lock the queue */
 | 
						|
    SDL_LockSpinlock(&queue->lock);
 | 
						|
    SDL_assert(SDL_GetAtomicInt(&queue->watcher) == 0);
 | 
						|
    SDL_AtomicIncRef(&queue->rwcount);
 | 
						|
    SDL_UnlockSpinlock(&queue->lock);
 | 
						|
#endif
 | 
						|
 | 
						|
    queue_pos = (unsigned)SDL_GetAtomicInt(&queue->enqueue_pos);
 | 
						|
    for (;;) {
 | 
						|
        entry = &queue->entries[queue_pos & WRAP_MASK];
 | 
						|
        entry_seq = (unsigned)SDL_GetAtomicInt(&entry->sequence);
 | 
						|
 | 
						|
        delta = (int)(entry_seq - queue_pos);
 | 
						|
        if (delta == 0) {
 | 
						|
            /* The entry and the queue position match, try to increment the queue position */
 | 
						|
            if (SDL_CompareAndSwapAtomicInt(&queue->enqueue_pos, (int)queue_pos, (int)(queue_pos + 1))) {
 | 
						|
                /* We own the object, fill it! */
 | 
						|
                entry->event = *event;
 | 
						|
                SDL_SetAtomicInt(&entry->sequence, (int)(queue_pos + 1));
 | 
						|
                status = true;
 | 
						|
                break;
 | 
						|
            }
 | 
						|
        } else if (delta < 0) {
 | 
						|
            /* We ran into an old queue entry, which means it still needs to be dequeued */
 | 
						|
            status = false;
 | 
						|
            break;
 | 
						|
        } else {
 | 
						|
            /* We ran into a new queue entry, get the new queue position */
 | 
						|
            queue_pos = (unsigned)SDL_GetAtomicInt(&queue->enqueue_pos);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
#ifdef TEST_SPINLOCK_FIFO
 | 
						|
    (void)SDL_AtomicDecRef(&queue->rwcount);
 | 
						|
#endif
 | 
						|
    return status;
 | 
						|
}
 | 
						|
 | 
						|
static bool DequeueEvent_LockFree(SDL_EventQueue *queue, SDL_Event *event)
 | 
						|
{
 | 
						|
    SDL_EventQueueEntry *entry;
 | 
						|
    unsigned queue_pos;
 | 
						|
    unsigned entry_seq;
 | 
						|
    int delta;
 | 
						|
    bool status;
 | 
						|
 | 
						|
#ifdef TEST_SPINLOCK_FIFO
 | 
						|
    /* This is a gate so an external thread can lock the queue */
 | 
						|
    SDL_LockSpinlock(&queue->lock);
 | 
						|
    SDL_assert(SDL_GetAtomicInt(&queue->watcher) == 0);
 | 
						|
    SDL_AtomicIncRef(&queue->rwcount);
 | 
						|
    SDL_UnlockSpinlock(&queue->lock);
 | 
						|
#endif
 | 
						|
 | 
						|
    queue_pos = (unsigned)SDL_GetAtomicInt(&queue->dequeue_pos);
 | 
						|
    for (;;) {
 | 
						|
        entry = &queue->entries[queue_pos & WRAP_MASK];
 | 
						|
        entry_seq = (unsigned)SDL_GetAtomicInt(&entry->sequence);
 | 
						|
 | 
						|
        delta = (int)(entry_seq - (queue_pos + 1));
 | 
						|
        if (delta == 0) {
 | 
						|
            /* The entry and the queue position match, try to increment the queue position */
 | 
						|
            if (SDL_CompareAndSwapAtomicInt(&queue->dequeue_pos, (int)queue_pos, (int)(queue_pos + 1))) {
 | 
						|
                /* We own the object, fill it! */
 | 
						|
                *event = entry->event;
 | 
						|
                SDL_SetAtomicInt(&entry->sequence, (int)(queue_pos + MAX_ENTRIES));
 | 
						|
                status = true;
 | 
						|
                break;
 | 
						|
            }
 | 
						|
        } else if (delta < 0) {
 | 
						|
            /* We ran into an old queue entry, which means we've hit empty */
 | 
						|
            status = false;
 | 
						|
            break;
 | 
						|
        } else {
 | 
						|
            /* We ran into a new queue entry, get the new queue position */
 | 
						|
            queue_pos = (unsigned)SDL_GetAtomicInt(&queue->dequeue_pos);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
#ifdef TEST_SPINLOCK_FIFO
 | 
						|
    (void)SDL_AtomicDecRef(&queue->rwcount);
 | 
						|
#endif
 | 
						|
    return status;
 | 
						|
}
 | 
						|
 | 
						|
static bool EnqueueEvent_Mutex(SDL_EventQueue *queue, const SDL_Event *event)
 | 
						|
{
 | 
						|
    SDL_EventQueueEntry *entry;
 | 
						|
    unsigned queue_pos;
 | 
						|
    unsigned entry_seq;
 | 
						|
    int delta;
 | 
						|
    bool status = false;
 | 
						|
 | 
						|
    SDL_LockMutex(queue->mutex);
 | 
						|
 | 
						|
    queue_pos = (unsigned)queue->enqueue_pos.value;
 | 
						|
    entry = &queue->entries[queue_pos & WRAP_MASK];
 | 
						|
    entry_seq = (unsigned)entry->sequence.value;
 | 
						|
 | 
						|
    delta = (int)(entry_seq - queue_pos);
 | 
						|
    if (delta == 0) {
 | 
						|
        ++queue->enqueue_pos.value;
 | 
						|
 | 
						|
        /* We own the object, fill it! */
 | 
						|
        entry->event = *event;
 | 
						|
        entry->sequence.value = (int)(queue_pos + 1);
 | 
						|
        status = true;
 | 
						|
    } else if (delta < 0) {
 | 
						|
        /* We ran into an old queue entry, which means it still needs to be dequeued */
 | 
						|
    } else {
 | 
						|
        SDL_Log("ERROR: mutex failed!");
 | 
						|
    }
 | 
						|
 | 
						|
    SDL_UnlockMutex(queue->mutex);
 | 
						|
 | 
						|
    return status;
 | 
						|
}
 | 
						|
 | 
						|
static bool DequeueEvent_Mutex(SDL_EventQueue *queue, SDL_Event *event)
 | 
						|
{
 | 
						|
    SDL_EventQueueEntry *entry;
 | 
						|
    unsigned queue_pos;
 | 
						|
    unsigned entry_seq;
 | 
						|
    int delta;
 | 
						|
    bool status = false;
 | 
						|
 | 
						|
    SDL_LockMutex(queue->mutex);
 | 
						|
 | 
						|
    queue_pos = (unsigned)queue->dequeue_pos.value;
 | 
						|
    entry = &queue->entries[queue_pos & WRAP_MASK];
 | 
						|
    entry_seq = (unsigned)entry->sequence.value;
 | 
						|
 | 
						|
    delta = (int)(entry_seq - (queue_pos + 1));
 | 
						|
    if (delta == 0) {
 | 
						|
        ++queue->dequeue_pos.value;
 | 
						|
 | 
						|
        /* We own the object, fill it! */
 | 
						|
        *event = entry->event;
 | 
						|
        entry->sequence.value = (int)(queue_pos + MAX_ENTRIES);
 | 
						|
        status = true;
 | 
						|
    } else if (delta < 0) {
 | 
						|
        /* We ran into an old queue entry, which means we've hit empty */
 | 
						|
    } else {
 | 
						|
        SDL_Log("ERROR: mutex failed!");
 | 
						|
    }
 | 
						|
 | 
						|
    SDL_UnlockMutex(queue->mutex);
 | 
						|
 | 
						|
    return status;
 | 
						|
}
 | 
						|
 | 
						|
typedef struct
 | 
						|
{
 | 
						|
    SDL_EventQueue *queue;
 | 
						|
    int index;
 | 
						|
    char padding1[SDL_CACHELINE_SIZE - (sizeof(SDL_EventQueue *) + sizeof(int)) % SDL_CACHELINE_SIZE];
 | 
						|
    int waits;
 | 
						|
    bool lock_free;
 | 
						|
    char padding2[SDL_CACHELINE_SIZE - sizeof(int) - sizeof(bool)];
 | 
						|
    SDL_Thread *thread;
 | 
						|
} WriterData;
 | 
						|
 | 
						|
typedef struct
 | 
						|
{
 | 
						|
    SDL_EventQueue *queue;
 | 
						|
    int counters[NUM_WRITERS];
 | 
						|
    int waits;
 | 
						|
    bool lock_free;
 | 
						|
    char padding[SDL_CACHELINE_SIZE - (sizeof(SDL_EventQueue *) + sizeof(int) * NUM_WRITERS + sizeof(int) + sizeof(bool)) % SDL_CACHELINE_SIZE];
 | 
						|
    SDL_Thread *thread;
 | 
						|
} ReaderData;
 | 
						|
 | 
						|
static int SDLCALL FIFO_Writer(void *_data)
 | 
						|
{
 | 
						|
    WriterData *data = (WriterData *)_data;
 | 
						|
    SDL_EventQueue *queue = data->queue;
 | 
						|
    int i;
 | 
						|
    SDL_Event event;
 | 
						|
 | 
						|
    event.type = SDL_EVENT_USER;
 | 
						|
    event.user.windowID = 0;
 | 
						|
    event.user.code = 0;
 | 
						|
    event.user.data1 = data;
 | 
						|
    event.user.data2 = NULL;
 | 
						|
 | 
						|
    if (data->lock_free) {
 | 
						|
        for (i = 0; i < EVENTS_PER_WRITER; ++i) {
 | 
						|
            event.user.code = i;
 | 
						|
            while (!EnqueueEvent_LockFree(queue, &event)) {
 | 
						|
                ++data->waits;
 | 
						|
                SDL_Delay(0);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    } else {
 | 
						|
        for (i = 0; i < EVENTS_PER_WRITER; ++i) {
 | 
						|
            event.user.code = i;
 | 
						|
            while (!EnqueueEvent_Mutex(queue, &event)) {
 | 
						|
                ++data->waits;
 | 
						|
                SDL_Delay(0);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int SDLCALL FIFO_Reader(void *_data)
 | 
						|
{
 | 
						|
    ReaderData *data = (ReaderData *)_data;
 | 
						|
    SDL_EventQueue *queue = data->queue;
 | 
						|
    SDL_Event event;
 | 
						|
 | 
						|
    if (data->lock_free) {
 | 
						|
        for (;;) {
 | 
						|
            if (DequeueEvent_LockFree(queue, &event)) {
 | 
						|
                WriterData *writer = (WriterData *)event.user.data1;
 | 
						|
                ++data->counters[writer->index];
 | 
						|
            } else if (SDL_GetAtomicInt(&queue->active)) {
 | 
						|
                ++data->waits;
 | 
						|
                SDL_Delay(0);
 | 
						|
            } else {
 | 
						|
                /* We drained the queue, we're done! */
 | 
						|
                break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    } else {
 | 
						|
        for (;;) {
 | 
						|
            if (DequeueEvent_Mutex(queue, &event)) {
 | 
						|
                WriterData *writer = (WriterData *)event.user.data1;
 | 
						|
                ++data->counters[writer->index];
 | 
						|
            } else if (SDL_GetAtomicInt(&queue->active)) {
 | 
						|
                ++data->waits;
 | 
						|
                SDL_Delay(0);
 | 
						|
            } else {
 | 
						|
                /* We drained the queue, we're done! */
 | 
						|
                break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
#ifdef TEST_SPINLOCK_FIFO
 | 
						|
/* This thread periodically locks the queue for no particular reason */
 | 
						|
static int SDLCALL FIFO_Watcher(void *_data)
 | 
						|
{
 | 
						|
    SDL_EventQueue *queue = (SDL_EventQueue *)_data;
 | 
						|
 | 
						|
    while (SDL_GetAtomicInt(&queue->active)) {
 | 
						|
        SDL_LockSpinlock(&queue->lock);
 | 
						|
        SDL_AtomicIncRef(&queue->watcher);
 | 
						|
        while (SDL_GetAtomicInt(&queue->rwcount) > 0) {
 | 
						|
            SDL_Delay(0);
 | 
						|
        }
 | 
						|
        /* Do queue manipulation here... */
 | 
						|
        (void)SDL_AtomicDecRef(&queue->watcher);
 | 
						|
        SDL_UnlockSpinlock(&queue->lock);
 | 
						|
 | 
						|
        /* Wait a bit... */
 | 
						|
        SDL_Delay(1);
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
#endif /* TEST_SPINLOCK_FIFO */
 | 
						|
 | 
						|
static void RunFIFOTest(bool lock_free)
 | 
						|
{
 | 
						|
    SDL_EventQueue queue;
 | 
						|
    SDL_Thread *fifo_thread = NULL;
 | 
						|
    WriterData writerData[NUM_WRITERS];
 | 
						|
    ReaderData readerData[NUM_READERS];
 | 
						|
    Uint64 start, end;
 | 
						|
    int i, j;
 | 
						|
    int grand_total;
 | 
						|
    char textBuffer[1024];
 | 
						|
    size_t len;
 | 
						|
 | 
						|
    SDL_Log("%s", "");
 | 
						|
    SDL_Log("FIFO test---------------------------------------");
 | 
						|
    SDL_Log("%s", "");
 | 
						|
    SDL_Log("Mode: %s", lock_free ? "LockFree" : "Mutex");
 | 
						|
 | 
						|
    SDL_memset(&queue, 0xff, sizeof(queue));
 | 
						|
 | 
						|
    InitEventQueue(&queue);
 | 
						|
    if (!lock_free) {
 | 
						|
        queue.mutex = SDL_CreateMutex();
 | 
						|
    }
 | 
						|
 | 
						|
    start = SDL_GetTicksNS();
 | 
						|
 | 
						|
#ifdef TEST_SPINLOCK_FIFO
 | 
						|
    /* Start a monitoring thread */
 | 
						|
    if (lock_free) {
 | 
						|
        fifo_thread = SDL_CreateThread(FIFO_Watcher, "FIFOWatcher", &queue);
 | 
						|
    }
 | 
						|
#endif
 | 
						|
 | 
						|
    /* Start the readers first */
 | 
						|
    SDL_Log("Starting %d readers", NUM_READERS);
 | 
						|
    SDL_zeroa(readerData);
 | 
						|
    for (i = 0; i < NUM_READERS; ++i) {
 | 
						|
        char name[64];
 | 
						|
        (void)SDL_snprintf(name, sizeof(name), "FIFOReader%d", i);
 | 
						|
        readerData[i].queue = &queue;
 | 
						|
        readerData[i].lock_free = lock_free;
 | 
						|
        readerData[i].thread = SDL_CreateThread(FIFO_Reader, name, &readerData[i]);
 | 
						|
    }
 | 
						|
 | 
						|
    /* Start up the writers */
 | 
						|
    SDL_Log("Starting %d writers", NUM_WRITERS);
 | 
						|
    SDL_zeroa(writerData);
 | 
						|
    for (i = 0; i < NUM_WRITERS; ++i) {
 | 
						|
        char name[64];
 | 
						|
        (void)SDL_snprintf(name, sizeof(name), "FIFOWriter%d", i);
 | 
						|
        writerData[i].queue = &queue;
 | 
						|
        writerData[i].index = i;
 | 
						|
        writerData[i].lock_free = lock_free;
 | 
						|
        writerData[i].thread = SDL_CreateThread(FIFO_Writer, name, &writerData[i]);
 | 
						|
    }
 | 
						|
 | 
						|
    /* Wait for the writers */
 | 
						|
    for (i = 0; i < NUM_WRITERS; ++i) {
 | 
						|
        SDL_WaitThread(writerData[i].thread, NULL);
 | 
						|
    }
 | 
						|
 | 
						|
    /* Shut down the queue so readers exit */
 | 
						|
    SDL_SetAtomicInt(&queue.active, 0);
 | 
						|
 | 
						|
    /* Wait for the readers */
 | 
						|
    for (i = 0; i < NUM_READERS; ++i) {
 | 
						|
        SDL_WaitThread(readerData[i].thread, NULL);
 | 
						|
    }
 | 
						|
 | 
						|
    end = SDL_GetTicksNS();
 | 
						|
 | 
						|
    /* Wait for the FIFO thread */
 | 
						|
    if (fifo_thread) {
 | 
						|
        SDL_WaitThread(fifo_thread, NULL);
 | 
						|
    }
 | 
						|
 | 
						|
    if (!lock_free) {
 | 
						|
        SDL_DestroyMutex(queue.mutex);
 | 
						|
    }
 | 
						|
 | 
						|
    SDL_Log("Finished in %f sec", (end - start) / 1000000000.0);
 | 
						|
 | 
						|
    SDL_Log("%s", "");
 | 
						|
    for (i = 0; i < NUM_WRITERS; ++i) {
 | 
						|
        SDL_Log("Writer %d wrote %d events, had %d waits", i, EVENTS_PER_WRITER, writerData[i].waits);
 | 
						|
    }
 | 
						|
    SDL_Log("Writers wrote %d total events", NUM_WRITERS * EVENTS_PER_WRITER);
 | 
						|
 | 
						|
    /* Print a breakdown of which readers read messages from which writer */
 | 
						|
    SDL_Log("%s", "");
 | 
						|
    grand_total = 0;
 | 
						|
    for (i = 0; i < NUM_READERS; ++i) {
 | 
						|
        int total = 0;
 | 
						|
        for (j = 0; j < NUM_WRITERS; ++j) {
 | 
						|
            total += readerData[i].counters[j];
 | 
						|
        }
 | 
						|
        grand_total += total;
 | 
						|
        SDL_Log("Reader %d read %d events, had %d waits", i, total, readerData[i].waits);
 | 
						|
        (void)SDL_snprintf(textBuffer, sizeof(textBuffer), "  { ");
 | 
						|
        for (j = 0; j < NUM_WRITERS; ++j) {
 | 
						|
            if (j > 0) {
 | 
						|
                len = SDL_strlen(textBuffer);
 | 
						|
                (void)SDL_snprintf(textBuffer + len, sizeof(textBuffer) - len, ", ");
 | 
						|
            }
 | 
						|
            len = SDL_strlen(textBuffer);
 | 
						|
            (void)SDL_snprintf(textBuffer + len, sizeof(textBuffer) - len, "%d", readerData[i].counters[j]);
 | 
						|
        }
 | 
						|
        len = SDL_strlen(textBuffer);
 | 
						|
        (void)SDL_snprintf(textBuffer + len, sizeof(textBuffer) - len, " }\n");
 | 
						|
        SDL_Log("%s", textBuffer);
 | 
						|
    }
 | 
						|
    SDL_Log("Readers read %d total events", grand_total);
 | 
						|
}
 | 
						|
 | 
						|
/* End FIFO test */
 | 
						|
/**************************************************************************/
 | 
						|
 | 
						|
int main(int argc, char *argv[])
 | 
						|
{
 | 
						|
    SDLTest_CommonState *state;
 | 
						|
    int i;
 | 
						|
    bool enable_threads = true;
 | 
						|
 | 
						|
    /* Initialize test framework */
 | 
						|
    state = SDLTest_CommonCreateState(argv, 0);
 | 
						|
    if (!state) {
 | 
						|
        return 1;
 | 
						|
    }
 | 
						|
 | 
						|
    /* Parse commandline */
 | 
						|
    for (i = 1; i < argc;) {
 | 
						|
        int consumed;
 | 
						|
 | 
						|
        consumed = SDLTest_CommonArg(state, i);
 | 
						|
        if (consumed == 0) {
 | 
						|
            consumed = -1;
 | 
						|
            if (SDL_strcasecmp(argv[i], "--no-threads") == 0) {
 | 
						|
                enable_threads = false;
 | 
						|
                consumed = 1;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        if (consumed < 0) {
 | 
						|
            static const char *options[] = {
 | 
						|
                "[--no-threads]",
 | 
						|
                NULL
 | 
						|
            };
 | 
						|
            SDLTest_CommonLogUsage(state, argv[0], options);
 | 
						|
            return 1;
 | 
						|
        }
 | 
						|
        i += consumed;
 | 
						|
    }
 | 
						|
 | 
						|
    RunBasicTest();
 | 
						|
 | 
						|
    if (SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "SDL_TESTS_QUICK") != NULL) {
 | 
						|
        SDL_Log("Not running slower tests");
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    if (enable_threads) {
 | 
						|
        RunEpicTest();
 | 
						|
    }
 | 
						|
/* This test is really slow, so don't run it by default */
 | 
						|
#if 0
 | 
						|
    RunFIFOTest(false);
 | 
						|
#endif
 | 
						|
    RunFIFOTest(true);
 | 
						|
    SDL_Quit();
 | 
						|
    SDLTest_CommonDestroyState(state);
 | 
						|
    return 0;
 | 
						|
}
 |