all repos — mgba @ f0ea421feada3a4960ed37e32cb7096103e275ab

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