all repos — mgba @ 33a4c45f3ff72c83a3f68b5f4e5730764018a743

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	MutexLock(&threadContext->stateMutex);
166	ThreadCreate(&threadContext->thread, _mCoreThreadRun, threadContext);
167	while (threadContext->state < THREAD_RUNNING) {
168		ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
169	}
170	MutexUnlock(&threadContext->stateMutex);
171
172	return true;
173}
174
175bool mCoreThreadHasStarted(struct mCoreThread* threadContext) {
176	bool hasStarted;
177	MutexLock(&threadContext->stateMutex);
178	hasStarted = threadContext->state > THREAD_INITIALIZED;
179	MutexUnlock(&threadContext->stateMutex);
180	return hasStarted;
181}
182
183bool mCoreThreadHasExited(struct mCoreThread* threadContext) {
184	bool hasExited;
185	MutexLock(&threadContext->stateMutex);
186	hasExited = threadContext->state > THREAD_EXITING;
187	MutexUnlock(&threadContext->stateMutex);
188	return hasExited;
189}
190
191bool mCoreThreadHasCrashed(struct mCoreThread* threadContext) {
192	bool hasExited;
193	MutexLock(&threadContext->stateMutex);
194	hasExited = threadContext->state == THREAD_CRASHED;
195	MutexUnlock(&threadContext->stateMutex);
196	return hasExited;
197}
198
199void mCoreThreadEnd(struct mCoreThread* threadContext) {
200	MutexLock(&threadContext->stateMutex);
201	_waitOnInterrupt(threadContext);
202	threadContext->state = THREAD_EXITING;
203	ConditionWake(&threadContext->stateCond);
204	MutexUnlock(&threadContext->stateMutex);
205	MutexLock(&threadContext->sync.audioBufferMutex);
206	threadContext->sync.audioWait = 0;
207	ConditionWake(&threadContext->sync.audioRequiredCond);
208	MutexUnlock(&threadContext->sync.audioBufferMutex);
209
210	MutexLock(&threadContext->sync.videoFrameMutex);
211	threadContext->sync.videoFrameWait = false;
212	threadContext->sync.videoFrameOn = false;
213	ConditionWake(&threadContext->sync.videoFrameRequiredCond);
214	ConditionWake(&threadContext->sync.videoFrameAvailableCond);
215	MutexUnlock(&threadContext->sync.videoFrameMutex);
216}
217
218void mCoreThreadReset(struct mCoreThread* threadContext) {
219	MutexLock(&threadContext->stateMutex);
220	_waitOnInterrupt(threadContext);
221	threadContext->state = THREAD_RESETING;
222	ConditionWake(&threadContext->stateCond);
223	MutexUnlock(&threadContext->stateMutex);
224}
225
226void mCoreThreadJoin(struct mCoreThread* threadContext) {
227	ThreadJoin(threadContext->thread);
228
229	MutexDeinit(&threadContext->stateMutex);
230	ConditionDeinit(&threadContext->stateCond);
231
232	MutexDeinit(&threadContext->sync.videoFrameMutex);
233	ConditionWake(&threadContext->sync.videoFrameAvailableCond);
234	ConditionDeinit(&threadContext->sync.videoFrameAvailableCond);
235	ConditionWake(&threadContext->sync.videoFrameRequiredCond);
236	ConditionDeinit(&threadContext->sync.videoFrameRequiredCond);
237
238	ConditionWake(&threadContext->sync.audioRequiredCond);
239	ConditionDeinit(&threadContext->sync.audioRequiredCond);
240	MutexDeinit(&threadContext->sync.audioBufferMutex);
241}
242
243bool mCoreThreadIsActive(struct mCoreThread* threadContext) {
244	return threadContext->state >= THREAD_RUNNING && threadContext->state < THREAD_EXITING;
245}
246
247void mCoreThreadInterrupt(struct mCoreThread* threadContext) {
248	if (!threadContext) {
249		return;
250	}
251	MutexLock(&threadContext->stateMutex);
252	++threadContext->interruptDepth;
253	if (threadContext->interruptDepth > 1 || !mCoreThreadIsActive(threadContext)) {
254		MutexUnlock(&threadContext->stateMutex);
255		return;
256	}
257	threadContext->savedState = threadContext->state;
258	_waitOnInterrupt(threadContext);
259	threadContext->state = THREAD_INTERRUPTING;
260	ConditionWake(&threadContext->stateCond);
261	_waitUntilNotState(threadContext, THREAD_INTERRUPTING);
262	MutexUnlock(&threadContext->stateMutex);
263}
264
265void mCoreThreadContinue(struct mCoreThread* threadContext) {
266	if (!threadContext) {
267		return;
268	}
269	MutexLock(&threadContext->stateMutex);
270	--threadContext->interruptDepth;
271	if (threadContext->interruptDepth < 1 && mCoreThreadIsActive(threadContext)) {
272		threadContext->state = threadContext->savedState;
273		ConditionWake(&threadContext->stateCond);
274	}
275	MutexUnlock(&threadContext->stateMutex);
276}
277
278void mCoreThreadRunFunction(struct mCoreThread* threadContext, void (*run)(struct mCoreThread*)) {
279	MutexLock(&threadContext->stateMutex);
280	threadContext->run = run;
281	_waitOnInterrupt(threadContext);
282	threadContext->savedState = threadContext->state;
283	threadContext->state = THREAD_RUN_ON;
284	ConditionWake(&threadContext->stateCond);
285	_waitUntilNotState(threadContext, THREAD_RUN_ON);
286	MutexUnlock(&threadContext->stateMutex);
287}
288
289void mCoreThreadPause(struct mCoreThread* threadContext) {
290	bool frameOn = threadContext->sync.videoFrameOn;
291	MutexLock(&threadContext->stateMutex);
292	_waitOnInterrupt(threadContext);
293	if (threadContext->state == THREAD_RUNNING) {
294		_pauseThread(threadContext, false);
295		threadContext->frameWasOn = frameOn;
296		frameOn = false;
297	}
298	MutexUnlock(&threadContext->stateMutex);
299
300	mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
301}
302
303void mCoreThreadUnpause(struct mCoreThread* threadContext) {
304	bool frameOn = threadContext->sync.videoFrameOn;
305	MutexLock(&threadContext->stateMutex);
306	_waitOnInterrupt(threadContext);
307	if (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_PAUSING) {
308		threadContext->state = THREAD_RUNNING;
309		ConditionWake(&threadContext->stateCond);
310		frameOn = threadContext->frameWasOn;
311	}
312	MutexUnlock(&threadContext->stateMutex);
313
314	mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
315}
316
317bool mCoreThreadIsPaused(struct mCoreThread* threadContext) {
318	bool isPaused;
319	MutexLock(&threadContext->stateMutex);
320	_waitOnInterrupt(threadContext);
321	isPaused = threadContext->state == THREAD_PAUSED;
322	MutexUnlock(&threadContext->stateMutex);
323	return isPaused;
324}
325
326void mCoreThreadTogglePause(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	} else if (threadContext->state == THREAD_RUNNING) {
335		_pauseThread(threadContext, false);
336		threadContext->frameWasOn = frameOn;
337		frameOn = false;
338	}
339	MutexUnlock(&threadContext->stateMutex);
340
341	mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
342}
343
344void mCoreThreadPauseFromThread(struct mCoreThread* threadContext) {
345	bool frameOn = true;
346	MutexLock(&threadContext->stateMutex);
347	_waitOnInterrupt(threadContext);
348	if (threadContext->state == THREAD_RUNNING) {
349		_pauseThread(threadContext, true);
350		frameOn = false;
351	}
352	MutexUnlock(&threadContext->stateMutex);
353
354	mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
355}
356
357#ifdef USE_PTHREADS
358struct mCoreThread* mCoreThreadGet(void) {
359	pthread_once(&_contextOnce, _createTLS);
360	return pthread_getspecific(_contextKey);
361}
362#elif _WIN32
363struct mCoreThread* mCoreThreadGet(void) {
364	InitOnceExecuteOnce(&_contextOnce, _createTLS, NULL, 0);
365	return TlsGetValue(_contextKey);
366}
367#endif
368
369#else
370struct mCoreThread* mCoreThreadGet(void) {
371	return NULL;
372}
373#endif
374
375static void _mCoreLog(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args) {
376	printf("%s: ", mLogCategoryName(category));
377	vprintf(format, args);
378	printf("\n");
379
380}
381
382struct mLogger* mCoreThreadLogger(void) {
383	struct mCoreThread* thread = mCoreThreadGet();
384	if (thread) {
385		if (!thread->logger.log) {
386			thread->logger.log = _mCoreLog;
387		}
388		return &thread->logger;
389	}
390	return NULL;
391}
392