all repos — mgba @ 3837213f941a150bc7a3c26c9e38b75f4ef12952

mGBA Game Boy Advance Emulator

Support Win32 threads on Vista and higher
Jeffrey Pfau jeffrey@endrift.com
Wed, 15 Jan 2014 23:46:47 -0800
commit

3837213f941a150bc7a3c26c9e38b75f4ef12952

parent

85d30ac27174392c415c275d67a95badec64436a

M CMakeLists.txtCMakeLists.txt

@@ -17,8 +17,10 @@

find_package(SDL 1.2 REQUIRED) file(GLOB PLATFORM_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/sdl-*.c) if(WIN32) + add_definitions(-D_WIN32_WINNT=0x0600) file(GLOB OS_SRC ${CMAKE_SOURCE_DIR}/src/platform/windows/*.c) else() + add_definitions(-DUSE_PTHREADS) file(GLOB OS_SRC ${CMAKE_SOURCE_DIR}/src/platform/posix/*.c) endif() include_directories(${CMAKE_SOURCE_DIR}/src/platform/sdl)
M src/gba/gba-thread.csrc/gba/gba-thread.c

@@ -7,15 +7,41 @@

#include <stdlib.h> #include <signal.h> +#ifdef USE_PTHREADS static pthread_key_t _contextKey; static pthread_once_t _contextOnce = PTHREAD_ONCE_INIT; static void _createTLS(void) { pthread_key_create(&_contextKey, 0); } +#else +static DWORD _contextKey; +static INIT_ONCE _contextOnce = INIT_ONCE_STATIC_INIT; -static void* _GBAThreadRun(void* context) { +static BOOL CALLBACK _createTLS(PINIT_ONCE once, PVOID param, PVOID* context) { + (void) (once); + (void) (param); + (void) (context); + _contextKey = TlsAlloc(); + return TRUE; +} +#endif + +static void _changeState(struct GBAThread* threadContext, enum ThreadState newState, int broadcast) { + MutexLock(&threadContext->stateMutex); + threadContext->state = newState; + if (broadcast) { + ConditionWake(&threadContext->stateCond); + } + MutexUnlock(&threadContext->stateMutex); +} + +static THREAD_ENTRY _GBAThreadRun(void* context) { +#ifdef USE_PTHREADS pthread_once(&_contextOnce, _createTLS); +#else + InitOnceExecuteOnce(&_contextOnce, _createTLS, NULL, 0); +#endif #ifdef USE_DEBUGGER struct ARMDebugger debugger;

@@ -24,7 +50,7 @@ struct GBA gba;

struct GBAThread* threadContext = context; char* savedata = 0; -#ifndef _WIN32 +#if !defined(_WIN32) && defined(USE_PTHREADS) sigset_t signals; sigfillset(&signals); pthread_sigmask(SIG_UNBLOCK, &signals, 0);

@@ -33,7 +59,11 @@

GBAInit(&gba); threadContext->gba = &gba; gba.sync = &threadContext->sync; +#ifdef USE_PTHREADS pthread_setspecific(_contextKey, threadContext); +#else + TlsSetValue(_contextKey, threadContext); +#endif if (threadContext->renderer) { GBAVideoAssociateRenderer(&gba.video, threadContext->renderer); }

@@ -79,19 +109,14 @@ if (threadContext->startCallback) {

threadContext->startCallback(threadContext); } - pthread_mutex_lock(&threadContext->stateMutex); - threadContext->state = THREAD_RUNNING; - pthread_cond_broadcast(&threadContext->stateCond); - pthread_mutex_unlock(&threadContext->stateMutex); + _changeState(threadContext, THREAD_RUNNING, 1); while (threadContext->state < THREAD_EXITING) { #ifdef USE_DEBUGGER if (threadContext->useDebugger) { ARMDebuggerRun(&debugger); if (debugger.state == DEBUGGER_SHUTDOWN) { - pthread_mutex_lock(&threadContext->stateMutex); - threadContext->state = THREAD_EXITING; - pthread_mutex_unlock(&threadContext->stateMutex); + _changeState(threadContext, THREAD_EXITING, 0); } } else { #endif

@@ -101,17 +126,15 @@ }

#ifdef USE_DEBUGGER } #endif + MutexLock(&threadContext->stateMutex); while (threadContext->state == THREAD_PAUSED) { - pthread_mutex_lock(&threadContext->stateMutex); - pthread_cond_wait(&threadContext->stateCond, &threadContext->stateMutex); - pthread_mutex_unlock(&threadContext->stateMutex); + ConditionWait(&threadContext->stateCond, &threadContext->stateMutex); } + MutexUnlock(&threadContext->stateMutex); } while (threadContext->state != THREAD_SHUTDOWN) { - pthread_mutex_lock(&threadContext->stateMutex); - threadContext->state = THREAD_SHUTDOWN; - pthread_mutex_unlock(&threadContext->stateMutex); + _changeState(threadContext, THREAD_SHUTDOWN, 0); } if (threadContext->cleanCallback) {

@@ -120,8 +143,8 @@ }

GBADeinit(&gba); - pthread_cond_broadcast(&threadContext->sync.videoFrameAvailableCond); - pthread_cond_broadcast(&threadContext->sync.audioRequiredCond); + ConditionWake(&threadContext->sync.videoFrameAvailableCond); + ConditionWake(&threadContext->sync.audioRequiredCond); free(savedata); return 0;

@@ -129,52 +152,55 @@ }

int GBAThreadStart(struct GBAThread* threadContext) { // TODO: error check - pthread_mutex_init(&threadContext->stateMutex, 0); - pthread_cond_init(&threadContext->stateCond, 0); - - pthread_mutex_init(&threadContext->sync.videoFrameMutex, 0); - pthread_cond_init(&threadContext->sync.videoFrameAvailableCond, 0); - pthread_cond_init(&threadContext->sync.videoFrameRequiredCond, 0); - pthread_mutex_init(&threadContext->sync.audioBufferMutex, 0); - pthread_cond_init(&threadContext->sync.audioRequiredCond, 0); - - pthread_mutex_lock(&threadContext->stateMutex); threadContext->activeKeys = 0; threadContext->state = THREAD_INITIALIZED; threadContext->sync.videoFrameOn = 1; threadContext->sync.videoFrameSkip = 0; - pthread_create(&threadContext->thread, 0, _GBAThreadRun, threadContext); - pthread_cond_wait(&threadContext->stateCond, &threadContext->stateMutex); - pthread_mutex_unlock(&threadContext->stateMutex); + + MutexInit(&threadContext->stateMutex); + ConditionInit(&threadContext->stateCond); + + MutexInit(&threadContext->sync.videoFrameMutex); + ConditionInit(&threadContext->sync.videoFrameAvailableCond); + ConditionInit(&threadContext->sync.videoFrameRequiredCond); + MutexInit(&threadContext->sync.audioBufferMutex); + ConditionInit(&threadContext->sync.audioRequiredCond); + + MutexLock(&threadContext->stateMutex); + ThreadCreate(&threadContext->thread, _GBAThreadRun, threadContext); + while (threadContext->state < THREAD_RUNNING) { + ConditionWait(&threadContext->stateCond, &threadContext->stateMutex); + } + MutexUnlock(&threadContext->stateMutex); return 0; } void GBAThreadJoin(struct GBAThread* threadContext) { - pthread_mutex_lock(&threadContext->sync.videoFrameMutex); + MutexLock(&threadContext->sync.videoFrameMutex); threadContext->sync.videoFrameWait = 0; - pthread_cond_broadcast(&threadContext->sync.videoFrameRequiredCond); - pthread_mutex_unlock(&threadContext->sync.videoFrameMutex); + ConditionWake(&threadContext->sync.videoFrameRequiredCond); + MutexUnlock(&threadContext->sync.videoFrameMutex); - pthread_join(threadContext->thread, 0); + ThreadJoin(threadContext->thread); - pthread_mutex_destroy(&threadContext->stateMutex); - pthread_cond_destroy(&threadContext->stateCond); + MutexDeinit(&threadContext->stateMutex); + ConditionDeinit(&threadContext->stateCond); - pthread_mutex_destroy(&threadContext->sync.videoFrameMutex); - pthread_cond_broadcast(&threadContext->sync.videoFrameAvailableCond); - pthread_cond_destroy(&threadContext->sync.videoFrameAvailableCond); - pthread_cond_broadcast(&threadContext->sync.videoFrameRequiredCond); - pthread_cond_destroy(&threadContext->sync.videoFrameRequiredCond); + MutexDeinit(&threadContext->sync.videoFrameMutex); + ConditionWake(&threadContext->sync.videoFrameAvailableCond); + ConditionDeinit(&threadContext->sync.videoFrameAvailableCond); + ConditionWake(&threadContext->sync.videoFrameRequiredCond); + ConditionDeinit(&threadContext->sync.videoFrameRequiredCond); - pthread_cond_broadcast(&threadContext->sync.audioRequiredCond); - pthread_cond_destroy(&threadContext->sync.audioRequiredCond); - pthread_mutex_destroy(&threadContext->sync.audioBufferMutex); + ConditionWake(&threadContext->sync.audioRequiredCond); + ConditionDeinit(&threadContext->sync.audioRequiredCond); + MutexDeinit(&threadContext->sync.audioBufferMutex); } void GBAThreadPause(struct GBAThread* threadContext) { int frameOn = 1; - pthread_mutex_lock(&threadContext->stateMutex); + MutexLock(&threadContext->stateMutex); if (threadContext->state == THREAD_RUNNING) { if (threadContext->debugger && threadContext->debugger->state == DEBUGGER_RUNNING) { threadContext->debugger->state = DEBUGGER_EXITING;

@@ -182,37 +208,37 @@ }

threadContext->state = THREAD_PAUSED; frameOn = 0; } - pthread_mutex_unlock(&threadContext->stateMutex); - pthread_mutex_lock(&threadContext->sync.videoFrameMutex); + MutexUnlock(&threadContext->stateMutex); + MutexLock(&threadContext->sync.videoFrameMutex); if (frameOn != threadContext->sync.videoFrameOn) { threadContext->sync.videoFrameOn = frameOn; - pthread_cond_broadcast(&threadContext->sync.videoFrameAvailableCond); + ConditionWake(&threadContext->sync.videoFrameAvailableCond); } - pthread_mutex_unlock(&threadContext->sync.videoFrameMutex); + MutexUnlock(&threadContext->sync.videoFrameMutex); } void GBAThreadUnpause(struct GBAThread* threadContext) { int frameOn = 1; - pthread_mutex_lock(&threadContext->stateMutex); + MutexLock(&threadContext->stateMutex); if (threadContext->state == THREAD_PAUSED) { threadContext->state = THREAD_RUNNING; - pthread_cond_broadcast(&threadContext->stateCond); + ConditionWake(&threadContext->stateCond); } - pthread_mutex_unlock(&threadContext->stateMutex); - pthread_mutex_lock(&threadContext->sync.videoFrameMutex); + MutexUnlock(&threadContext->stateMutex); + MutexLock(&threadContext->sync.videoFrameMutex); if (frameOn != threadContext->sync.videoFrameOn) { threadContext->sync.videoFrameOn = frameOn; - pthread_cond_broadcast(&threadContext->sync.videoFrameAvailableCond); + ConditionWake(&threadContext->sync.videoFrameAvailableCond); } - pthread_mutex_unlock(&threadContext->sync.videoFrameMutex); + MutexUnlock(&threadContext->sync.videoFrameMutex); } void GBAThreadTogglePause(struct GBAThread* threadContext) { int frameOn = 1; - pthread_mutex_lock(&threadContext->stateMutex); + MutexLock(&threadContext->stateMutex); if (threadContext->state == THREAD_PAUSED) { threadContext->state = THREAD_RUNNING; - pthread_cond_broadcast(&threadContext->stateCond); + ConditionWake(&threadContext->stateCond); } else if (threadContext->state == THREAD_RUNNING) { if (threadContext->debugger && threadContext->debugger->state == DEBUGGER_RUNNING) { threadContext->debugger->state = DEBUGGER_EXITING;

@@ -220,35 +246,43 @@ }

threadContext->state = THREAD_PAUSED; frameOn = 0; } - pthread_mutex_unlock(&threadContext->stateMutex); - pthread_mutex_lock(&threadContext->sync.videoFrameMutex); + MutexUnlock(&threadContext->stateMutex); + MutexLock(&threadContext->sync.videoFrameMutex); if (frameOn != threadContext->sync.videoFrameOn) { threadContext->sync.videoFrameOn = frameOn; - pthread_cond_broadcast(&threadContext->sync.videoFrameAvailableCond); + ConditionWake(&threadContext->sync.videoFrameAvailableCond); } - pthread_mutex_unlock(&threadContext->sync.videoFrameMutex); + MutexUnlock(&threadContext->sync.videoFrameMutex); } + +#ifdef USE_PTHREADS struct GBAThread* GBAThreadGetContext(void) { pthread_once(&_contextOnce, _createTLS); return pthread_getspecific(_contextKey); } +#else +struct GBAThread* GBAThreadGetContext(void) { + InitOnceExecuteOnce(&_contextOnce, _createTLS, NULL, 0); + return TlsGetValue(_contextKey); +} +#endif void GBASyncPostFrame(struct GBASync* sync) { if (!sync) { return; } - pthread_mutex_lock(&sync->videoFrameMutex); + MutexLock(&sync->videoFrameMutex); ++sync->videoFramePending; --sync->videoFrameSkip; if (sync->videoFrameSkip < 0) { - pthread_cond_broadcast(&sync->videoFrameAvailableCond); - if (sync->videoFrameWait) { - pthread_cond_wait(&sync->videoFrameRequiredCond, &sync->videoFrameMutex); + ConditionWake(&sync->videoFrameAvailableCond); + while (sync->videoFrameWait && sync->videoFramePending) { + ConditionWait(&sync->videoFrameRequiredCond, &sync->videoFrameMutex); } } - pthread_mutex_unlock(&sync->videoFrameMutex); + MutexUnlock(&sync->videoFrameMutex); } int GBASyncWaitFrameStart(struct GBASync* sync, int frameskip) {

@@ -256,12 +290,12 @@ if (!sync) {

return 1; } - pthread_mutex_lock(&sync->videoFrameMutex); - pthread_cond_broadcast(&sync->videoFrameRequiredCond); + MutexLock(&sync->videoFrameMutex); + ConditionWake(&sync->videoFrameRequiredCond); if (!sync->videoFrameOn) { return 0; } - pthread_cond_wait(&sync->videoFrameAvailableCond, &sync->videoFrameMutex); + ConditionWait(&sync->videoFrameAvailableCond, &sync->videoFrameMutex); sync->videoFramePending = 0; sync->videoFrameSkip = frameskip; return 1;

@@ -272,7 +306,7 @@ if (!sync) {

return; } - pthread_mutex_unlock(&sync->videoFrameMutex); + MutexUnlock(&sync->videoFrameMutex); } int GBASyncDrawingFrame(struct GBASync* sync) {

@@ -281,16 +315,17 @@ }

void GBASyncProduceAudio(struct GBASync* sync, int wait) { if (sync->audioWait && wait) { - pthread_cond_wait(&sync->audioRequiredCond, &sync->audioBufferMutex); + // TODO loop properly in event of spurious wakeups + ConditionWait(&sync->audioRequiredCond, &sync->audioBufferMutex); } - pthread_mutex_unlock(&sync->audioBufferMutex); + MutexUnlock(&sync->audioBufferMutex); } void GBASyncLockAudio(struct GBASync* sync) { - pthread_mutex_lock(&sync->audioBufferMutex); + MutexLock(&sync->audioBufferMutex); } void GBASyncConsumeAudio(struct GBASync* sync) { - pthread_cond_broadcast(&sync->audioRequiredCond); - pthread_mutex_unlock(&sync->audioBufferMutex); + ConditionWake(&sync->audioRequiredCond); + MutexUnlock(&sync->audioBufferMutex); }
M src/gba/gba-thread.hsrc/gba/gba-thread.h

@@ -1,7 +1,7 @@

#ifndef GBA_THREAD_H #define GBA_THREAD_H -#include <pthread.h> +#include "threading.h" struct GBAThread; typedef void (*ThreadCallback)(struct GBAThread* threadContext);

@@ -29,10 +29,10 @@ int activeKeys;

int frameskip; // Threading state - pthread_t thread; + Thread thread; - pthread_mutex_t stateMutex; - pthread_cond_t stateCond; + Mutex stateMutex; + Condition stateCond; ThreadCallback startCallback; ThreadCallback cleanCallback;

@@ -43,13 +43,13 @@ int videoFramePending;

int videoFrameWait; int videoFrameSkip; int videoFrameOn; - pthread_mutex_t videoFrameMutex; - pthread_cond_t videoFrameAvailableCond; - pthread_cond_t videoFrameRequiredCond; + Mutex videoFrameMutex; + Condition videoFrameAvailableCond; + Condition videoFrameRequiredCond; int audioWait; - pthread_cond_t audioRequiredCond; - pthread_mutex_t audioBufferMutex; + Condition audioRequiredCond; + Mutex audioBufferMutex; } sync; };
M src/platform/sdl/sdl-events.csrc/platform/sdl/sdl-events.c

@@ -133,10 +133,10 @@ // FIXME: this isn't thread-safe

if (context->debugger) { context->debugger->state = DEBUGGER_EXITING; } - pthread_mutex_lock(&context->stateMutex); + MutexLock(&context->stateMutex); context->state = THREAD_EXITING; - pthread_cond_broadcast(&context->stateCond); - pthread_mutex_unlock(&context->stateMutex); + ConditionWake(&context->stateCond); + MutexUnlock(&context->stateMutex); break; case SDL_KEYDOWN: case SDL_KEYUP:
A src/util/threading.h

@@ -0,0 +1,120 @@

+#ifndef THREADING_H +#define THREADING_H + + +#ifdef USE_PTHREADS +#include <pthread.h> + +#define THREAD_ENTRY void* +typedef THREAD_ENTRY (*ThreadEntry)(void*); + +typedef pthread_t Thread; +typedef pthread_mutex_t Mutex; +typedef pthread_cond_t Condition; + +static inline int MutexInit(Mutex* mutex) { + return pthread_mutex_init(mutex, 0); +} + +static inline int MutexDeinit(Mutex* mutex) { + return pthread_mutex_destroy(mutex); +} + +static inline int MutexLock(Mutex* mutex) { + return pthread_mutex_lock(mutex); +} + +static inline int MutexUnlock(Mutex* mutex) { + return pthread_mutex_unlock(mutex); +} + +static inline int ConditionInit(Condition* cond) { + return pthread_cond_init(cond, 0); +} + +static inline int ConditionDeinit(Condition* cond) { + return pthread_cond_destroy(cond); +} + +static inline int ConditionWait(Condition* cond, Mutex* mutex) { + return pthread_cond_wait(cond, mutex); +} + +static inline int ConditionWake(Condition* cond) { + return pthread_cond_broadcast(cond); +} + +static inline int ThreadCreate(Thread* thread, ThreadEntry entry, void* context) { + return pthread_create(thread, 0, entry, context); +} + +static inline int ThreadJoin(Thread thread) { + return pthread_join(thread, 0); +} + +#else +#define _WIN32_WINNT 0x0600 +#include <windows.h> +#define THREAD_ENTRY DWORD WINAPI +typedef THREAD_ENTRY ThreadEntry(LPVOID); + +typedef HANDLE Thread; +typedef CRITICAL_SECTION Mutex; +typedef CONDITION_VARIABLE Condition; + +static inline int MutexInit(Mutex* mutex) { + InitializeCriticalSection(mutex); + return GetLastError(); +} + +static inline int MutexDeinit(Mutex* mutex) { + DeleteCriticalSection(mutex); + return GetLastError(); +} + +static inline int MutexLock(Mutex* mutex) { + EnterCriticalSection(mutex); + return GetLastError(); +} + +static inline int MutexUnlock(Mutex* mutex) { + LeaveCriticalSection(mutex); + return GetLastError(); +} + +static inline int ConditionInit(Condition* cond) { + InitializeConditionVariable(cond); + return GetLastError(); +} + +static inline int ConditionDeinit(Condition* cond) { + // This is a no-op on Windows + (void)(cond); + return 0; +} + +static inline int ConditionWait(Condition* cond, Mutex* mutex) { + SleepConditionVariableCS(cond, mutex, INFINITE); + return GetLastError(); +} + +static inline int ConditionWake(Condition* cond) { + WakeAllConditionVariable(cond); + return GetLastError(); +} + +static inline int ThreadCreate(Thread* thread, ThreadEntry entry, void* context) { + *thread = CreateThread(NULL, 0, entry, context, 0, 0); + return GetLastError(); +} + +static inline int ThreadJoin(Thread thread) { + DWORD error = WaitForSingleObject(thread, INFINITE); + if (error == WAIT_FAILED) { + return GetLastError(); + } + return 0; +} +#endif + +#endif