all repos — mgba @ 2e0ee682a589c08d965a57b70c772141e8ca042f

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