all repos — mgba @ 4c38f769565e8ddd7d3a8eef1a41975206c129a0

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 mCoreThreadMarkCrashed(struct mCoreThread* threadContext) {
253	MutexLock(&threadContext->stateMutex);
254	threadContext->state = THREAD_CRASHED;
255	MutexUnlock(&threadContext->stateMutex);
256}
257
258void mCoreThreadEnd(struct mCoreThread* threadContext) {
259	MutexLock(&threadContext->stateMutex);
260	_waitOnInterrupt(threadContext);
261	threadContext->state = THREAD_EXITING;
262	ConditionWake(&threadContext->stateCond);
263	MutexUnlock(&threadContext->stateMutex);
264	MutexLock(&threadContext->sync.audioBufferMutex);
265	threadContext->sync.audioWait = 0;
266	ConditionWake(&threadContext->sync.audioRequiredCond);
267	MutexUnlock(&threadContext->sync.audioBufferMutex);
268
269	MutexLock(&threadContext->sync.videoFrameMutex);
270	threadContext->sync.videoFrameWait = false;
271	threadContext->sync.videoFrameOn = false;
272	ConditionWake(&threadContext->sync.videoFrameRequiredCond);
273	ConditionWake(&threadContext->sync.videoFrameAvailableCond);
274	MutexUnlock(&threadContext->sync.videoFrameMutex);
275}
276
277void mCoreThreadReset(struct mCoreThread* threadContext) {
278	MutexLock(&threadContext->stateMutex);
279	if (threadContext->state == THREAD_INTERRUPTED || threadContext->state == THREAD_INTERRUPTING) {
280		threadContext->savedState = THREAD_RESETING;
281	} else {
282		threadContext->state = THREAD_RESETING;
283	}
284	ConditionWake(&threadContext->stateCond);
285	MutexUnlock(&threadContext->stateMutex);
286}
287
288void mCoreThreadJoin(struct mCoreThread* threadContext) {
289	ThreadJoin(threadContext->thread);
290
291	MutexDeinit(&threadContext->stateMutex);
292	ConditionDeinit(&threadContext->stateCond);
293
294	MutexDeinit(&threadContext->sync.videoFrameMutex);
295	ConditionWake(&threadContext->sync.videoFrameAvailableCond);
296	ConditionDeinit(&threadContext->sync.videoFrameAvailableCond);
297	ConditionWake(&threadContext->sync.videoFrameRequiredCond);
298	ConditionDeinit(&threadContext->sync.videoFrameRequiredCond);
299
300	ConditionWake(&threadContext->sync.audioRequiredCond);
301	ConditionDeinit(&threadContext->sync.audioRequiredCond);
302	MutexDeinit(&threadContext->sync.audioBufferMutex);
303}
304
305bool mCoreThreadIsActive(struct mCoreThread* threadContext) {
306	return threadContext->state >= THREAD_RUNNING && threadContext->state < THREAD_EXITING;
307}
308
309void mCoreThreadInterrupt(struct mCoreThread* threadContext) {
310	if (!threadContext) {
311		return;
312	}
313	MutexLock(&threadContext->stateMutex);
314	++threadContext->interruptDepth;
315	if (threadContext->interruptDepth > 1 || !mCoreThreadIsActive(threadContext)) {
316		MutexUnlock(&threadContext->stateMutex);
317		return;
318	}
319	threadContext->savedState = threadContext->state;
320	_waitOnInterrupt(threadContext);
321	threadContext->state = THREAD_INTERRUPTING;
322	ConditionWake(&threadContext->stateCond);
323	_waitUntilNotState(threadContext, THREAD_INTERRUPTING);
324	MutexUnlock(&threadContext->stateMutex);
325}
326
327void mCoreThreadInterruptFromThread(struct mCoreThread* threadContext) {
328	if (!threadContext) {
329		return;
330	}
331	MutexLock(&threadContext->stateMutex);
332	++threadContext->interruptDepth;
333	if (threadContext->interruptDepth > 1 || !mCoreThreadIsActive(threadContext)) {
334		MutexUnlock(&threadContext->stateMutex);
335		return;
336	}
337	threadContext->savedState = threadContext->state;
338	threadContext->state = THREAD_INTERRUPTING;
339	ConditionWake(&threadContext->stateCond);
340	MutexUnlock(&threadContext->stateMutex);
341}
342
343void mCoreThreadContinue(struct mCoreThread* threadContext) {
344	if (!threadContext) {
345		return;
346	}
347	MutexLock(&threadContext->stateMutex);
348	--threadContext->interruptDepth;
349	if (threadContext->interruptDepth < 1 && mCoreThreadIsActive(threadContext)) {
350		threadContext->state = threadContext->savedState;
351		ConditionWake(&threadContext->stateCond);
352	}
353	MutexUnlock(&threadContext->stateMutex);
354}
355
356void mCoreThreadRunFunction(struct mCoreThread* threadContext, void (*run)(struct mCoreThread*)) {
357	MutexLock(&threadContext->stateMutex);
358	threadContext->run = run;
359	_waitOnInterrupt(threadContext);
360	threadContext->savedState = threadContext->state;
361	threadContext->state = THREAD_RUN_ON;
362	ConditionWake(&threadContext->stateCond);
363	_waitUntilNotState(threadContext, THREAD_RUN_ON);
364	MutexUnlock(&threadContext->stateMutex);
365}
366
367void mCoreThreadPause(struct mCoreThread* threadContext) {
368	bool frameOn = threadContext->sync.videoFrameOn;
369	MutexLock(&threadContext->stateMutex);
370	_waitOnInterrupt(threadContext);
371	if (threadContext->state == THREAD_RUNNING) {
372		_pauseThread(threadContext);
373		threadContext->frameWasOn = frameOn;
374		frameOn = false;
375	}
376	MutexUnlock(&threadContext->stateMutex);
377
378	mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
379}
380
381void mCoreThreadUnpause(struct mCoreThread* threadContext) {
382	bool frameOn = threadContext->sync.videoFrameOn;
383	MutexLock(&threadContext->stateMutex);
384	_waitOnInterrupt(threadContext);
385	if (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_PAUSING) {
386		threadContext->state = THREAD_RUNNING;
387		ConditionWake(&threadContext->stateCond);
388		frameOn = threadContext->frameWasOn;
389	}
390	MutexUnlock(&threadContext->stateMutex);
391
392	mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
393}
394
395bool mCoreThreadIsPaused(struct mCoreThread* threadContext) {
396	bool isPaused;
397	MutexLock(&threadContext->stateMutex);
398	if (threadContext->state == THREAD_INTERRUPTED || threadContext->state == THREAD_INTERRUPTING) {
399		isPaused = threadContext->savedState == THREAD_PAUSED;
400	} else {
401		isPaused = threadContext->state == THREAD_PAUSED;
402	}
403	MutexUnlock(&threadContext->stateMutex);
404	return isPaused;
405}
406
407void mCoreThreadTogglePause(struct mCoreThread* threadContext) {
408	bool frameOn = threadContext->sync.videoFrameOn;
409	MutexLock(&threadContext->stateMutex);
410	_waitOnInterrupt(threadContext);
411	if (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_PAUSING) {
412		threadContext->state = THREAD_RUNNING;
413		ConditionWake(&threadContext->stateCond);
414		frameOn = threadContext->frameWasOn;
415	} else if (threadContext->state == THREAD_RUNNING) {
416		_pauseThread(threadContext);
417		threadContext->frameWasOn = frameOn;
418		frameOn = false;
419	}
420	MutexUnlock(&threadContext->stateMutex);
421
422	mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
423}
424
425void mCoreThreadPauseFromThread(struct mCoreThread* threadContext) {
426	bool frameOn = true;
427	MutexLock(&threadContext->stateMutex);
428	if (threadContext->state == THREAD_RUNNING || (threadContext->state == THREAD_INTERRUPTING && threadContext->savedState == THREAD_RUNNING)) {
429		threadContext->state = THREAD_PAUSING;
430		frameOn = false;
431	}
432	MutexUnlock(&threadContext->stateMutex);
433
434	mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
435}
436
437void mCoreThreadSetRewinding(struct mCoreThread* threadContext, bool rewinding) {
438	MutexLock(&threadContext->stateMutex);
439	_waitOnInterrupt(threadContext);
440	if (rewinding && threadContext->state == THREAD_RUNNING) {
441		threadContext->state = THREAD_REWINDING;
442	}
443	if (!rewinding && threadContext->state == THREAD_REWINDING) {
444		threadContext->state = THREAD_RUNNING;
445	}
446	MutexUnlock(&threadContext->stateMutex);
447}
448
449void mCoreThreadWaitFromThread(struct mCoreThread* threadContext) {
450	MutexLock(&threadContext->stateMutex);
451	if ((threadContext->state == THREAD_INTERRUPTED || threadContext->state == THREAD_INTERRUPTING) && threadContext->savedState == THREAD_RUNNING) {
452		threadContext->savedState = THREAD_WAITING;
453	} else if (threadContext->state == THREAD_RUNNING) {
454		threadContext->state = THREAD_WAITING;
455	}
456	MutexUnlock(&threadContext->stateMutex);
457}
458
459void mCoreThreadStopWaiting(struct mCoreThread* threadContext) {
460	MutexLock(&threadContext->stateMutex);
461	if ((threadContext->state == THREAD_INTERRUPTED || threadContext->state == THREAD_INTERRUPTING) && threadContext->savedState == THREAD_WAITING) {
462		threadContext->savedState = THREAD_RUNNING;
463	} else if (threadContext->state == THREAD_WAITING) {
464		threadContext->state = THREAD_RUNNING;
465		ConditionWake(&threadContext->stateCond);
466	}
467	MutexUnlock(&threadContext->stateMutex);
468}
469
470#ifdef USE_PTHREADS
471struct mCoreThread* mCoreThreadGet(void) {
472	pthread_once(&_contextOnce, _createTLS);
473	return pthread_getspecific(_contextKey);
474}
475#elif _WIN32
476struct mCoreThread* mCoreThreadGet(void) {
477	InitOnceExecuteOnce(&_contextOnce, _createTLS, NULL, 0);
478	return TlsGetValue(_contextKey);
479}
480#else
481struct mCoreThread* mCoreThreadGet(void) {
482	return NULL;
483}
484#endif
485
486void mCoreThreadFrameStarted(struct mCoreThread* thread) {
487	if (!thread) {
488		return;
489	}
490	if (thread->core->opts.rewindEnable && thread->state != THREAD_REWINDING) {
491		mCoreRewindAppend(&thread->rewind, thread->core);
492	} else if (thread->state == THREAD_REWINDING) {
493		if (!mCoreRewindRestore(&thread->rewind, thread->core)) {
494			mCoreRewindAppend(&thread->rewind, thread->core);
495		}
496	}
497}
498
499void mCoreThreadFrameEnded(struct mCoreThread* thread) {
500	if (!thread) {
501		return;
502	}
503	if (thread->frameCallback) {
504		thread->frameCallback(thread);
505	}
506}
507
508#else
509struct mCoreThread* mCoreThreadGet(void) {
510	return NULL;
511}
512
513void mCoreThreadFrameStarted(struct mCoreThread* thread) {
514	UNUSED(thread);
515}
516
517void mCoreThreadFrameEnded(struct mCoreThread* thread) {
518	UNUSED(thread);
519}
520
521#endif
522
523static void _mCoreLog(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args) {
524	UNUSED(logger);
525	struct mCoreThread* thread = mCoreThreadGet();
526	if (thread && !(thread->logLevel & level)) {
527		return;
528	}
529	printf("%s: ", mLogCategoryName(category));
530	vprintf(format, args);
531	printf("\n");
532}
533
534struct mLogger* mCoreThreadLogger(void) {
535	struct mCoreThread* thread = mCoreThreadGet();
536	if (thread) {
537		if (!thread->logger.d.log) {
538			thread->logger.d.log = _mCoreLog;
539		}
540		return &thread->logger.d;
541	}
542	return NULL;
543}
544