all repos — mgba @ 5b50c43857a5aa5bcefc7b339648bfc73d74fc2f

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