SDL  2.0
testatomic.c File Reference
#include <stdio.h>
#include "SDL.h"
+ Include dependency graph for testatomic.c:

Go to the source code of this file.

Data Structures

struct  SDL_EventQueueEntry
struct  SDL_EventQueue
struct  WriterData
struct  ReaderData

Macros

#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)
#define TEST_SPINLOCK_FIFO
#define NUM_READERS   4
#define NUM_WRITERS   4
#define EVENTS_PER_WRITER   1000000
#define MAX_ENTRIES   256
#define WRAP_MASK   (MAX_ENTRIES-1)

Enumerations

enum  

Functions

static char * tf (SDL_bool tf)
static void RunBasicTest ()
 SDL_COMPILE_TIME_ASSERT (size, CountTo_GreaterThanZero)
static int adder (void *junk)
static void runAdder (void)
static void RunEpicTest ()
static void InitEventQueue (SDL_EventQueue *queue)
static SDL_bool EnqueueEvent_LockFree (SDL_EventQueue *queue, const SDL_Event *event)
static SDL_bool DequeueEvent_LockFree (SDL_EventQueue *queue, SDL_Event *event)
static SDL_bool EnqueueEvent_Mutex (SDL_EventQueue *queue, const SDL_Event *event)
static SDL_bool DequeueEvent_Mutex (SDL_EventQueue *queue, SDL_Event *event)
static int FIFO_Writer (void *_data)
static int FIFO_Reader (void *_data)
static int FIFO_Watcher (void *_data)
static void RunFIFOTest (SDL_bool lock_free)
int main (int argc, char *argv[])

Variables

static SDL_atomic_t good = { 42 }
static atomicValue bad = 42
static SDL_atomic_t threadsRunning
static SDL_sem * threadDone
static SDL_sem * writersDone
static SDL_sem * readersDone
static SDL_atomic_t writersRunning
static SDL_atomic_t readersRunning

Macro Definition Documentation

#define atomicValue   int

Definition at line 101 of file testatomic.c.

Referenced by RunEpicTest().

#define CountInc   100

Definition at line 98 of file testatomic.c.

Referenced by adder().

#define CountTo   ((atomicValue)((unsigned int)(1<<(VALBITS-1))-1))

Definition at line 102 of file testatomic.c.

Referenced by RunEpicTest().

#define EVENTS_PER_WRITER   1000000

Definition at line 256 of file testatomic.c.

Referenced by FIFO_Writer(), and RunFIFOTest().

#define Expect   (CountTo-NInter*CountInc*NThreads)

Definition at line 104 of file testatomic.c.

Referenced by RunEpicTest().

#define MAX_ENTRIES   256

Definition at line 259 of file testatomic.c.

Referenced by DequeueEvent_LockFree(), DequeueEvent_Mutex(), and InitEventQueue().

#define NInter   (CountTo/CountInc/NThreads)

Definition at line 103 of file testatomic.c.

Referenced by adder().

#define NThreads   2

Definition at line 97 of file testatomic.c.

Referenced by runAdder().

#define NUM_READERS   4

Definition at line 254 of file testatomic.c.

Referenced by RunFIFOTest().

#define NUM_WRITERS   4

Definition at line 255 of file testatomic.c.

Referenced by RunFIFOTest().

#define TEST_SPINLOCK_FIFO

Definition at line 252 of file testatomic.c.

#define VALBITS   (sizeof(atomicValue)*8)

Definition at line 99 of file testatomic.c.

#define WRAP_MASK   (MAX_ENTRIES-1)

Enumeration Type Documentation

anonymous enum

Definition at line 106 of file testatomic.c.

{
CountTo_GreaterThanZero = CountTo > 0,
};

Function Documentation

static int adder ( void junk)
static

Definition at line 120 of file testatomic.c.

References bad, CountInc, NInter, SDL_AtomicAdd, SDL_Log, SDL_SemPost, and threadDone.

Referenced by runAdder().

{
unsigned long N=NInter;
SDL_Log("Thread subtracting %d %lu times\n",CountInc,N);
while (N--) {
}
return 0;
}
static SDL_bool DequeueEvent_LockFree ( SDL_EventQueue queue,
SDL_Event event 
)
static

Definition at line 361 of file testatomic.c.

