Support Win32 threads on Vista and higher

This commit is contained in:
Jeffrey Pfau 2014-01-15 23:46:47 -08:00
parent 85d30ac271
commit 3837213f94
5 changed files with 244 additions and 87 deletions

View File

@ -17,8 +17,10 @@ include_directories(${CMAKE_SOURCE_DIR}/src/util)
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)

View File

@ -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 @@ static void* _GBAThreadRun(void* context) {
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 @@ static void* _GBAThreadRun(void* context) {
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 @@ static void* _GBAThreadRun(void* context) {
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 @@ static void* _GBAThreadRun(void* context) {
#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 @@ static void* _GBAThreadRun(void* context) {
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 @@ static void* _GBAThreadRun(void* context) {
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 @@ void GBAThreadPause(struct GBAThread* threadContext) {
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 @@ void GBAThreadTogglePause(struct GBAThread* threadContext) {
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 @@ int GBASyncWaitFrameStart(struct GBASync* sync, int frameskip) {
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 @@ void GBASyncWaitFrameEnd(struct GBASync* sync) {
return;
}
pthread_mutex_unlock(&sync->videoFrameMutex);
MutexUnlock(&sync->videoFrameMutex);
}
int GBASyncDrawingFrame(struct GBASync* sync) {
@ -281,16 +315,17 @@ int GBASyncDrawingFrame(struct GBASync* sync) {
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);
}

View File

@ -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 @@ struct GBAThread {
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 @@ struct GBAThread {
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;
};

View File

@ -133,10 +133,10 @@ void GBASDLHandleEvent(struct GBAThread* context, const union SDL_Event* event)
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:

120
src/util/threading.h Normal file
View File

@ -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