all repos — mgba @ d1eda4250da99ec38e0e21d42a633667509a254f

mGBA Game Boy Advance Emulator

Thread pausing refining
Jeffrey Pfau jeffrey@endrift.com
Tue, 22 Jul 2014 01:52:16 -0700
commit

d1eda4250da99ec38e0e21d42a633667509a254f

parent

6e727db553141811d10ec9e0478c8343d6dda2cb

3 files changed, 69 insertions(+), 38 deletions(-)

jump to
M src/gba/gba-thread.csrc/gba/gba-thread.c

@@ -33,7 +33,7 @@ return TRUE;

} #endif -static void _changeState(struct GBAThread* threadContext, enum ThreadState newState, int broadcast) { +static void _changeState(struct GBAThread* threadContext, enum ThreadState newState, bool broadcast) { MutexLock(&threadContext->stateMutex); threadContext->state = newState; if (broadcast) {

@@ -48,6 +48,43 @@ ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);

} } +static void _waitUntilNotState(struct GBAThread* threadContext, enum ThreadState oldState) { + while (threadContext->state == oldState) { + MutexUnlock(&threadContext->stateMutex); + + MutexLock(&threadContext->sync.videoFrameMutex); + ConditionWake(&threadContext->sync.videoFrameRequiredCond); + MutexUnlock(&threadContext->sync.videoFrameMutex); + + MutexLock(&threadContext->sync.audioBufferMutex); + ConditionWake(&threadContext->sync.audioRequiredCond); + MutexUnlock(&threadContext->sync.audioBufferMutex); + + MutexLock(&threadContext->stateMutex); + ConditionWake(&threadContext->stateCond); + } +} + +static void _pauseThread(struct GBAThread* threadContext, bool onThread) { + if (threadContext->debugger && threadContext->debugger->state == DEBUGGER_RUNNING) { + threadContext->debugger->state = DEBUGGER_EXITING; + } + threadContext->state = THREAD_PAUSING; + if (!onThread) { + _waitUntilNotState(threadContext, THREAD_PAUSING); + } +} + +static void _changeVideoSync(struct GBAThread* threadContext, bool frameOn) { + // Make sure the video thread can process events while the GBA thread is paused + MutexLock(&threadContext->sync.videoFrameMutex); + if (frameOn != threadContext->sync.videoFrameOn) { + threadContext->sync.videoFrameOn = frameOn; + ConditionWake(&threadContext->sync.videoFrameAvailableCond); + } + MutexUnlock(&threadContext->sync.videoFrameMutex); +} + static THREAD_ENTRY _GBAThreadRun(void* context) { #ifdef USE_PTHREADS pthread_once(&_contextOnce, _createTLS);

@@ -119,14 +156,14 @@ if (threadContext->startCallback) {

threadContext->startCallback(threadContext); } - _changeState(threadContext, THREAD_RUNNING, 1); + _changeState(threadContext, THREAD_RUNNING, true); while (threadContext->state < THREAD_EXITING) { if (threadContext->debugger) { struct ARMDebugger* debugger = threadContext->debugger; ARMDebuggerRun(debugger); if (debugger->state == DEBUGGER_SHUTDOWN) { - _changeState(threadContext, THREAD_EXITING, 0); + _changeState(threadContext, THREAD_EXITING, false); } } else { while (threadContext->state == THREAD_RUNNING) {

@@ -158,7 +195,7 @@ }

} while (threadContext->state != THREAD_SHUTDOWN) { - _changeState(threadContext, THREAD_SHUTDOWN, 0); + _changeState(threadContext, THREAD_SHUTDOWN, false); } if (threadContext->cleanCallback) {

@@ -198,7 +235,7 @@ bool GBAThreadStart(struct GBAThread* threadContext) {

// TODO: error check threadContext->activeKeys = 0; threadContext->state = THREAD_INITIALIZED; - threadContext->sync.videoFrameOn = 1; + threadContext->sync.videoFrameOn = true; threadContext->sync.videoFrameSkip = 0; threadContext->rewindBufferNext = threadContext->rewindBufferInterval;

@@ -309,6 +346,7 @@ if (threadContext->debugger && threadContext->debugger->state == DEBUGGER_RUNNING) {

threadContext->debugger->state = DEBUGGER_EXITING; } threadContext->state = THREAD_EXITING; + ConditionWake(&threadContext->stateCond); MutexUnlock(&threadContext->stateMutex); MutexLock(&threadContext->sync.audioBufferMutex); threadContext->sync.audioWait = 0;

@@ -396,9 +434,7 @@ if (threadContext->debugger && threadContext->debugger->state == DEBUGGER_RUNNING) {

threadContext->debugger->state = DEBUGGER_EXITING; } ConditionWake(&threadContext->stateCond); - while (threadContext->state == THREAD_INTERRUPTING) { - ConditionWait(&threadContext->stateCond, &threadContext->stateMutex); - } + _waitUntilNotState(threadContext, THREAD_INTERRUPTING); MutexUnlock(&threadContext->stateMutex); }

@@ -407,27 +443,19 @@ _changeState(threadContext, threadContext->savedState, 1);

} void GBAThreadPause(struct GBAThread* threadContext) { - int frameOn = 1; + bool frameOn = true; MutexLock(&threadContext->stateMutex); _waitOnInterrupt(threadContext); if (threadContext->state == THREAD_RUNNING) { - if (threadContext->debugger && threadContext->debugger->state == DEBUGGER_RUNNING) { - threadContext->debugger->state = DEBUGGER_EXITING; - } - threadContext->state = THREAD_PAUSING; - frameOn = 0; + _pauseThread(threadContext, false); + frameOn = false; } MutexUnlock(&threadContext->stateMutex); - MutexLock(&threadContext->sync.videoFrameMutex); - if (frameOn != threadContext->sync.videoFrameOn) { - threadContext->sync.videoFrameOn = frameOn; - ConditionWake(&threadContext->sync.videoFrameAvailableCond); - } - MutexUnlock(&threadContext->sync.videoFrameMutex); + + _changeVideoSync(threadContext, frameOn); } void GBAThreadUnpause(struct GBAThread* threadContext) { - int frameOn = 1; MutexLock(&threadContext->stateMutex); _waitOnInterrupt(threadContext); if (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_PAUSING) {

@@ -435,12 +463,8 @@ threadContext->state = THREAD_RUNNING;

ConditionWake(&threadContext->stateCond); } MutexUnlock(&threadContext->stateMutex); - MutexLock(&threadContext->sync.videoFrameMutex); - if (frameOn != threadContext->sync.videoFrameOn) { - threadContext->sync.videoFrameOn = frameOn; - ConditionWake(&threadContext->sync.videoFrameAvailableCond); - } - MutexUnlock(&threadContext->sync.videoFrameMutex); + + _changeVideoSync(threadContext, true); } bool GBAThreadIsPaused(struct GBAThread* threadContext) {

@@ -456,23 +480,29 @@ void GBAThreadTogglePause(struct GBAThread* threadContext) {

bool frameOn = true; MutexLock(&threadContext->stateMutex); _waitOnInterrupt(threadContext); - if (threadContext->state == THREAD_PAUSED) { + if (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_PAUSING) { threadContext->state = THREAD_RUNNING; ConditionWake(&threadContext->stateCond); } else if (threadContext->state == THREAD_RUNNING) { - if (threadContext->debugger && threadContext->debugger->state == DEBUGGER_RUNNING) { - threadContext->debugger->state = DEBUGGER_EXITING; - } - threadContext->state = THREAD_PAUSED; + _pauseThread(threadContext, false); frameOn = false; } MutexUnlock(&threadContext->stateMutex); - MutexLock(&threadContext->sync.videoFrameMutex); - if (frameOn != threadContext->sync.videoFrameOn) { - threadContext->sync.videoFrameOn = frameOn; - ConditionWake(&threadContext->sync.videoFrameAvailableCond); + + _changeVideoSync(threadContext, frameOn); +} + +void GBAThreadPauseFromThread(struct GBAThread* threadContext) { + bool frameOn = true; + MutexLock(&threadContext->stateMutex); + _waitOnInterrupt(threadContext); + if (threadContext->state == THREAD_RUNNING) { + _pauseThread(threadContext, true); + frameOn = false; } - MutexUnlock(&threadContext->sync.videoFrameMutex); + MutexUnlock(&threadContext->stateMutex); + + _changeVideoSync(threadContext, frameOn); } #ifdef USE_PTHREADS
M src/gba/gba-thread.hsrc/gba/gba-thread.h

@@ -98,6 +98,7 @@ void GBAThreadPause(struct GBAThread* threadContext);

void GBAThreadUnpause(struct GBAThread* threadContext); bool GBAThreadIsPaused(struct GBAThread* threadContext); void GBAThreadTogglePause(struct GBAThread* threadContext); +void GBAThreadPauseFromThread(struct GBAThread* threadContext); struct GBAThread* GBAThreadGetContext(void); void GBASyncPostFrame(struct GBASync* sync);
M src/platform/sdl/sdl-events.csrc/platform/sdl/sdl-events.c

@@ -51,7 +51,7 @@ }

static void _pauseAfterFrame(struct GBAThread* context) { context->frameCallback = 0; - GBAThreadPause(context); + GBAThreadPauseFromThread(context); } static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents* sdlContext, const struct SDL_KeyboardEvent* event) {