Skip to content
This repository has been archived by the owner on Feb 11, 2021. It is now read-only.

Commit

Permalink
WinRT: added a functional threading backend using C++11 apis
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidLudwig committed Nov 24, 2012
1 parent 8f956d0 commit 551b558
Show file tree
Hide file tree
Showing 4 changed files with 176 additions and 182 deletions.
178 changes: 66 additions & 112 deletions src/thread/stdcpp/SDL_syscond.cpp
Expand Up @@ -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 */
Expand All @@ -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);
}
}

Expand All @@ -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 */
Expand All @@ -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.
Expand Down Expand Up @@ -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 */
Expand Down
106 changes: 40 additions & 66 deletions src/thread/stdcpp/SDL_sysmutex.cpp
Expand Up @@ -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 */
Expand All @@ -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);
}
}

Expand All @@ -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.