all repos — mgba @ a088ad781a815f66c227b42a64dbb660880fdb52

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