References SDL_EventQueue::dequeue_pos, SDL_EventQueue::entries, SDL_EventQueueEntry::event, SDL_EventQueue::lock, MAX_ENTRIES, SDL_EventQueue::rwcount, SDL_assert, SDL_AtomicCAS, SDL_AtomicDecRef, SDL_AtomicGet, SDL_AtomicIncRef, SDL_AtomicLock, SDL_AtomicSet, SDL_AtomicUnlock, SDL_FALSE, SDL_TRUE, SDL_EventQueueEntry::sequence, SDL_EventQueue::watcher, and WRAP_MASK.

Referenced by FIFO_Reader().

{
unsigned queue_pos;
unsigned entry_seq;
int delta;
SDL_bool status;
#ifdef TEST_SPINLOCK_FIFO
/* This is a gate so an external thread can lock the queue */
SDL_AtomicLock(&queue->lock);
#endif
queue_pos = (unsigned)SDL_AtomicGet(&queue->dequeue_pos);
for ( ; ; ) {
entry = &queue->entries[queue_pos & WRAP_MASK];
entry_seq = (unsigned)SDL_AtomicGet(&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_AtomicCAS(&queue->dequeue_pos, (int)queue_pos, (int)(queue_pos+1))) {
/* We own the object, fill it! */
*event = entry->event;
SDL_AtomicSet(&entry->sequence, (int)(queue_pos+MAX_ENTRIES));
status = SDL_TRUE;
break;
}
} else if (delta < 0) {
/* We ran into an old queue entry, which means we've hit empty */
status = SDL_FALSE;
break;
} else {
/* We ran into a new queue entry, get the new queue position */
queue_pos = (unsigned)SDL_AtomicGet(&queue->dequeue_pos);
}
}
#ifdef TEST_SPINLOCK_FIFO
#endif
return status;
}
static SDL_bool DequeueEvent_Mutex ( SDL_EventQueue queue,
SDL_Event event 
)
static

Definition at line 441 of file testatomic.c.

References SDL_EventQueue::dequeue_pos, SDL_EventQueue::entries, SDL_EventQueueEntry::event, MAX_ENTRIES, SDL_EventQueue::mutex, SDL_FALSE, SDL_LockMutex, SDL_Log, SDL_TRUE, SDL_UnlockMutex, SDL_EventQueueEntry::sequence, SDL_atomic_t::value, and WRAP_MASK.

Referenced by FIFO_Reader().

{
unsigned queue_pos;
unsigned entry_seq;
int delta;
SDL_bool status = SDL_FALSE;
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 = SDL_TRUE;
} else if (delta < 0) {
/* We ran into an old queue entry, which means we've hit empty */
} else {
SDL_Log("ERROR: mutex failed!\n");
}
return status;
}
static SDL_bool EnqueueEvent_LockFree ( SDL_EventQueue queue,
const SDL_Event event 
)
static

Definition at line 314 of file testatomic.c.

References SDL_EventQueue::enqueue_pos, SDL_EventQueue::entries, SDL_EventQueueEntry::event, SDL_EventQueue::lock, SDL_EventQueue::rwcount, SDL_assert, SDL_AtomicCAS, SDL_AtomicDecRef, SDL_AtomicGet, SDL_AtomicIncRef, SDL_AtomicLock, SDL_AtomicSet, SDL_AtomicUnlock, SDL_FALSE, SDL_TRUE, SDL_EventQueueEntry::sequence, SDL_EventQueue::watcher, and WRAP_MASK.

Referenced by FIFO_Writer().

{
unsigned queue_pos;
unsigned entry_seq;
int delta;
SDL_bool status;
#ifdef TEST_SPINLOCK_FIFO
/* This is a gate so an external thread can lock the queue */
SDL_AtomicLock(&queue->lock);
#endif
queue_pos = (unsigned)SDL_AtomicGet(&queue->enqueue_pos);
for ( ; ; ) {
entry = &queue->entries[queue_pos & WRAP_MASK];
entry_seq = (unsigned)SDL_AtomicGet(&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_AtomicCAS(&queue->enqueue_pos, (int)queue_pos, (int)(queue_pos+1))) {
/* We own the object, fill it! */
entry->event = *event;
SDL_AtomicSet(&entry->sequence, (int)(queue_pos + 1));
status = SDL_TRUE;
break;
}
} else if (delta < 0) {
/* We ran into an old queue entry, which means it still needs to be dequeued */
status = SDL_FALSE;
break;
} else {
/* We ran into a new queue entry, get the new queue position */
queue_pos = (unsigned)SDL_AtomicGet(&queue->enqueue_pos);
}
}
#ifdef TEST_SPINLOCK_FIFO
#endif
return status;
}
static SDL_bool EnqueueEvent_Mutex ( SDL_EventQueue queue,
const SDL_Event event 
)
static

