mirror of https://github.com/mgba-emu/mgba.git
Thread pausing refining
This commit is contained in:
parent
6e727db553
commit
d1eda4250d
|
@ -33,7 +33,7 @@ static BOOL CALLBACK _createTLS(PINIT_ONCE once, PVOID param, PVOID* context) {
|
||||||
}
|
}
|
||||||
#endif
|
#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);
|
MutexLock(&threadContext->stateMutex);
|
||||||
threadContext->state = newState;
|
threadContext->state = newState;
|
||||||
if (broadcast) {
|
if (broadcast) {
|
||||||
|
@ -48,6 +48,43 @@ static void _waitOnInterrupt(struct GBAThread* threadContext) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
static THREAD_ENTRY _GBAThreadRun(void* context) {
|
||||||
#ifdef USE_PTHREADS
|
#ifdef USE_PTHREADS
|
||||||
pthread_once(&_contextOnce, _createTLS);
|
pthread_once(&_contextOnce, _createTLS);
|
||||||
|
@ -119,14 +156,14 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
|
||||||
threadContext->startCallback(threadContext);
|
threadContext->startCallback(threadContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
_changeState(threadContext, THREAD_RUNNING, 1);
|
_changeState(threadContext, THREAD_RUNNING, true);
|
||||||
|
|
||||||
while (threadContext->state < THREAD_EXITING) {
|
while (threadContext->state < THREAD_EXITING) {
|
||||||
if (threadContext->debugger) {
|
if (threadContext->debugger) {
|
||||||
struct ARMDebugger* debugger = threadContext->debugger;
|
struct ARMDebugger* debugger = threadContext->debugger;
|
||||||
ARMDebuggerRun(debugger);
|
ARMDebuggerRun(debugger);
|
||||||
if (debugger->state == DEBUGGER_SHUTDOWN) {
|
if (debugger->state == DEBUGGER_SHUTDOWN) {
|
||||||
_changeState(threadContext, THREAD_EXITING, 0);
|
_changeState(threadContext, THREAD_EXITING, false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
while (threadContext->state == THREAD_RUNNING) {
|
while (threadContext->state == THREAD_RUNNING) {
|
||||||
|
@ -158,7 +195,7 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
while (threadContext->state != THREAD_SHUTDOWN) {
|
while (threadContext->state != THREAD_SHUTDOWN) {
|
||||||
_changeState(threadContext, THREAD_SHUTDOWN, 0);
|
_changeState(threadContext, THREAD_SHUTDOWN, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (threadContext->cleanCallback) {
|
if (threadContext->cleanCallback) {
|
||||||
|
@ -198,7 +235,7 @@ bool GBAThreadStart(struct GBAThread* threadContext) {
|
||||||
// TODO: error check
|
// TODO: error check
|
||||||
threadContext->activeKeys = 0;
|
threadContext->activeKeys = 0;
|
||||||
threadContext->state = THREAD_INITIALIZED;
|
threadContext->state = THREAD_INITIALIZED;
|
||||||
threadContext->sync.videoFrameOn = 1;
|
threadContext->sync.videoFrameOn = true;
|
||||||
threadContext->sync.videoFrameSkip = 0;
|
threadContext->sync.videoFrameSkip = 0;
|
||||||
|
|
||||||
threadContext->rewindBufferNext = threadContext->rewindBufferInterval;
|
threadContext->rewindBufferNext = threadContext->rewindBufferInterval;
|
||||||
|
@ -309,6 +346,7 @@ void GBAThreadEnd(struct GBAThread* threadContext) {
|
||||||
threadContext->debugger->state = DEBUGGER_EXITING;
|
threadContext->debugger->state = DEBUGGER_EXITING;
|
||||||
}
|
}
|
||||||
threadContext->state = THREAD_EXITING;
|
threadContext->state = THREAD_EXITING;
|
||||||
|
ConditionWake(&threadContext->stateCond);
|
||||||
MutexUnlock(&threadContext->stateMutex);
|
MutexUnlock(&threadContext->stateMutex);
|
||||||
MutexLock(&threadContext->sync.audioBufferMutex);
|
MutexLock(&threadContext->sync.audioBufferMutex);
|
||||||
threadContext->sync.audioWait = 0;
|
threadContext->sync.audioWait = 0;
|
||||||
|
@ -396,9 +434,7 @@ void GBAThreadInterrupt(struct GBAThread* threadContext) {
|
||||||
threadContext->debugger->state = DEBUGGER_EXITING;
|
threadContext->debugger->state = DEBUGGER_EXITING;
|
||||||
}
|
}
|
||||||
ConditionWake(&threadContext->stateCond);
|
ConditionWake(&threadContext->stateCond);
|
||||||
while (threadContext->state == THREAD_INTERRUPTING) {
|
_waitUntilNotState(threadContext, THREAD_INTERRUPTING);
|
||||||
ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
|
|
||||||
}
|
|
||||||
MutexUnlock(&threadContext->stateMutex);
|
MutexUnlock(&threadContext->stateMutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -407,27 +443,19 @@ void GBAThreadContinue(struct GBAThread* threadContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAThreadPause(struct GBAThread* threadContext) {
|
void GBAThreadPause(struct GBAThread* threadContext) {
|
||||||
int frameOn = 1;
|
bool frameOn = true;
|
||||||
MutexLock(&threadContext->stateMutex);
|
MutexLock(&threadContext->stateMutex);
|
||||||
_waitOnInterrupt(threadContext);
|
_waitOnInterrupt(threadContext);
|
||||||
if (threadContext->state == THREAD_RUNNING) {
|
if (threadContext->state == THREAD_RUNNING) {
|
||||||
if (threadContext->debugger && threadContext->debugger->state == DEBUGGER_RUNNING) {
|
_pauseThread(threadContext, false);
|
||||||
threadContext->debugger->state = DEBUGGER_EXITING;
|
frameOn = false;
|
||||||
}
|
|
||||||
threadContext->state = THREAD_PAUSING;
|
|
||||||
frameOn = 0;
|
|
||||||
}
|
}
|
||||||
MutexUnlock(&threadContext->stateMutex);
|
MutexUnlock(&threadContext->stateMutex);
|
||||||
MutexLock(&threadContext->sync.videoFrameMutex);
|
|
||||||
if (frameOn != threadContext->sync.videoFrameOn) {
|
_changeVideoSync(threadContext, frameOn);
|
||||||
threadContext->sync.videoFrameOn = frameOn;
|
|
||||||
ConditionWake(&threadContext->sync.videoFrameAvailableCond);
|
|
||||||
}
|
|
||||||
MutexUnlock(&threadContext->sync.videoFrameMutex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAThreadUnpause(struct GBAThread* threadContext) {
|
void GBAThreadUnpause(struct GBAThread* threadContext) {
|
||||||
int frameOn = 1;
|
|
||||||
MutexLock(&threadContext->stateMutex);
|
MutexLock(&threadContext->stateMutex);
|
||||||
_waitOnInterrupt(threadContext);
|
_waitOnInterrupt(threadContext);
|
||||||
if (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_PAUSING) {
|
if (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_PAUSING) {
|
||||||
|
@ -435,12 +463,8 @@ void GBAThreadUnpause(struct GBAThread* threadContext) {
|
||||||
ConditionWake(&threadContext->stateCond);
|
ConditionWake(&threadContext->stateCond);
|
||||||
}
|
}
|
||||||
MutexUnlock(&threadContext->stateMutex);
|
MutexUnlock(&threadContext->stateMutex);
|
||||||
MutexLock(&threadContext->sync.videoFrameMutex);
|
|
||||||
if (frameOn != threadContext->sync.videoFrameOn) {
|
_changeVideoSync(threadContext, true);
|
||||||
threadContext->sync.videoFrameOn = frameOn;
|
|
||||||
ConditionWake(&threadContext->sync.videoFrameAvailableCond);
|
|
||||||
}
|
|
||||||
MutexUnlock(&threadContext->sync.videoFrameMutex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GBAThreadIsPaused(struct GBAThread* threadContext) {
|
bool GBAThreadIsPaused(struct GBAThread* threadContext) {
|
||||||
|
@ -456,23 +480,29 @@ void GBAThreadTogglePause(struct GBAThread* threadContext) {
|
||||||
bool frameOn = true;
|
bool frameOn = true;
|
||||||
MutexLock(&threadContext->stateMutex);
|
MutexLock(&threadContext->stateMutex);
|
||||||
_waitOnInterrupt(threadContext);
|
_waitOnInterrupt(threadContext);
|
||||||
if (threadContext->state == THREAD_PAUSED) {
|
if (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_PAUSING) {
|
||||||
threadContext->state = THREAD_RUNNING;
|
threadContext->state = THREAD_RUNNING;
|
||||||
ConditionWake(&threadContext->stateCond);
|
ConditionWake(&threadContext->stateCond);
|
||||||
} else if (threadContext->state == THREAD_RUNNING) {
|
} else if (threadContext->state == THREAD_RUNNING) {
|
||||||
if (threadContext->debugger && threadContext->debugger->state == DEBUGGER_RUNNING) {
|
_pauseThread(threadContext, false);
|
||||||
threadContext->debugger->state = DEBUGGER_EXITING;
|
|
||||||
}
|
|
||||||
threadContext->state = THREAD_PAUSED;
|
|
||||||
frameOn = false;
|
frameOn = false;
|
||||||
}
|
}
|
||||||
MutexUnlock(&threadContext->stateMutex);
|
MutexUnlock(&threadContext->stateMutex);
|
||||||
MutexLock(&threadContext->sync.videoFrameMutex);
|
|
||||||
if (frameOn != threadContext->sync.videoFrameOn) {
|
_changeVideoSync(threadContext, frameOn);
|
||||||
threadContext->sync.videoFrameOn = frameOn;
|
}
|
||||||
ConditionWake(&threadContext->sync.videoFrameAvailableCond);
|
|
||||||
|
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
|
#ifdef USE_PTHREADS
|
||||||
|
|
|
@ -98,6 +98,7 @@ void GBAThreadPause(struct GBAThread* threadContext);
|
||||||
void GBAThreadUnpause(struct GBAThread* threadContext);
|
void GBAThreadUnpause(struct GBAThread* threadContext);
|
||||||
bool GBAThreadIsPaused(struct GBAThread* threadContext);
|
bool GBAThreadIsPaused(struct GBAThread* threadContext);
|
||||||
void GBAThreadTogglePause(struct GBAThread* threadContext);
|
void GBAThreadTogglePause(struct GBAThread* threadContext);
|
||||||
|
void GBAThreadPauseFromThread(struct GBAThread* threadContext);
|
||||||
struct GBAThread* GBAThreadGetContext(void);
|
struct GBAThread* GBAThreadGetContext(void);
|
||||||
|
|
||||||
void GBASyncPostFrame(struct GBASync* sync);
|
void GBASyncPostFrame(struct GBASync* sync);
|
||||||
|
|
|
@ -51,7 +51,7 @@ enum GBAKey GBASDLMapButtonToKey(int button) {
|
||||||
|
|
||||||
static void _pauseAfterFrame(struct GBAThread* context) {
|
static void _pauseAfterFrame(struct GBAThread* context) {
|
||||||
context->frameCallback = 0;
|
context->frameCallback = 0;
|
||||||
GBAThreadPause(context);
|
GBAThreadPauseFromThread(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents* sdlContext, const struct SDL_KeyboardEvent* event) {
|
static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents* sdlContext, const struct SDL_KeyboardEvent* event) {
|
||||||
|
|
Loading…
Reference in New Issue