Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
Browse files

WinRT: added a functional threading backend using C++11 apis

  • Loading branch information
DavidLudwig committed Nov 24, 2012
1 parent 8f956d0 commit 551b55866ba590f6ce45f7cc2a26eab10d123e57
@@ -20,44 +20,38 @@
*/
#include "SDL_config.h"

/* An implementation of condition variables using semaphores and mutexes */
/*
This implementation borrows heavily from the BeOS condition variable
implementation, written by Christopher Tate and Owen Smith. Thanks!
*/

extern "C" {
#include "SDL_thread.h"
}

#include <chrono>
#include <condition_variable>
#include <exception>
#include <ratio>

#include "SDL_sysmutex_c.h"

struct SDL_cond
{
SDL_mutex *lock;
int waiting;
int signals;
SDL_sem *wait_sem;
SDL_sem *wait_done;
std::condition_variable_any cpp_cond;
};

/* Create a condition variable */
extern "C"
SDL_cond *
SDL_CreateCond(void)
{
SDL_cond *cond;

cond = (SDL_cond *) SDL_malloc(sizeof(SDL_cond));
if (cond) {
cond->lock = SDL_CreateMutex();
cond->wait_sem = SDL_CreateSemaphore(0);
cond->wait_done = SDL_CreateSemaphore(0);
cond->waiting = cond->signals = 0;
if (!cond->lock || !cond->wait_sem || !cond->wait_done) {
SDL_DestroyCond(cond);
cond = NULL;
}
} else {
SDL_OutOfMemory();
/* Allocate and initialize the condition variable */
try {
SDL_cond * cond = new SDL_cond;
return cond;
} catch (std::exception & ex) {
SDL_SetError("unable to create C++ condition variable: %s", ex.what());
return NULL;
} catch (...) {
SDL_SetError("unable to create C++ condition variable due to an unknown exception");
return NULL;
}
return (cond);
}

/* Destroy a condition variable */
@@ -66,16 +60,11 @@ void
SDL_DestroyCond(SDL_cond * cond)
{
if (cond) {
if (cond->wait_sem) {
SDL_DestroySemaphore(cond->wait_sem);
try {
delete cond;
} catch (...) {
// catch any and all exceptions, just in case something happens
}
if (cond->wait_done) {
SDL_DestroySemaphore(cond->wait_done);
}
if (cond->lock) {
SDL_DestroyMutex(cond->lock);
}
SDL_free(cond);
}
}

@@ -89,20 +78,14 @@ SDL_CondSignal(SDL_cond * cond)
return -1;
}

/* If there are waiting threads not already signalled, then
signal the condition and wait for the thread to respond.
*/
SDL_LockMutex(cond->lock);
if (cond->waiting > cond->signals) {
++cond->signals;
SDL_SemPost(cond->wait_sem);
SDL_UnlockMutex(cond->lock);
SDL_SemWait(cond->wait_done);
} else {
SDL_UnlockMutex(cond->lock);
try {
cond->cpp_cond.notify_one();
return 0;
} catch (...) {
// catch any and all exceptions, just in case something happens
SDL_SetError("unable to signal C++ condition variable due to an unknown exception");
return -1;
}

return 0;
}

/* Restart all threads that are waiting on the condition variable */
@@ -115,30 +98,14 @@ SDL_CondBroadcast(SDL_cond * cond)
return -1;
}

/* If there are waiting threads not already signalled, then
signal the condition and wait for the thread to respond.
*/
SDL_LockMutex(cond->lock);
if (cond->waiting > cond->signals) {
int i, num_waiting;

num_waiting = (cond->waiting - cond->signals);
cond->signals = cond->waiting;
for (i = 0; i < num_waiting; ++i) {
SDL_SemPost(cond->wait_sem);
}
/* Now all released threads are blocked here, waiting for us.
Collect them all (and win fabulous prizes!) :-)
*/
SDL_UnlockMutex(cond->lock);
for (i = 0; i < num_waiting; ++i) {
SDL_SemWait(cond->wait_done);
}
} else {
SDL_UnlockMutex(cond->lock);
try {
cond->cpp_cond.notify_all();
return 0;
} catch (...) {
// catch any and all exceptions, just in case something happens
SDL_SetError("unable to broadcast C++ condition variable due to an unknown exception");
return -1;
}

return 0;
}

/* Wait on the condition variable for at most 'ms' milliseconds.
@@ -166,56 +133,43 @@ extern "C"
int
SDL_CondWaitTimeout(SDL_cond * cond, SDL_mutex * mutex, Uint32 ms)
{
int retval;

if (!cond) {
SDL_SetError("Passed a NULL condition variable");
return -1;
}

/* Obtain the protection mutex, and increment the number of waiters.
This allows the signal mechanism to only perform a signal if there
are waiting threads.
*/
SDL_LockMutex(cond->lock);
++cond->waiting;
SDL_UnlockMutex(cond->lock);

/* Unlock the mutex, as is required by condition variable semantics */
SDL_UnlockMutex(mutex);