Definition at line 408 of file testatomic.c.

References SDL_EventQueue::enqueue_pos, SDL_EventQueue::entries, SDL_EventQueueEntry::event, SDL_EventQueue::mutex, SDL_FALSE, SDL_LockMutex, SDL_Log, SDL_TRUE, SDL_UnlockMutex, SDL_EventQueueEntry::sequence, SDL_atomic_t::value, and WRAP_MASK.

Referenced by FIFO_Writer().

{
unsigned queue_pos;
unsigned entry_seq;
int delta;
SDL_bool status = SDL_FALSE;
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 = SDL_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!\n");
}
return status;
}
static int FIFO_Reader ( void _data)
static

Definition at line 533 of file testatomic.c.

References SDL_EventQueue::active, ReaderData::counters, SDL_UserEvent::data1, DequeueEvent_LockFree(), DequeueEvent_Mutex(), WriterData::index, ReaderData::lock_free, ReaderData::queue, readersDone, SDL_AtomicAdd, SDL_AtomicGet, SDL_Delay, SDL_SemPost, SDL_Event::user, and ReaderData::waits.

Referenced by RunFIFOTest().

{
ReaderData *data = (ReaderData *)_data;
SDL_EventQueue *queue = data->queue;
if (data->lock_free) {
for ( ; ; ) {
if (DequeueEvent_LockFree(queue, &event)) {
WriterData *writer = (WriterData*)event.user.data1;
++data->counters[writer->index];
} else if (SDL_AtomicGet(&queue->active)) {
++data->waits;
} 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_AtomicGet(&queue->active)) {
++data->waits;
} else {
/* We drained the queue, we're done! */
break;
}
}
}
return 0;
}
static int FIFO_Watcher ( void _data)
static

Definition at line 573 of file testatomic.c.

References SDL_EventQueue::active, SDL_EventQueue::lock, SDL_EventQueue::rwcount, SDL_AtomicDecRef, SDL_AtomicGet, SDL_AtomicIncRef, SDL_AtomicLock, SDL_AtomicUnlock, SDL_Delay, and SDL_EventQueue::watcher.

Referenced by RunFIFOTest().

{
SDL_EventQueue *queue = (SDL_EventQueue *)_data;
while (SDL_AtomicGet(&queue->active)) {
SDL_AtomicLock(&queue->lock);
while (SDL_AtomicGet(&queue->rwcount) > 0) {
}
/* Do queue manipulation here... */
/* Wait a bit... */
}
return 0;
}
static int FIFO_Writer ( void _data)
static

Definition at line 498 of file testatomic.c.

References EnqueueEvent_LockFree(), EnqueueEvent_Mutex(), EVENTS_PER_WRITER, i, WriterData::lock_free, NULL, WriterData::queue, SDL_AtomicAdd, SDL_Delay, SDL_SemPost, SDL_USEREVENT, WriterData::waits, and writersDone.

Referenced by RunFIFOTest().

{
WriterData *data = (WriterData *)_data;
SDL_EventQueue *queue = data->queue;
int i;
event.type = SDL_USEREVENT;
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;
}
}
} else {
for (i = 0; i < EVENTS_PER_WRITER; ++i) {
event.user.code = i;
while (!EnqueueEvent_Mutex(queue, &event)) {
++data->waits;
}
}
}
return 0;
}
static void InitEventQueue ( SDL_EventQueue queue)
static
int main ( int  argc,
char *  argv[] 
)

Definition at line 712 of file testatomic.c.

References RunBasicTest(), RunEpicTest(), RunFIFOTest(), SDL_FALSE, SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, SDL_LogSetPriority, and SDL_TRUE.

{
/* Enable standard application logging */
/* This test is really slow, so don't run it by default */
#if 0
#endif
return 0;
}
static void runAdder ( void  )
static
static void RunBasicTest ( )
static

Definition at line 37 of file testatomic.c.

References lock, SDL_AtomicAdd, SDL_AtomicCAS, SDL_AtomicDecRef, SDL_AtomicGet, SDL_AtomicIncRef, SDL_AtomicLock, SDL_AtomicSet, SDL_AtomicUnlock, SDL_FALSE, SDL_Log, SDL_TRUE, and tf().

