all repos — mgba @ 6519c4e465f42d58c297fc92e497766bfbbd5e0a

mGBA Game Boy Advance Emulator

src/core/thread.c (view raw)

  1/* Copyright (c) 2013-2016 Jeffrey Pfau
  2 *
  3 * This Source Code Form is subject to the terms of the Mozilla Public
  4 * License, v. 2.0. If a copy of the MPL was not distributed with this
  5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6#include "thread.h"
  7
  8#include "core/core.h"
  9#include "util/patch.h"
 10#include "util/vfs.h"
 11
 12#include "feature/commandline.h"
 13
 14#include <signal.h>
 15
 16#ifndef DISABLE_THREADING
 17
 18static const float _defaultFPSTarget = 60.f;
 19
 20#ifdef USE_PTHREADS
 21static pthread_key_t _contextKey;
 22static pthread_once_t _contextOnce = PTHREAD_ONCE_INIT;
 23
 24static void _createTLS(void) {
 25	pthread_key_create(&_contextKey, 0);
 26}
 27#elif _WIN32
 28static DWORD _contextKey;
 29static INIT_ONCE _contextOnce = INIT_ONCE_STATIC_INIT;
 30
 31static BOOL CALLBACK _createTLS(PINIT_ONCE once, PVOID param, PVOID* context) {
 32	UNUSED(once);
 33	UNUSED(param);
 34	UNUSED(context);
 35	_contextKey = TlsAlloc();
 36	return TRUE;
 37}
 38#endif
 39
 40static void _changeState(struct mCoreThread* threadContext, enum mCoreThreadState newState, bool broadcast) {
 41	MutexLock(&threadContext->stateMutex);
 42	threadContext->state = newState;
 43	if (broadcast) {
 44		ConditionWake(&threadContext->stateCond);
 45	}
 46	MutexUnlock(&threadContext->stateMutex);
 47}
 48
 49static void _waitOnInterrupt(struct mCoreThread* threadContext) {
 50	while (threadContext->state == THREAD_INTERRUPTED) {
 51		ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
 52	}
 53}
 54
 55static void _waitUntilNotState(struct mCoreThread* threadContext, enum mCoreThreadState oldState) {
 56	MutexLock(&threadContext->sync.videoFrameMutex);
 57	bool videoFrameWait = threadContext->sync.videoFrameWait;
 58	threadContext->sync.videoFrameWait = false;
 59	MutexUnlock(&threadContext->sync.videoFrameMutex);
 60
 61	while (threadContext->state == oldState) {
 62		MutexUnlock(&threadContext->stateMutex);
 63
 64		if (!MutexTryLock(&threadContext->sync.videoFrameMutex)) {
 65			ConditionWake(&threadContext->sync.videoFrameRequiredCond);
 66			MutexUnlock(&threadContext->sync.videoFrameMutex);
 67		}
 68
 69		if (!MutexTryLock(&threadContext->sync.audioBufferMutex)) {
 70			ConditionWake(&threadContext->sync.audioRequiredCond);
 71			MutexUnlock(&threadContext->sync.audioBufferMutex);
 72		}
 73
 74		MutexLock(&threadContext->stateMutex);
 75		ConditionWake(&threadContext->stateCond);
 76	}
 77
 78	MutexLock(&threadContext->sync.videoFrameMutex);
 79	threadContext->sync.videoFrameWait = videoFrameWait;
 80	MutexUnlock(&threadContext->sync.videoFrameMutex);
 81}
 82
 83static void _pauseThread(struct mCoreThread* threadContext, bool onThread) {
 84	threadContext->state = THREAD_PAUSING;
 85	if (!onThread) {
 86		_waitUntilNotState(threadContext, THREAD_PAUSING);
 87	}
 88}
 89
 90static THREAD_ENTRY _mCoreThreadRun(void* context) {
 91	struct mCoreThread* threadContext = context;
 92#ifdef USE_PTHREADS
 93	pthread_once(&_contextOnce, _createTLS);
 94	pthread_setspecific(_contextKey, threadContext);
 95#elif _WIN32
 96	InitOnceExecuteOnce(&_contextOnce, _createTLS, NULL, 0);
 97	TlsSetValue(_contextKey, threadContext);
 98#endif
 99
100	ThreadSetName("CPU Thread");
101
102#if !defined(_WIN32) && defined(USE_PTHREADS)
103	sigset_t signals;
104	sigemptyset(&signals);
105	pthread_sigmask(SIG_SETMASK, &signals, 0);
106#endif
107
108	struct mCore* core = threadContext->core;
109	core->setSync(core, &threadContext->sync);
110	core->reset(core);
111
112	if (core->opts.rewindEnable) {
113		 mCoreRewindContextInit(&threadContext->rewind, core->opts.rewindBufferCapacity);
114	}
115
116	_changeState(threadContext, THREAD_RUNNING, true);
117
118	if (threadContext->startCallback) {
119		threadContext->startCallback(threadContext);
120	}
121	if (threadContext->resetCallback) {
122		threadContext->resetCallback(threadContext);
123	}
124
125	while (threadContext->state < THREAD_EXITING) {
126		struct mDebugger* debugger = core->debugger;
127		if (debugger) {
128			mDebuggerRun(debugger);
129			if (debugger->state == DEBUGGER_SHUTDOWN) {
130				_changeState(threadContext, THREAD_EXITING, false);
131			}
132		} else {
133			while (threadContext->state <= THREAD_MAX_RUNNING) {
134				core->runLoop(core);
135			}
136		}
137
138		int resetScheduled = 0;
139		MutexLock(&threadContext->stateMutex);
140		while (threadContext->state > THREAD_MAX_RUNNING && threadContext->state < THREAD_EXITING) {
141			if (threadContext->state == THREAD_PAUSING) {
142				threadContext->state = THREAD_PAUSED;
143				ConditionWake(&threadContext->stateCond);
144			}
145			if (threadContext->state == THREAD_INTERRUPTING) {
146				threadContext->state = THREAD_INTERRUPTED;
147				ConditionWake(&threadContext->stateCond);
148			}
149			if (threadContext->state == THREAD_RUN_ON) {
150				if (threadContext->run) {
151					threadContext->run(threadContext);
152				}
153				threadContext->state = threadContext->savedState;
154				ConditionWake(&threadContext->stateCond);
155			}
156			if (threadContext->state == THREAD_RESETING) {
157				threadContext->state = THREAD_RUNNING;
158				resetScheduled = 1;
159			}
160			while (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_INTERRUPTED) {
161				ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
162			}
163		}
164		MutexUnlock(&threadContext->stateMutex);
165		if (resetScheduled) {
166			core->reset(core);
167			if (threadContext->resetCallback) {
168				threadContext->resetCallback(threadContext);
169			}
170		}
171	}
172
173	while (threadContext->state < THREAD_SHUTDOWN) {
174		_changeState(threadContext, THREAD_SHUTDOWN, false);
175	}
176
177	if (core->opts.rewindEnable) {
178		 mCoreRewindContextDeinit(&threadContext->rewind);
179	}
180
181	if (threadContext->cleanCallback) {
182		threadContext->cleanCallback(threadContext);
183	}
184
185	return 0;
186}
187
188bool mCoreThreadStart(struct mCoreThread* threadContext) {
189	threadContext->state = THREAD_INITIALIZED;
190	threadContext->logger.p = threadContext;
191	threadContext->logLevel = threadContext->core->opts.logLevel;
192
193	if (!threadContext->sync.fpsTarget) {
194		threadContext->sync.fpsTarget = _defaultFPSTarget;
195	}
196
197	MutexInit(&threadContext->stateMutex);
198	ConditionInit(&threadContext->stateCond);
199
200	MutexInit(&threadContext->sync.videoFrameMutex);
201	ConditionInit(&threadContext->sync.videoFrameAvailableCond);
202	ConditionInit(&threadContext->sync.videoFrameRequiredCond);
203	MutexInit(&threadContext->sync.audioBufferMutex);
204	ConditionInit(&threadContext->sync.audioRequiredCond);
205
206	threadContext->interruptDepth = 0;
207
208#ifdef USE_PTHREADS
209	sigset_t signals;
210	sigemptyset(&signals);
211	sigaddset(&signals, SIGINT);
212	sigaddset(&signals, SIGTRAP);
213	pthread_sigmask(SIG_BLOCK, &signals, 0);
214#endif
215
216	threadContext->sync.audioWait = threadContext->core->opts.audioSync;
217	threadContext->sync.videoFrameWait = threadContext->core->opts.videoSync;
218	threadContext->sync.fpsTarget = threadContext->core->opts.fpsTarget;
219
220	MutexLock(&threadContext->stateMutex);
221	ThreadCreate(&threadContext->thread, _mCoreThreadRun, threadContext);
222	while (threadContext->state < THREAD_RUNNING) {
223		ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
224	}
225	MutexUnlock(&threadContext->stateMutex);
226
227	return true;
228}
229
230bool mCoreThreadHasStarted(struct mCoreThread* threadContext) {
231	bool hasStarted;
232	MutexLock(&threadContext->stateMutex);
233	hasStarted = threadContext->state > THREAD_INITIALIZED;
234	MutexUnlock(&threadContext->stateMutex);
235	return hasStarted;
236}
237
238bool mCoreThreadHasExited(struct mCoreThread* threadContext) {
239	bool hasExited;
240	MutexLock(&threadContext->stateMutex);
241	hasExited = threadContext->state > THREAD_EXITING;
242	MutexUnlock(&threadContext->stateMutex);
243	return hasExited;
244}
245
246bool mCoreThreadHasCrashed(struct mCoreThread* threadContext) {
247	bool hasExited;
248	MutexLock(&threadContext->stateMutex);
249	hasExited = threadContext->state == THREAD_CRASHED;
250	MutexUnlock(&threadContext->stateMutex);
251	return hasExited;
252}
253
254void mCoreThreadEnd(struct mCoreThread* threadContext) {
255	MutexLock(&threadContext->stateMutex);
256	_waitOnInterrupt(threadContext);
257	threadContext->state = THREAD_EXITING;
258	ConditionWake(&threadContext->stateCond);
259	MutexUnlock(&threadContext->stateMutex);
260	MutexLock(&threadContext->sync.audioBufferMutex);
261	threadContext->sync.audioWait = 0;
262	ConditionWake(&threadContext->sync.audioRequiredCond);
263	MutexUnlock(&threadContext->sync.audioBufferMutex);
264
265	MutexLock(&threadContext->sync.videoFrameMutex);
266	threadContext->sync.videoFrameWait = false;
267	threadContext->sync.videoFrameOn = false;
268	ConditionWake(&threadContext->sync.videoFrameRequiredCond);
269	ConditionWake(&threadContext->sync.videoFrameAvailableCond);
270	MutexUnlock(&threadContext->sync.videoFrameMutex);
271}
272
273void mCoreThreadReset(struct mCoreThread* threadContext) {
274	MutexLock(&threadContext->stateMutex);
275	if (threadContext->state == THREAD_INTERRUPTED) {
276		threadContext->savedState = THREAD_RESETING;
277	} else {
278		threadContext->state = THREAD_RESETING;
279	}
280	ConditionWake(&threadContext->stateCond);
281	MutexUnlock(&threadContext->stateMutex);
282}
283
284void mCoreThreadJoin(struct mCoreThread* threadContext) {
285	ThreadJoin(threadContext->thread);
286
287	MutexDeinit(&threadContext->stateMutex);
288	ConditionDeinit(&threadContext->stateCond);
289
290	MutexDeinit(&threadContext->sync.videoFrameMutex);
291	ConditionWake(&threadContext->sync.videoFrameAvailableCond);
292	ConditionDeinit(&threadContext->sync.videoFrameAvailableCond);
293	ConditionWake(&threadContext->sync.videoFrameRequiredCond);
294	ConditionDeinit(&threadContext->sync.videoFrameRequiredCond);
295
296	ConditionWake(&threadContext->sync.audioRequiredCond);
297	ConditionDeinit(&threadContext->sync.audioRequiredCond);
298	MutexDeinit(&threadContext->sync.audioBufferMutex);
299}
300
301bool mCoreThreadIsActive(struct mCoreThread* threadContext) {
302	return threadContext->state >= THREAD_RUNNING && threadContext->state < THREAD_EXITING;
303}
304
305void mCoreThreadInterrupt(struct mCoreThread* threadContext) {
306	if (!threadContext) {
307		return;
308	}
309	MutexLock(&threadContext->stateMutex);
310	++threadContext->interruptDepth;
311	if (threadContext->interruptDepth > 1 || !mCoreThreadIsActive(threadContext)) {
312		MutexUnlock(&threadContext->stateMutex);
313		return;
314	}
315	threadContext->savedState = threadContext->state;
316	_waitOnInterrupt(threadContext);
317	threadContext->state = THREAD_INTERRUPTING;
318	ConditionWake(&threadContext->stateCond);
319	_waitUntilNotState(threadContext, THREAD_INTERRUPTING);
320	MutexUnlock(&threadContext->stateMutex);
321}
322
323void mCoreThreadInterruptFromThread(struct mCoreThread* threadContext) {
324	if (!threadContext) {
325		return;
326	}
327	MutexLock(&threadContext->stateMutex);
328	++threadContext->interruptDepth;
329	if (threadContext->interruptDepth > 1 || !mCoreThreadIsActive(threadContext)) {
330		MutexUnlock(&threadContext->stateMutex);
331		return;
332	}
333	threadContext->savedState = threadContext->state;
334	threadContext->state = THREAD_INTERRUPTING;
335	ConditionWake(&threadContext->stateCond);
336	MutexUnlock(&threadContext->stateMutex);
337}
338
339void mCoreThreadContinue(struct mCoreThread* threadContext) {
340	if (!threadContext) {
341		return;
342	}
343	MutexLock(&threadContext->stateMutex);
344	--threadContext->interruptDepth;
345	if (threadContext->interruptDepth < 1 && mCoreThreadIsActive(threadContext)) {
346		threadContext->state = threadContext->savedState;
347		ConditionWake(&threadContext->stateCond);
348	}
349	MutexUnlock(&threadContext->stateMutex);
350}
351
352void mCoreThreadRunFunction(struct mCoreThread* threadContext, void (*run)(struct mCoreThread*)) {
353	MutexLock(&threadContext->stateMutex);
354	threadContext->run = run;
355	_waitOnInterrupt(threadContext);
356	threadContext->savedState = threadContext->state;
357	threadContext->state = THREAD_RUN_ON;
358	ConditionWake(&threadContext->stateCond);
359	_waitUntilNotState(threadContext, THREAD_RUN_ON);
360	MutexUnlock(&threadContext->stateMutex);
361}
362
363void mCoreThreadPause(struct mCoreThread* threadContext) {
364	bool frameOn = threadContext->sync.videoFrameOn;
365	MutexLock(&threadContext->stateMutex);
366	_waitOnInterrupt(threadContext);
367	if (threadContext->state == THREAD_RUNNING) {
368		_pauseThread(threadContext, false);
369		threadContext->frameWasOn = frameOn;
370		frameOn = false;
371	}
372	MutexUnlock(&threadContext->stateMutex);
373
374	mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
375}
376
377void mCoreThreadUnpause(struct mCoreThread* threadContext) {
378	bool frameOn = threadContext->sync.videoFrameOn;
379	MutexLock(&threadContext->stateMutex);
380	_waitOnInterrupt(threadContext);
381	if (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_PAUSING) {
382		threadContext->state = THREAD_RUNNING;
383		ConditionWake(&threadContext->stateCond);
384		frameOn = threadContext->frameWasOn;
385	}
386	MutexUnlock(&threadContext->stateMutex);
387
388	mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
389}
390
391bool mCoreThreadIsPaused(struct mCoreThread* threadContext) {
392	bool isPaused;
393	MutexLock(&threadContext->stateMutex);
394	if (threadContext->state == THREAD_INTERRUPTED) {
395		isPaused = threadContext->savedState == THREAD_PAUSED;
396	} else {
397		isPaused = threadContext->state == THREAD_PAUSED;
398	}
399	MutexUnlock(&threadContext->stateMutex);
400	return isPaused;
401}
402
403void mCoreThreadTogglePause(struct mCoreThread* threadContext) {
404	bool frameOn = threadContext->sync.videoFrameOn;
405	MutexLock(&threadContext->stateMutex);
406	_waitOnInterrupt(threadContext);
407	if (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_PAUSING) {
408		threadContext->state = THREAD_RUNNING;
409		ConditionWake(&threadContext->stateCond);
410		frameOn = threadContext->frameWasOn;
411	} else if (threadContext->state == THREAD_RUNNING) {
412		_pauseThread(threadContext, false);
413		threadContext->frameWasOn = frameOn;
414		frameOn = false;
415	}
416	MutexUnlock(&threadContext->stateMutex);
417
418	mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
419}
420
421void mCoreThreadPauseFromThread(struct mCoreThread* threadContext) {
422	bool frameOn = true;
423	MutexLock(&threadContext->stateMutex);
424	if (threadContext->state == THREAD_RUNNING) {
425		_pauseThread(threadContext, true);
426		frameOn = false;
427	}
428	MutexUnlock(&threadContext->stateMutex);
429
430	mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
431}
432
433void mCoreThreadSetRewinding(struct mCoreThread* threadContext, bool rewinding) {
434	MutexLock(&threadContext->stateMutex);
435	_waitOnInterrupt(threadContext);
436	if (rewinding && threadContext->state == THREAD_RUNNING) {
437		threadContext->state = THREAD_REWINDING;
438	}
439	if (!rewinding && threadContext->state == THREAD_REWINDING) {
440		threadContext->state = THREAD_RUNNING;
441	}
442	MutexUnlock(&threadContext->stateMutex);
443}
444
445#ifdef USE_PTHREADS
446struct mCoreThread* mCoreThreadGet(void) {
447	pthread_once(&_contextOnce, _createTLS);
448	return pthread_getspecific(_contextKey);
449}
450#elif _WIN32
451struct mCoreThread* mCoreThreadGet(void) {
452	InitOnceExecuteOnce(&_contextOnce, _createTLS, NULL, 0);
453	return TlsGetValue(_contextKey);
454}
455#else
456struct mCoreThread* mCoreThreadGet(void) {
457	return NULL;
458}
459#endif
460
461void mCoreThreadFrameStarted(struct mCoreThread* thread) {
462	if (!thread) {
463		return;
464	}
465	if (thread->core->opts.rewindEnable && thread->state != THREAD_REWINDING) {
466		mCoreRewindAppend(&thread->rewind, thread->core);
467	} else if (thread->state == THREAD_REWINDING) {
468		if (!mCoreRewindRestore(&thread->rewind, thread->core)) {
469			mCoreRewindAppend(&thread->rewind, thread->core);
470		}
471	}
472}
473
474void mCoreThreadFrameEnded(struct mCoreThread* thread) {
475	if (!thread) {
476		return;
477	}
478	if (thread->frameCallback) {
479		thread->frameCallback(thread);
480	}
481}
482
483#else
484struct mCoreThread* mCoreThreadGet(void) {
485	return NULL;
486}
487
488void mCoreThreadFrameStarted(struct mCoreThread* thread) {
489	UNUSED(thread);
490}
491
492void mCoreThreadFrameEnded(struct mCoreThread* thread) {
493	UNUSED(thread);
494}
495
496#endif
497
498static void _mCoreLog(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args) {
499	UNUSED(logger);
500	struct mCoreThread* thread = mCoreThreadGet();
501	if (thread && !(thread->logLevel & level)) {
502		return;
503	}
504	printf("%s: ", mLogCategoryName(category));
505	vprintf(format, args);
506	printf("\n");
507}
508
509struct mLogger* mCoreThreadLogger(void) {
510	struct mCoreThread* thread = mCoreThreadGet();
511	if (thread) {
512		if (!thread->logger.d.log) {
513			thread->logger.d.log = _mCoreLog;
514		}
515		return &thread->logger.d;
516	}
517	return NULL;
518}
519