all repos — mgba @ 98266d1b83a24376415f907cea2d24d0531f6166

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