Esempio n. 1
0
int
SDL_TimerInit(void)
{
    SDL_TimerData *data = &SDL_timer_data;

    if (!SDL_AtomicGet(&data->active)) {
        const char *name = "SDLTimer";
        data->timermap_lock = SDL_CreateMutex();
        if (!data->timermap_lock) {
            return -1;
        }

        data->sem = SDL_CreateSemaphore(0);
        if (!data->sem) {
            SDL_DestroyMutex(data->timermap_lock);
            return -1;
        }

        SDL_AtomicSet(&data->active, 1);

        /* Timer threads use a callback into the app, so we can't set a limited stack size here. */
        data->thread = SDL_CreateThreadInternal(SDL_TimerThread, name, 0, data);
        if (!data->thread) {
            SDL_TimerQuit();
            return -1;
        }

        SDL_AtomicSet(&data->nextID, 1);
    }
    return 0;
}
Esempio n. 2
0
static void InitEventQueue(SDL_EventQueue *queue)
{
    int i;

    for (i = 0; i < MAX_ENTRIES; ++i) {
        SDL_AtomicSet(&queue->entries[i].sequence, i);
    }
    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;
}
Esempio n. 3
0
SDL_bool
SDL_RemoveTimer(SDL_TimerID id)
{
    SDL_TimerData *data = &SDL_timer_data;
    SDL_TimerMap *prev, *entry;
    SDL_bool canceled = SDL_FALSE;

    /* Find the timer */
    SDL_LockMutex(data->timermap_lock);
    prev = NULL;
    for (entry = data->timermap; entry; prev = entry, entry = entry->next) {
        if (entry->timerID == id) {
            if (prev) {
                prev->next = entry->next;
            } else {
                data->timermap = entry->next;
            }
            break;
        }
    }
    SDL_UnlockMutex(data->timermap_lock);

    if (entry) {
        if (!SDL_AtomicGet(&entry->timer->canceled)) {
            SDL_AtomicSet(&entry->timer->canceled, 1);
            canceled = SDL_TRUE;
        }
        SDL_free(entry);
    }
    return canceled;
}
Esempio n. 4
0
int
SDL_TimerInit(void)
{
    SDL_TimerData *data = &SDL_timer_data;

    if (!data->active) {
        data->timermap_lock = SDL_CreateMutex();
        if (!data->timermap_lock) {
            return -1;
        }

        data->sem = SDL_CreateSemaphore(0);
        if (!data->sem) {
            SDL_DestroyMutex(data->timermap_lock);
            return -1;
        }

        data->active = SDL_TRUE;
        /* !!! FIXME: this is nasty. */
#if (defined(__WIN32__) && !defined(_WIN32_WCE)) && !defined(HAVE_LIBC)
#undef SDL_CreateThread
        data->thread = SDL_CreateThread(SDL_TimerThread, data, NULL, NULL);
#else
        data->thread = SDL_CreateThread(SDL_TimerThread, data);
#endif
        if (!data->thread) {
            SDL_TimerQuit();
            return -1;
        }

        SDL_AtomicSet(&data->nextID, 1);
    }
    return 0;
}
Esempio n. 5
0
/*
 * Clean up after system specific haptic stuff
 */
