all repos — mgba @ 67905d281bfecbb06f51f2ca5ac939df378734a5

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