all repos — mgba @ ab44084236a244751d4ecd8e6403d58fb7c38de2

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