void
SDL_SYS_HapticQuit(void)
{
    SDL_hapticlist_item *item;
    SDL_hapticlist_item *next = NULL;
    SDL_Haptic *hapticitem = NULL;

    extern SDL_Haptic *SDL_haptics;
    for (hapticitem = SDL_haptics; hapticitem; hapticitem = hapticitem->next) {
        if ((hapticitem->hwdata->bXInputHaptic) && (hapticitem->hwdata->thread)) {
            /* we _have_ to stop the thread before we free the XInput DLL! */
            SDL_AtomicSet(&hapticitem->hwdata->stopThread, 1);
            SDL_WaitThread(hapticitem->hwdata->thread, NULL);
            hapticitem->hwdata->thread = NULL;
        }
    }

    for (item = SDL_hapticlist; item; item = next) {
        /* Opened and not closed haptics are leaked, this is on purpose.
         * Close your haptic devices after usage. */
        /* !!! FIXME: (...is leaking on purpose a good idea?) - No, of course not. */
        next = item->next;
        SDL_free(item->name);
        SDL_free(item);
    }

    SDL_XINPUT_HapticQuit();
    SDL_DINPUT_HapticQuit();

    numhaptics = 0;
    SDL_hapticlist = NULL;
    SDL_hapticlist_tail = NULL;
}
Esempio n. 6
0
DxVsyncSource::DxVsyncSource(Pacer* pacer) :
    m_Pacer(pacer),
    m_Thread(nullptr),
    m_Gdi32Handle(nullptr)
{
    SDL_AtomicSet(&m_Stopping, 0);
}
Esempio n. 7
0
void
SDL_XINPUT_HapticClose(SDL_Haptic * haptic)
{
    SDL_AtomicSet(&haptic->hwdata->stopThread, 1);
    SDL_WaitThread(haptic->hwdata->thread, NULL);
    SDL_DestroyMutex(haptic->hwdata->mutex);
}
Esempio n. 8
0
int jrq_Init( JobRingQueue* queue, size_t size )
{
	assert( queue != NULL );
	assert( size > 0 );

	queue->size = size;
	queue->ringBuffer = mem_Allocate( sizeof( queue->ringBuffer[0] ) * size );
	if( queue->ringBuffer == NULL ) {
		return -1;
	}
	memset( queue->ringBuffer, 0, size * sizeof( queue->ringBuffer[0] ) );
	SDL_AtomicSet( &( queue->head ), 0 );
	SDL_AtomicSet( &( queue->tail ), 0 );
	SDL_AtomicSet( &( queue->busy ), 0 );

	return 0;
}
Esempio n. 9
0
static
void RunBasicTest()
{
    int value;
    SDL_SpinLock lock = 0;

    SDL_atomic_t v;
    SDL_bool tfret = SDL_FALSE;

    printf("\nspin lock---------------------------------------\n\n");

    SDL_AtomicLock(&lock);
    printf("AtomicLock                   lock=%d\n", lock);
    SDL_AtomicUnlock(&lock);
    printf("AtomicUnlock                 lock=%d\n", lock);

    printf("\natomic -----------------------------------------\n\n");
     
    SDL_AtomicSet(&v, 0);
    tfret = SDL_AtomicSet(&v, 10) == 0;
    printf("AtomicSet(10)        tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
    tfret = SDL_AtomicAdd(&v, 10) == 10;
    printf("AtomicAdd(10)        tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));

    SDL_AtomicSet(&v, 0);
    SDL_AtomicIncRef(&v);
    tfret = (SDL_AtomicGet(&v) == 1);
    printf("AtomicIncRef()       tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
    SDL_AtomicIncRef(&v);
    tfret = (SDL_AtomicGet(&v) == 2);
    printf("AtomicIncRef()       tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
    tfret = (SDL_AtomicDecRef(&v) == SDL_FALSE);
    printf("AtomicDecRef()       tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
    tfret = (SDL_AtomicDecRef(&v) == SDL_TRUE);
    printf("AtomicDecRef()       tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));

    SDL_AtomicSet(&v, 10);
    tfret = (SDL_AtomicCAS(&v, 0, 20) == SDL_FALSE);
    printf("AtomicCAS()          tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
    value = SDL_AtomicGet(&v);
    tfret = (SDL_AtomicCAS(&v, value, 20) == SDL_TRUE);
    printf("AtomicCAS()          tfret=%s val=%d\n", tf(tfret), SDL_AtomicGet(&v));
}
Esempio n. 10
0
DxVsyncSource::~DxVsyncSource()
{
    if (m_Thread != nullptr) {
        SDL_AtomicSet(&m_Stopping, 1);
        SDL_WaitThread(m_Thread, nullptr);
    }

    if (m_Gdi32Handle != nullptr) {
        FreeLibrary(m_Gdi32Handle);
    }
}
Esempio n. 11
0
void Object::destroy() throw() {

    GC::collect_mutex.lock();

    // If another thread just dereferenced a weak pointer to this
    // object, we might now have references. Now that we have the
    // lock, check the refcount again.

    if (SDL_AtomicGet(&refcount) > 0) {
	GC::collect_mutex.unlock();
	return;
    }

    SDL_AtomicSet(&refcount, 0);

    untrack();

    weakptrbase* cur = weak;
    weakptrbase* old;
    while (cur) {
	old = cur;
	cur = cur->next;
	old->set_ref(NULL);
    }

    // Avoid stack overflow.

    if (trashlevel < max_trashlevel) {
	++trashlevel;

	delete this;
	
	if (trashlevel == 1) {
	    while (!trashcan.empty()) {
		Object* op = static_cast<Object*>(trashcan.next);
		op->remove();
		delete op;
	    }
	}

	--trashlevel;
	
    } else {
	moveto(&trashcan);
    }
    GC::collect_mutex.unlock();
}
Esempio n. 12
0
static SDL_bool DequeueEvent_LockFree(SDL_EventQueue *queue, SDL_Event *event)
{
    SDL_EventQueueEntry *entry;
    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 ( ; ; ) {
        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
    SDL_AtomicDecRef(&queue->rwcount);
#endif
    return status;
}
Esempio n. 13
0
void la_port_input(la_window_t* window) {
	// Touch Input
	window->input.touch.x = al_safe_get_float(&window->mouse_x);
	window->input.touch.y = al_safe_get_float(&window->mouse_y);
	window->input.touch.p = 0;
	if(safe_get_uint8(&window->in.touch.h)) {
		la_print("Just touch %d", safe_get_uint8(&window->in.touch.p));
		window->input.touch.h = 1;
		window->input.touch.p = safe_get_uint8(&window->in.touch.p);
		// Not just pressed anymore
		safe_set_uint8(&window->in.touch.h, 0);
	}else{
		window->input.touch.h = 0;
	}
	//
	if(safe_get_uint8(&window->in.back)) {
		la_print("Back");
		safe_set_uint8(&window->in.back, 0);
		SDL_AtomicSet(&la_rmcexit, 0);
	}
}
Esempio n. 14
0
void Audio_init ()
{
	SDL_AudioSpec want;
	want.freq     = 44100;
	want.format   = AUDIO_S16LSB;
	want.channels = 2;
	want.samples  = 2048;
	want.callback = Audio_mixer;
	want.userdata = NULL;

	if (App_get_option_IV("info")) {
		int i;

		printf("Audio devices:\n");
		for (i = 0; i < SDL_GetNumAudioDevices(0); i++) {
			const char* name = SDL_GetAudioDeviceName(i, 0);
			printf("\t%s\n", name);
		}

		const char* cur = SDL_GetCurrentAudioDriver();
		printf("Current audio driver:\n\t%s\n", cur);

		printf("Audio drivers:\n");
		for (i = 0; i < SDL_GetNumAudioDrivers(); i++) {
			const char* name = SDL_GetAudioDriver(i);
			printf("\t%s\n", name);
		}
	}

	_audio.device = SDL_OpenAudioDevice(NULL, 0, &want, &_audio.spec, SDL_AUDIO_ALLOW_ANY_CHANGE);
	if (_audio.device <= 0)
		error("Could not open audio device");

	if (!App_get_option_IV("mute"))
		_audio.volume = SDL_MIX_MAXVOLUME;

	(void)SDL_AtomicSet(&_audio.playback_rate, 1);

	SDL_PauseAudioDevice(_audio.device, 0);
}
Esempio n. 15
0
static
void runAdder(void)
{
    Uint32 start, end;
    int T=NThreads;
 
    start = SDL_GetTicks();
 
    threadDone = SDL_CreateSemaphore(0);

    SDL_AtomicSet(&threadsRunning, NThreads);

    while (T--)
        SDL_CreateThread(adder, "Adder", NULL);
 
    while (SDL_AtomicGet(&threadsRunning) > 0)
        SDL_SemWait(threadDone);
 
    SDL_DestroySemaphore(threadDone);

    end = SDL_GetTicks();
 
    printf("Finished in %f sec\n", (end - start) / 1000.f);
}
Esempio n. 16
0
/**
 * This is the main entry point of a native application that is using
 * android_native_app_glue.  It runs in its own thread, with its own
 * event loop for receiving input events and doing other things.
 */
void android_main(struct android_app* state) {
	la_window_t* window = la_memory_allocate(sizeof(la_window_t));
	int ident;
	int events;
	struct android_poll_source* source;

	// Make sure glue isn't stripped.
	app_dummy();

	state->userData = window;
	state->onAppCmd = window_handle_cmd;
	state->onInputEvent = window_handle_input;
	window->app = state;

	// Prepare to monitor accelerometer
	window->sensorManager = ASensorManager_getInstance();
	window->accelerometerSensor = ASensorManager_getDefaultSensor(
		window->sensorManager, ASENSOR_TYPE_ACCELEROMETER);
	window->sensorEventQueue = ASensorManager_createEventQueue(
		window->sensorManager, state->looper, LOOPER_ID_USER, NULL, NULL);

//	if (state->savedState != NULL) {
		// We are starting with a previous saved state; restore from it.
//		window->state = *(struct saved_state*)state->savedState;
//	}

	// Run main():
	la_window = window;

	// TODO: is needed?
	SDL_AtomicSet(&la_rmcexit, 1);
	// Window thread ( Drawing + Events ).
	while (SDL_AtomicGet(&la_rmcexit)) {
		// Poll Events
		ident = ALooper_pollAll(0, NULL, &events, (void**)&source);

		// Process this event.
		if (source != NULL) {
			source->process(state, source);
		}

		// If a sensor has data, process it now.
		if (ident == LOOPER_ID_USER) {
			if (window->accelerometerSensor != NULL) {
				ASensorEvent event;
				while (ASensorEventQueue_getEvents(
						window->sensorEventQueue,
						&event, 1) > 0)
				{
					window->input.accel.x =
						event.acceleration.x;
					window->input.accel.y =
						event.acceleration.y;
					window->input.accel.z =
						event.acceleration.z;
				}
			}
		}
		// Run the cross-platform window loop.
		if(window->context) la_window_loop__(window);
		// Update the screen.
		la_port_swap_buffers(window);
	}
	la_print("port-android quitting....");
	// The cross-platform window kill.
	la_window_kill__(window);
	// The window is being hidden or closed, clean it up.
	window_term_display(window);
	la_print("port-android quitted....");
	exit(0);
	return;
}
Esempio n. 17
0
static int
SDL_TimerThread(void *_data)
{
    SDL_TimerData *data = (SDL_TimerData *)_data;
    SDL_Timer *pending;
    SDL_Timer *current;
    SDL_Timer *freelist_head = NULL;
    SDL_Timer *freelist_tail = NULL;
    Uint32 tick, now, interval, delay;

    /* Threaded timer loop:
     *  1. Queue timers added by other threads
     *  2. Handle any timers that should dispatch this cycle
     *  3. Wait until next dispatch time or new timer arrives
     */
    for ( ; ; ) {
        /* Pending and freelist maintenance */
        SDL_AtomicLock(&data->lock);
        {
            /* Get any timers ready to be queued */
            pending = data->pending;
            data->pending = NULL;

            /* Make any unused timer structures available */
            if (freelist_head) {
                freelist_tail->next = data->freelist;
                data->freelist = freelist_head;
            }
        }
        SDL_AtomicUnlock(&data->lock);

        /* Sort the pending timers into our list */
        while (pending) {
            current = pending;
            pending = pending->next;
            SDL_AddTimerInternal(data, current);
        }
        freelist_head = NULL;
        freelist_tail = NULL;

        /* Check to see if we're still running, after maintenance */
        if (!SDL_AtomicGet(&data->active)) {
            break;
        }

        /* Initial delay if there are no timers */
        delay = SDL_MUTEX_MAXWAIT;

        tick = SDL_GetTicks();

        /* Process all the pending timers for this tick */
        while (data->timers) {
            current = data->timers;

            if ((Sint32)(tick-current->scheduled) < 0) {
                /* Scheduled for the future, wait a bit */
                delay = (current->scheduled - tick);
                break;
            }

            /* We're going to do something with this timer */
            data->timers = current->next;

            if (SDL_AtomicGet(&current->canceled)) {
                interval = 0;
            } else {
                interval = current->callback(current->interval, current->param);
            }

            if (interval > 0) {
                /* Reschedule this timer */
                current->scheduled = tick + interval;
                SDL_AddTimerInternal(data, current);
            } else {
                if (!freelist_head) {
                    freelist_head = current;
                }
                if (freelist_tail) {
                    freelist_tail->next = current;
                }
                freelist_tail = current;

                SDL_AtomicSet(&current->canceled, 1);
            }
        }

        /* Adjust the delay based on processing time */
        now = SDL_GetTicks();
        interval = (now - tick);
        if (interval > delay) {
            delay = 0;
        } else {
            delay -= interval;
        }

        /* Note that each time a timer is added, this will return
           immediately, but we process the timers added all at once.
           That's okay, it just means we run through the loop a few
           extra times.
         */
        SDL_SemWaitTimeout(data->sem, delay);
    }
    return 0;
}
Esempio n. 18
0
static
void RunEpicTest()
{
    int b;
    atomicValue v;
 
    printf("\nepic test---------------------------------------\n\n");

    printf("Size asserted to be >= 32-bit\n");
    SDL_assert(sizeof(atomicValue)>=4);
 
    printf("Check static initializer\n");
    v=SDL_AtomicGet(&good);
    SDL_assert(v==42);
 
    SDL_assert(bad==42);
 
    printf("Test negative values\n");
    SDL_AtomicSet(&good, -5);
    v=SDL_AtomicGet(&good);
    SDL_assert(v==-5);
 
    printf("Verify maximum value\n");
    SDL_AtomicSet(&good, CountTo);
    v=SDL_AtomicGet(&good);
    SDL_assert(v==CountTo);
 
    printf("Test compare and exchange\n");
 
    b=SDL_AtomicCAS(&good, 500, 43);
    SDL_assert(!b); /* no swap since CountTo!=500 */
    v=SDL_AtomicGet(&good);
    SDL_assert(v==CountTo); /* ensure no swap */
 
    b=SDL_AtomicCAS(&good, CountTo, 44);
    SDL_assert(!!b); /* will swap */
    v=SDL_AtomicGet(&good);
    SDL_assert(v==44);
 
    printf("Test Add\n");
 
    v=SDL_AtomicAdd(&good, 1);
    SDL_assert(v==44);
    v=SDL_AtomicGet(&good);
    SDL_assert(v==45);
 
    v=SDL_AtomicAdd(&good, 10);
    SDL_assert(v==45);
    v=SDL_AtomicGet(&good);
    SDL_assert(v==55);
 
    printf("Test Add (Negative values)\n");
 
    v=SDL_AtomicAdd(&good, -20);
    SDL_assert(v==55);
    v=SDL_AtomicGet(&good);
    SDL_assert(v==35);
 
    v=SDL_AtomicAdd(&good, -50); /* crossing zero down */
    SDL_assert(v==35);
    v=SDL_AtomicGet(&good);
    SDL_assert(v==-15);
 
    v=SDL_AtomicAdd(&good, 30); /* crossing zero up */
    SDL_assert(v==-15);
    v=SDL_AtomicGet(&good);
    SDL_assert(v==15);
 
    printf("Reset before count down test\n");
    SDL_AtomicSet(&good, CountTo);
    v=SDL_AtomicGet(&good);
    SDL_assert(v==CountTo);
 
    bad=CountTo;
    SDL_assert(bad==CountTo);
 
    printf("Counting down from %d, Expect %d remaining\n",CountTo,Expect);
    runAdder();
 
    v=SDL_AtomicGet(&good);
    printf("Atomic %d Non-Atomic %d\n",v,bad);
    SDL_assert(v==Expect);
    SDL_assert(bad!=Expect);
}
Esempio n. 19
0
SDL_TimerID
SDL_AddTimer(Uint32 interval, SDL_TimerCallback callback, void *param)
{
    SDL_TimerData *data = &SDL_timer_data;
    SDL_Timer *timer;
    SDL_TimerMap *entry;

    SDL_AtomicLock(&data->lock);
    if (!SDL_AtomicGet(&data->active)) {
        if (SDL_TimerInit() < 0) {
            SDL_AtomicUnlock(&data->lock);
            return 0;
        }
    }

    timer = data->freelist;
    if (timer) {
        data->freelist = timer->next;
    }
    SDL_AtomicUnlock(&data->lock);

    if (timer) {
        SDL_RemoveTimer(timer->timerID);
    } else {
        timer = (SDL_Timer *)SDL_malloc(sizeof(*timer));
        if (!timer) {
            SDL_OutOfMemory();
            return 0;
        }
    }
    timer->timerID = SDL_AtomicIncRef(&data->nextID);
    timer->callback = callback;
    timer->param = param;
    timer->interval = interval;
    timer->scheduled = SDL_GetTicks() + interval;
    SDL_AtomicSet(&timer->canceled, 0);

    entry = (SDL_TimerMap *)SDL_malloc(sizeof(*entry));
    if (!entry) {
        SDL_free(timer);
        SDL_OutOfMemory();
        return 0;
    }
    entry->timer = timer;
    entry->timerID = timer->timerID;

    SDL_LockMutex(data->timermap_lock);
    entry->next = data->timermap;
    data->timermap = entry;
    SDL_UnlockMutex(data->timermap_lock);

    /* Add the timer to the pending list for the timer thread */
    SDL_AtomicLock(&data->lock);
    timer->next = data->pending;
    data->pending = timer;
    SDL_AtomicUnlock(&data->lock);

    /* Wake up the timer thread if necessary */
    SDL_SemPost(data->sem);

    return entry->timerID;
}
Esempio n. 20
0
 SampleData(void* _data, unsigned int _freq, unsigned int _len):
   audio_data(_data), sampling(_freq), length(_len) {
   SDL_AtomicSet(&refcnt, 1);
 }
Esempio n. 21
0
static void RunFIFOTest(SDL_bool lock_free)
{
    SDL_EventQueue queue;
    WriterData writerData[NUM_WRITERS];
    ReaderData readerData[NUM_READERS];
    Uint32 start, end;
    int i, j;
    int grand_total;
 
    printf("\nFIFO test---------------------------------------\n\n");
    printf("Mode: %s\n", lock_free ? "LockFree" : "Mutex");

    readersDone = SDL_CreateSemaphore(0);
    writersDone = SDL_CreateSemaphore(0);

    SDL_memset(&queue, 0xff, sizeof(queue));

    InitEventQueue(&queue);
    if (!lock_free) {
        queue.mutex = SDL_CreateMutex();
    }

    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 */
    printf("Starting %d readers\n", NUM_READERS);
    SDL_zero(readerData);
    SDL_AtomicSet(&readersRunning, NUM_READERS);
    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 */
    printf("Starting %d writers\n", NUM_WRITERS);
    SDL_zero(writerData);
    SDL_AtomicSet(&writersRunning, NUM_WRITERS);
    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) {
        SDL_SemWait(writersDone);
    }
 
    /* Shut down the queue so readers exit */
    queue.active = SDL_FALSE;

    /* Wait for the readers */
    while (SDL_AtomicGet(&readersRunning) > 0) {
        SDL_SemWait(readersDone);
    }

    end = SDL_GetTicks();
 
    SDL_DestroySemaphore(readersDone);
    SDL_DestroySemaphore(writersDone);

    if (!lock_free) {
        SDL_DestroyMutex(queue.mutex);
    }
 
    printf("Finished in %f sec\n", (end - start) / 1000.f);

    printf("\n");
    for (i = 0; i < NUM_WRITERS; ++i) {
        printf("Writer %d wrote %d events, had %d waits\n", i, EVENTS_PER_WRITER, writerData[i].waits);
    }
    printf("Writers wrote %d total events\n", NUM_WRITERS*EVENTS_PER_WRITER);

    /* Print a breakdown of which readers read messages from which writer */
    printf("\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;
        printf("Reader %d read %d events, had %d waits\n", i, total, readerData[i].waits);
        printf("  { ");
        for (j = 0; j < NUM_WRITERS; ++j) {
            if (j > 0) {
                printf(", ");
            }
            printf("%d", readerData[i].counters[j]);
        }
        printf(" }\n");
    }
    printf("Readers read %d total events\n", grand_total);
}