all repos — mgba @ f628f5bafcb5dedcbaa6a56c1e41cc086f2ceafa

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