all repos — mgba @ fa884d071ecaa3e05ff20b45a67bf9500dd3d6b6

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