/* Wait for a signal */
if (ms == SDL_MUTEX_MAXWAIT) {
retval = SDL_SemWait(cond->wait_sem);
} else {
retval = SDL_SemWaitTimeout(cond->wait_sem, ms);
if (!mutex) {
SDL_SetError("Passed a NULL mutex variable");
return -1;
}

/* Let the signaler know we have completed the wait, otherwise
the signaler can race ahead and get the condition semaphore
if we are stopped between the mutex unlock and semaphore wait,
giving a deadlock. See the following URL for details:
http://www-classic.be.com/aboutbe/benewsletter/volume_III/Issue40.html
*/
SDL_LockMutex(cond->lock);
if (cond->signals > 0) {
/* If we timed out, we need to eat a condition signal */
if (retval > 0) {
SDL_SemWait(cond->wait_sem);
try {
std::unique_lock<std::recursive_mutex> cpp_lock(mutex->cpp_mutex, std::defer_lock_t());
if (ms == SDL_MUTEX_MAXWAIT) {
cond->cpp_cond.wait(
cpp_lock
);
cpp_lock.release();
return 0;
} else {
auto wait_result = cond->cpp_cond.wait_for(
cpp_lock,
std::chrono::duration<Uint32, std::milli>(ms)
);
cpp_lock.release();
if (wait_result == std::cv_status::timeout) {
return SDL_MUTEX_TIMEDOUT;
} else {
return 0;
}
}
/* We always notify the signal thread that we are done */
SDL_SemPost(cond->wait_done);

/* Signal handshake complete */
--cond->signals;
} catch (std::exception & ex) {
SDL_SetError("unable to wait on C++ condition variable: %s", ex.what());
return -1;
} catch (...) {
SDL_SetError("unable to lock wait on C++ condition variable due to an unknown exception");
return -1;
}
--cond->waiting;
SDL_UnlockMutex(cond->lock);

/* Lock the mutex, as is required by condition variable semantics */
SDL_LockMutex(mutex);

return retval;
}

/* Wait on the condition variable forever */
@@ -20,41 +20,34 @@
*/
#include "SDL_config.h"

/* An implementation of mutexes using semaphores */

extern "C" {
#include "SDL_thread.h"
#include "SDL_systhread_c.h"
#include "SDL_log.h"
}

#include <exception>

#include "SDL_sysmutex_c.h"
#include <Windows.h>

struct SDL_mutex
{
int recursive;
SDL_threadID owner;
SDL_sem *sem;
};

/* Create a mutex */
extern "C"
SDL_mutex *
SDL_CreateMutex(void)
{
SDL_mutex *mutex;

/* Allocate mutex memory */
mutex = (SDL_mutex *) SDL_malloc(sizeof(*mutex));
if (mutex) {
/* Create the mutex semaphore, with initial value 1 */
mutex->sem = SDL_CreateSemaphore(1);
mutex->recursive = 0;
mutex->owner = 0;
if (!mutex->sem) {
SDL_free(mutex);
mutex = NULL;
}
} else {
SDL_OutOfMemory();
/* Allocate and initialize the mutex */
try {
SDL_mutex * mutex = new SDL_mutex;
return mutex;
} catch (std::exception & ex) {
SDL_SetError("unable to create C++ mutex: %s", ex.what());
return NULL;
} catch (...) {
SDL_SetError("unable to create C++ mutex due to an unknown exception");
return NULL;
}
return mutex;
}

/* Free the mutex */
@@ -63,10 +56,11 @@ void
SDL_DestroyMutex(SDL_mutex * mutex)
{
if (mutex) {
if (mutex->sem) {
SDL_DestroySemaphore(mutex->sem);
try {
delete mutex;
} catch (...) {
// catch any and all exceptions, just in case something happens
}
SDL_free(mutex);
}
}

@@ -75,65 +69,45 @@ extern "C"
int
SDL_mutexP(SDL_mutex * mutex)
{
#if SDL_THREADS_DISABLED
return 0;
#else
SDL_threadID this_thread;

SDL_threadID threadID = SDL_ThreadID();
DWORD realThreadID = GetCurrentThreadId();
if (mutex == NULL) {
SDL_SetError("Passed a NULL mutex");
return -1;
}

this_thread = SDL_ThreadID();
if (mutex->owner == this_thread) {
++mutex->recursive;
} else {
/* The order of operations is important.
We set the locking thread id after we obtain the lock
so unlocks from other threads will fail.
*/
SDL_SemWait(mutex->sem);
mutex->owner = this_thread;
mutex->recursive = 0;
try {
mutex->cpp_mutex.lock();
return 0;
} catch (std::exception & ex) {
SDL_SetError("unable to lock C++ mutex: %s", ex.what());
return -1;
} catch (...) {
SDL_SetError("unable to lock C++ mutex due to an unknown exception");
return -1;
}

return 0;
#endif /* SDL_THREADS_DISABLED */
}

/* Unlock the mutex */
extern "C"
int
SDL_mutexV(SDL_mutex * mutex)
{
#if SDL_THREADS_DISABLED
return 0;
#else
SDL_threadID threadID = SDL_ThreadID();
DWORD realThreadID = GetCurrentThreadId();
if (mutex == NULL) {
SDL_SetError("Passed a NULL mutex");
return -1;
}

/* If we don't own the mutex, we can't unlock it */
if (SDL_ThreadID() != mutex->owner) {
SDL_SetError("mutex not owned by this thread");
try {
mutex->cpp_mutex.unlock();
return 0;
} catch (...) {
// catch any and all exceptions, just in case something happens.
SDL_SetError("unable to unlock C++ mutex due to an unknown exception");
return -1;
}

if (mutex->recursive) {
--mutex->recursive;
} else {
/* The order of operations is important.
First reset the owner so another thread doesn't lock
the mutex and set the ownership before we reset it,
then release the lock semaphore.
*/
mutex->owner = 0;
SDL_SemPost(mutex->sem);
}
return 0;
#endif /* SDL_THREADS_DISABLED */
}

/* vi: set ts=4 sw=4 expandtab: */

0 comments on commit 551b558

Please sign in to comment.