# HG changeset patch # User Sam Lantinga # Date 1296029014 28800 # Node ID 1b3678ac98049174d5ef868a4f51933e8b6af4c1 # Parent 470ede30189c60667902235ca9b9ab05f1ae1edd Added a test to measure the impact of a separate thread periodically locking the queue entirely. diff -r 470ede30189c -r 1b3678ac9804 test/testatomic.c --- a/test/testatomic.c Tue Jan 25 23:23:52 2011 -0800 +++ b/test/testatomic.c Wed Jan 26 00:03:34 2011 -0800 @@ -234,6 +234,11 @@ /**************************************************************************/ /* 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 @@ -265,6 +270,14 @@ char cache_pad3[CACHELINE-sizeof(SDL_atomic_t)]; +#ifdef TEST_SPINLOCK_FIFO + SDL_SpinLock lock; + SDL_atomic_t rwcount; + SDL_atomic_t watcher; + + char cache_pad4[CACHELINE-sizeof(SDL_SpinLock)-2*sizeof(SDL_atomic_t)]; +#endif + SDL_bool active; /* Only needed for the mutex test */ @@ -281,6 +294,10 @@ } SDL_AtomicSet(&queue->enqueue_pos, 0); SDL_AtomicSet(&queue->dequeue_pos, 0); +#ifdef TEST_SPINLOCK_FIFO + queue->lock = 0; + SDL_AtomicSet(&queue->rwcount, 0); +#endif queue->active = SDL_TRUE; } @@ -290,6 +307,15 @@ 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); + SDL_assert(SDL_AtomicGet(&queue->watcher) == 0); + SDL_AtomicIncRef(&queue->rwcount); + SDL_AtomicUnlock(&queue->lock); +#endif queue_pos = (unsigned)SDL_AtomicGet(&queue->enqueue_pos); for ( ; ; ) { @@ -300,22 +326,26 @@ 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 */ - return SDL_FALSE; + 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); } } - /* We own the object, fill it! */ - entry->event = *event; - SDL_AtomicSet(&entry->sequence, (int)(queue_pos + 1)); - - return SDL_TRUE; +#ifdef TEST_SPINLOCK_FIFO + SDL_AtomicDecRef(&queue->rwcount); +#endif + return status; } static SDL_bool DequeueEvent_LockFree(SDL_EventQueue *queue, SDL_Event *event) @@ -324,6 +354,15 @@ 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); + SDL_assert(SDL_AtomicGet(&queue->watcher) == 0); + SDL_AtomicIncRef(&queue->rwcount); + SDL_AtomicUnlock(&queue->lock); +#endif queue_pos = (unsigned)SDL_AtomicGet(&queue->dequeue_pos); for ( ; ; ) { @@ -334,22 +373,26 @@ 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 */ - return SDL_FALSE; + 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); } } - /* We own the object, fill it! */ - *event = entry->event; - SDL_AtomicSet(&entry->sequence, (int)(queue_pos+MAX_ENTRIES)); - - return SDL_TRUE; +#ifdef TEST_SPINLOCK_FIFO + SDL_AtomicDecRef(&queue->rwcount); +#endif + return status; } static SDL_bool EnqueueEvent_Mutex(SDL_EventQueue *queue, const SDL_Event *event) @@ -358,6 +401,7 @@ unsigned queue_pos; unsigned entry_seq; int delta; + SDL_bool status = SDL_FALSE; SDL_mutexP(queue->mutex); @@ -368,21 +412,20 @@ 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 */ - SDL_mutexV(queue->mutex); - return SDL_FALSE; } else { printf("ERROR: mutex failed!\n"); } - /* We own the object, fill it! */ - entry->event = *event; - entry->sequence.value = (int)(queue_pos + 1); - SDL_mutexV(queue->mutex); - return SDL_TRUE; + return status; } static SDL_bool DequeueEvent_Mutex(SDL_EventQueue *queue, SDL_Event *event) @@ -391,6 +434,7 @@ unsigned queue_pos; unsigned entry_seq; int delta; + SDL_bool status = SDL_FALSE; SDL_mutexP(queue->mutex); @@ -401,21 +445,20 @@ 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 */ - SDL_mutexV(queue->mutex); - return SDL_FALSE; } else { printf("ERROR: mutex failed!\n"); } - /* We own the object, fill it! */ - *event = entry->event; - entry->sequence.value = (int)(queue_pos + MAX_ENTRIES); - SDL_mutexV(queue->mutex); - return SDL_TRUE; + return status; } static SDL_sem *writersDone; @@ -517,6 +560,29 @@ return 0; } +#ifdef TEST_SPINLOCK_FIFO +/* This thread periodically locks the queue for no particular reason */ +static int FIFO_Watcher(void* _data) +{ + SDL_EventQueue *queue = (SDL_EventQueue *)_data; + + while (queue->active) { + SDL_AtomicLock(&queue->lock); + SDL_AtomicIncRef(&queue->watcher); + while (SDL_AtomicGet(&queue->rwcount) > 0) { + SDL_Delay(0); + } + /* Do queue manipulation here... */ + SDL_AtomicDecRef(&queue->watcher); + SDL_AtomicUnlock(&queue->lock); + + /* Wait a bit... */ + SDL_Delay(1); + } + return 0; +} +#endif /* TEST_SPINLOCK_FIFO */ + static void RunFIFOTest(SDL_bool lock_free) { SDL_EventQueue queue; @@ -541,6 +607,13 @@ start = SDL_GetTicks(); +#ifdef TEST_SPINLOCK_FIFO + /* Start a monitoring thread */ + if (lock_free) { + SDL_CreateThread(FIFO_Watcher, &queue); + } +#endif + /* Start the readers first */ printf("Starting %d readers\n", NUM_READERS); SDL_zero(readerData);