all repos — mgba @ eefdcb649034ae17c83f8564a00fbb8d9242a0ee

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