Referenced by main().

{
int value;
SDL_Log("\nspin lock---------------------------------------\n\n");
SDL_Log("AtomicLock lock=%d\n", lock);
SDL_Log("AtomicUnlock lock=%d\n", lock);
SDL_Log("\natomic -----------------------------------------\n\n");
SDL_AtomicSet(&v, 0);
tfret = SDL_AtomicSet(&v, 10) == 0 ? SDL_TRUE : SDL_FALSE;
SDL_Log("AtomicSet(10) tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
tfret = SDL_AtomicAdd(&v, 10) == 10 ? SDL_TRUE : SDL_FALSE;
SDL_Log("AtomicAdd(10) tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
SDL_AtomicSet(&v, 0);
tfret = (SDL_AtomicGet(&v) == 1) ? SDL_TRUE : SDL_FALSE;
SDL_Log("AtomicIncRef() tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
tfret = (SDL_AtomicGet(&v) == 2) ? SDL_TRUE : SDL_FALSE;
SDL_Log("AtomicIncRef() tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
SDL_Log("AtomicDecRef() tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
SDL_Log("AtomicDecRef() tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
SDL_AtomicSet(&v, 10);
tfret = (SDL_AtomicCAS(&v, 0, 20) == SDL_FALSE) ? SDL_TRUE : SDL_FALSE;
SDL_Log("AtomicCAS() tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
value = SDL_AtomicGet(&v);
tfret = (SDL_AtomicCAS(&v, value, 20) == SDL_TRUE) ? SDL_TRUE : SDL_FALSE;
SDL_Log("AtomicCAS() tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
}
static void RunEpicTest ( )
static

Definition at line 159 of file testatomic.c.

References atomicValue, bad, CountTo, Expect, runAdder(), SDL_assert, SDL_AtomicAdd, SDL_AtomicCAS, SDL_AtomicGet, SDL_AtomicSet, and SDL_Log.

Referenced by main().

{
int b;
SDL_Log("\nepic test---------------------------------------\n\n");
SDL_Log("Size asserted to be >= 32-bit\n");
SDL_assert(sizeof(atomicValue)>=4);
SDL_Log("Check static initializer\n");
SDL_assert(v==42);
SDL_Log("Test negative values\n");
SDL_assert(v==-5);
SDL_Log("Verify maximum value\n");
SDL_Log("Test compare and exchange\n");
b=SDL_AtomicCAS(&good, 500, 43);
SDL_assert(!b); /* no swap since CountTo!=500 */
SDL_assert(v==CountTo); /* ensure no swap */
SDL_assert(!!b); /* will swap */
SDL_assert(v==44);
SDL_Log("Test Add\n");
SDL_assert(v==44);
SDL_assert(v==45);
SDL_assert(v==45);
SDL_assert(v==55);
SDL_Log("Test Add (Negative values)\n");
v=SDL_AtomicAdd(&good, -20);
SDL_assert(v==55);
SDL_assert(v==35);
v=SDL_AtomicAdd(&good, -50); /* crossing zero down */
SDL_assert(v==35);
SDL_assert(v==-15);
v=SDL_AtomicAdd(&good, 30); /* crossing zero up */
SDL_assert(v==-15);
SDL_assert(v==15);
SDL_Log("Reset before count down test\n");
SDL_Log("Counting down from %d, Expect %d remaining\n",CountTo,Expect);
SDL_Log("Atomic %d Non-Atomic %d\n",v,bad);
}
static void RunFIFOTest ( SDL_bool  lock_free)
static

Definition at line 594 of file testatomic.c.

References SDL_EventQueue::active, ReaderData::counters, EVENTS_PER_WRITER, FIFO_Reader(), FIFO_Watcher(), FIFO_Writer(), i, WriterData::index, InitEventQueue(), j, WriterData::lock_free, ReaderData::lock_free, SDL_EventQueue::mutex, NUM_READERS, NUM_WRITERS, WriterData::queue, ReaderData::queue, readersDone, SDL_AtomicGet, SDL_AtomicSet, SDL_CreateMutex, SDL_CreateSemaphore, SDL_CreateThread, SDL_DestroyMutex, SDL_DestroySemaphore, SDL_GetTicks(), SDL_Log, SDL_memset, SDL_SemWait, SDL_snprintf, SDL_strlen, SDL_zero, and writersDone.

Referenced by main().

{
WriterData writerData[NUM_WRITERS];
ReaderData readerData[NUM_READERS];
int i, j;
int grand_total;
char textBuffer[1024];
size_t len;
SDL_Log("\nFIFO test---------------------------------------\n\n");
SDL_Log("Mode: %s\n", lock_free ? "LockFree" : "Mutex");
SDL_memset(&queue, 0xff, sizeof(queue));
InitEventQueue(&queue);
if (!lock_free) {
}
start = SDL_GetTicks();
#ifdef TEST_SPINLOCK_FIFO
/* Start a monitoring thread */
if (lock_free) {
SDL_CreateThread(FIFO_Watcher, "FIFOWatcher", &queue);
}
#endif
/* Start the readers first */
SDL_Log("Starting %d readers\n", NUM_READERS);
SDL_zero(readerData);
for (i = 0; i < NUM_READERS; ++i) {
char name[64];
SDL_snprintf(name, sizeof (name), "FIFOReader%d", i);
readerData[i].queue = &queue;
readerData[i].lock_free = lock_free;
SDL_CreateThread(FIFO_Reader, name, &readerData[i]);
}
/* Start up the writers */
SDL_Log("Starting %d writers\n", NUM_WRITERS);
SDL_zero(writerData);
for (i = 0; i < NUM_WRITERS; ++i) {
char name[64];
SDL_snprintf(name, sizeof (name), "FIFOWriter%d", i);
writerData[i].queue = &queue;
writerData[i].index = i;
writerData[i].lock_free = lock_free;
SDL_CreateThread(FIFO_Writer, name, &writerData[i]);
}
/* Wait for the writers */
while (SDL_AtomicGet(&writersRunning) > 0) {
}
/* Shut down the queue so readers exit */
SDL_AtomicSet(&queue.active, 0);
/* Wait for the readers */
while (SDL_AtomicGet(&readersRunning) > 0) {
}
end = SDL_GetTicks();
if (!lock_free) {
}
SDL_Log("Finished in %f sec\n", (end - start) / 1000.f);
SDL_Log("\n");
for (i = 0; i < NUM_WRITERS; ++i) {
SDL_Log("Writer %d wrote %d events, had %d waits\n", i, EVENTS_PER_WRITER, writerData[i].waits);
}
SDL_Log("Writers wrote %d total events\n", NUM_WRITERS*EVENTS_PER_WRITER);
/* Print a breakdown of which readers read messages from which writer */
SDL_Log("\n");
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\n", i, total, readerData[i].waits);
SDL_snprintf(textBuffer, sizeof(textBuffer), " { ");
for (j = 0; j < NUM_WRITERS; ++j) {
if (j > 0) {
len = SDL_strlen(textBuffer);
SDL_snprintf(textBuffer + len, sizeof(textBuffer) - len, ", ");
}
len = SDL_strlen(textBuffer);
SDL_snprintf(textBuffer + len, sizeof(textBuffer) - len, "%d", readerData[i].counters[j]);
}
len = SDL_strlen(textBuffer);
SDL_snprintf(textBuffer + len, sizeof(textBuffer) - len, " }\n");
SDL_Log("%s", textBuffer);
}
SDL_Log("Readers read %d total events\n", grand_total);
}
SDL_COMPILE_TIME_ASSERT ( size  ,
CountTo_GreaterThanZero   
)
static char* tf ( SDL_bool  tf)
static

Definition at line 23 of file testatomic.c.

Referenced by RunBasicTest().

{
static char *t = "TRUE";
static char *f = "FALSE";
if (tf)
{
return t;
}
return f;
}

Variable Documentation

atomicValue bad = 42
static

Definition at line 113 of file testatomic.c.

Referenced by adder(), and RunEpicTest().

SDL_atomic_t good = { 42 }
static

Definition at line 111 of file testatomic.c.

SDL_sem* readersDone
static

Definition at line 475 of file testatomic.c.

Referenced by FIFO_Reader(), and RunFIFOTest().

SDL_atomic_t readersRunning
static

Definition at line 477 of file testatomic.c.

SDL_sem* threadDone
static

Definition at line 117 of file testatomic.c.

Referenced by adder(), and runAdder().

SDL_atomic_t threadsRunning
static

Definition at line 115 of file testatomic.c.

SDL_sem* writersDone
static

Definition at line 474 of file testatomic.c.

Referenced by FIFO_Writer(), and RunFIFOTest().

SDL_atomic_t writersRunning
static

Definition at line 476 of file testatomic.c.