all repos — mgba @ 5646ba7d606a5da1155be568946f80602a1c2e72

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