all repos — mgba @ 5f1011d4744195d40685b94f78df0ad0e2158974

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 "feature/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 || threadContext->state == THREAD_INTERRUPTING) {
 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) {
 84	threadContext->state = THREAD_PAUSING;
 85	_waitUntilNotState(threadContext, THREAD_PAUSING);
 86}
 87
 88void _frameStarted(void* context) {
 89	struct mCoreThread* thread = context;
 90	if (!thread) {
 91		return;
 92	}
 93	if (thread->core->opts.rewindEnable && thread->core->opts.rewindBufferCapacity > 0) {
 94		if (thread->state != THREAD_REWINDING) {
 95			mCoreRewindAppend(&thread->rewind, thread->core);
 96		} else if (thread->state == THREAD_REWINDING) {
 97			if (!mCoreRewindRestore(&thread->rewind, thread->core)) {
 98				mCoreRewindAppend(&thread->rewind, thread->core);
 99			}
100		}
101	}
102}
103
104void _frameEnded(void* context) {
105	struct mCoreThread* thread = context;
106	if (!thread) {
107		return;
108	}
109	if (thread->frameCallback) {
110		thread->frameCallback(thread);
111	}
112}
113
114void _crashed(void* context) {
115	struct mCoreThread* thread = context;
116	if (!thread) {
117		return;
118	}
119	_changeState(thread, THREAD_CRASHED, true);
120}
121
122static THREAD_ENTRY _mCoreThreadRun(void* context) {
123	struct mCoreThread* threadContext = context;
124#ifdef USE_PTHREADS
125	pthread_once(&_contextOnce, _createTLS);
126	pthread_setspecific(_contextKey, threadContext);
127#elif _WIN32
128	InitOnceExecuteOnce(&_contextOnce, _createTLS, NULL, 0);
129	TlsSetValue(_contextKey, threadContext);
130#endif
131
132	ThreadSetName("CPU Thread");
133
134#if !defined(_WIN32) && defined(USE_PTHREADS)
135	sigset_t signals;
136	sigemptyset(&signals);
137	pthread_sigmask(SIG_SETMASK, &signals, 0);
138#endif
139
140	struct mCore* core = threadContext->core;
141	struct mCoreCallbacks callbacks = {
142		.videoFrameStarted = _frameStarted,
143		.videoFrameEnded = _frameEnded,
144		.coreCrashed = _crashed,
145		.context = threadContext
146	};
147	core->setCoreCallbacks(core, &callbacks);
148	core->setSync(core, &threadContext->sync);
149	core->reset(core);
150
151	if (core->opts.rewindEnable && core->opts.rewindBufferCapacity > 0) {
152		 mCoreRewindContextInit(&threadContext->rewind, core->opts.rewindBufferCapacity);
153	}
154
155	_changeState(threadContext, THREAD_RUNNING, true);
156
157	if (threadContext->startCallback) {
158		threadContext->startCallback(threadContext);
159	}
160	if (threadContext->resetCallback) {
161		threadContext->resetCallback(threadContext);
162	}
163
164	while (threadContext->state < THREAD_EXITING) {
165		struct mDebugger* debugger = core->debugger;
166		if (debugger) {
167			mDebuggerRun(debugger);
168			if (debugger->state == DEBUGGER_SHUTDOWN) {
169				_changeState(threadContext, THREAD_EXITING, false);
170			}
171		} else {
172			while (threadContext->state <= THREAD_MAX_RUNNING) {
173				core->runLoop(core);
174			}
175		}
176
177		int resetScheduled = 0;
178		MutexLock(&threadContext->stateMutex);
179		while (threadContext->state > THREAD_MAX_RUNNING && threadContext->state < THREAD_EXITING) {
180			if (threadContext->state == THREAD_PAUSING) {
181				threadContext->state = THREAD_PAUSED;
182				ConditionWake(&threadContext->stateCond);
183			}
184			if (threadContext->state == THREAD_INTERRUPTING) {
185				threadContext->state = THREAD_INTERRUPTED;
186				ConditionWake(&threadContext->stateCond);
187			}
188			if (threadContext->state == THREAD_RUN_ON) {
189				if (threadContext->run) {
190					threadContext->run(threadContext);
191				}
192				threadContext->state = threadContext->savedState;
193				ConditionWake(&threadContext->stateCond);
194			}
195			if (threadContext->state == THREAD_RESETING) {
196				threadContext->state = THREAD_RUNNING;
197				resetScheduled = 1;
198			}
199			while (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_INTERRUPTED || threadContext->state == THREAD_WAITING) {
200				ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
201			}
202		}
203		MutexUnlock(&threadContext->stateMutex);
204		if (resetScheduled) {
205			core->reset(core);
206			if (threadContext->resetCallback) {
207				threadContext->resetCallback(threadContext);
208			}
209		}
210	}
211
212	while (threadContext->state < THREAD_SHUTDOWN) {
213		_changeState(threadContext, THREAD_SHUTDOWN, false);
214	}
215
216	if (core->opts.rewindEnable) {
217		 mCoreRewindContextDeinit(&threadContext->rewind);
218	}
219
220	if (threadContext->cleanCallback) {
221		threadContext->cleanCallback(threadContext);
222	}
223	core->setCoreCallbacks(core, NULL);
224
225	return 0;
226}
227
228bool mCoreThreadStart(struct mCoreThread* threadContext) {
229	threadContext->state = THREAD_INITIALIZED;
230	threadContext->logger.p = threadContext;
231	threadContext->logLevel = threadContext->core->opts.logLevel;
232
233	if (!threadContext->sync.fpsTarget) {
234		threadContext->sync.fpsTarget = _defaultFPSTarget;
235	}
236
237	MutexInit(&threadContext->stateMutex);
238	ConditionInit(&threadContext->stateCond);
239
240	MutexInit(&threadContext->sync.videoFrameMutex);
241	ConditionInit(&threadContext->sync.videoFrameAvailableCond);
242	ConditionInit(&threadContext->sync.videoFrameRequiredCond);
243	MutexInit(&threadContext->sync.audioBufferMutex);
244	ConditionInit(&threadContext->sync.audioRequiredCond);
245
246	threadContext->interruptDepth = 0;
247
248#ifdef USE_PTHREADS
249	sigset_t signals;
250	sigemptyset(&signals);
251	sigaddset(&signals, SIGINT);
252	sigaddset(&signals, SIGTRAP);
253	pthread_sigmask(SIG_BLOCK, &signals, 0);
254#endif
255
256	threadContext->sync.audioWait = threadContext->core->opts.audioSync;
257	threadContext->sync.videoFrameWait = threadContext->core->opts.videoSync;
258	threadContext->sync.fpsTarget = threadContext->core->opts.fpsTarget;
259
260	MutexLock(&threadContext->stateMutex);
261	ThreadCreate(&threadContext->thread, _mCoreThreadRun, threadContext);
262	while (threadContext->state < THREAD_RUNNING) {
263		ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
264	}
265	MutexUnlock(&threadContext->stateMutex);
266
267	return true;
268}
269
270bool mCoreThreadHasStarted(struct mCoreThread* threadContext) {
271	bool hasStarted;
272	MutexLock(&threadContext->stateMutex);
273	hasStarted = threadContext->state > THREAD_INITIALIZED;
274	MutexUnlock(&threadContext->stateMutex);
275	return hasStarted;
276}
277
278bool mCoreThreadHasExited(struct mCoreThread* threadContext) {
279	bool hasExited;
280	MutexLock(&threadContext->stateMutex);
281	hasExited = threadContext->state > THREAD_EXITING;
282	MutexUnlock(&threadContext->stateMutex);
283	return hasExited;
284}
285
286bool mCoreThreadHasCrashed(struct mCoreThread* threadContext) {
287	bool hasExited;
288	MutexLock(&threadContext->stateMutex);
289	hasExited = threadContext->state == THREAD_CRASHED;
290	MutexUnlock(&threadContext->stateMutex);
291	return hasExited;
292}
293
294void mCoreThreadMarkCrashed(struct mCoreThread* threadContext) {
295	MutexLock(&threadContext->stateMutex);
296	threadContext->state = THREAD_CRASHED;
297	MutexUnlock(&threadContext->stateMutex);
298}
299
300void mCoreThreadEnd(struct mCoreThread* threadContext) {
301	MutexLock(&threadContext->stateMutex);
302	_waitOnInterrupt(threadContext);
303	threadContext->state = THREAD_EXITING;
304	ConditionWake(&threadContext->stateCond);
305	MutexUnlock(&threadContext->stateMutex);
306	MutexLock(&threadContext->sync.audioBufferMutex);
307	threadContext->sync.audioWait = 0;
308	ConditionWake(&threadContext->sync.audioRequiredCond);
309	MutexUnlock(&threadContext->sync.audioBufferMutex);
310
311	MutexLock(&threadContext->sync.videoFrameMutex);
312	threadContext->sync.videoFrameWait = false;
313	threadContext->sync.videoFrameOn = false;
314	ConditionWake(&threadContext->sync.videoFrameRequiredCond);
315	ConditionWake(&threadContext->sync.videoFrameAvailableCond);
316	MutexUnlock(&threadContext->sync.videoFrameMutex);
317}
318
319void mCoreThreadReset(struct mCoreThread* threadContext) {
320	MutexLock(&threadContext->stateMutex);
321	if (threadContext->state == THREAD_INTERRUPTED || threadContext->state == THREAD_INTERRUPTING) {
322		threadContext->savedState = THREAD_RESETING;
323	} else {
324		threadContext->state = THREAD_RESETING;
325	}
326	ConditionWake(&threadContext->stateCond);
327	MutexUnlock(&threadContext->stateMutex);
328}
329
330void mCoreThreadJoin(struct mCoreThread* threadContext) {
331	ThreadJoin(threadContext->thread);
332
333	MutexDeinit(&threadContext->stateMutex);
334	ConditionDeinit(&threadContext->stateCond);
335
336	MutexDeinit(&threadContext->sync.videoFrameMutex);
337	ConditionWake(&threadContext->sync.videoFrameAvailableCond);
338	ConditionDeinit(&threadContext->sync.videoFrameAvailableCond);
339	ConditionWake(&threadContext->sync.videoFrameRequiredCond);
340	ConditionDeinit(&threadContext->sync.videoFrameRequiredCond);
341
342	ConditionWake(&threadContext->sync.audioRequiredCond);
343	ConditionDeinit(&threadContext->sync.audioRequiredCond);
344	MutexDeinit(&threadContext->sync.audioBufferMutex);
345}
346
347bool mCoreThreadIsActive(struct mCoreThread* threadContext) {
348	return threadContext->state >= THREAD_RUNNING && threadContext->state < THREAD_EXITING;
349}
350
351void mCoreThreadInterrupt(struct mCoreThread* threadContext) {
352	if (!threadContext) {
353		return;
354	}
355	MutexLock(&threadContext->stateMutex);
356	++threadContext->interruptDepth;
357	if (threadContext->interruptDepth > 1 || !mCoreThreadIsActive(threadContext)) {
358		MutexUnlock(&threadContext->stateMutex);
359		return;
360	}
361	threadContext->savedState = threadContext->state;
362	_waitOnInterrupt(threadContext);
363	threadContext->state = THREAD_INTERRUPTING;
364	ConditionWake(&threadContext->stateCond);
365	_waitUntilNotState(threadContext, THREAD_INTERRUPTING);
366	MutexUnlock(&threadContext->stateMutex);
367}
368
369void mCoreThreadInterruptFromThread(struct mCoreThread* threadContext) {
370	if (!threadContext) {
371		return;
372	}
373	MutexLock(&threadContext->stateMutex);
374	++threadContext->interruptDepth;
375	if (threadContext->interruptDepth > 1 || !mCoreThreadIsActive(threadContext)) {
376		MutexUnlock(&threadContext->stateMutex);
377		return;
378	}
379	threadContext->savedState = threadContext->state;
380	threadContext->state = THREAD_INTERRUPTING;
381	ConditionWake(&threadContext->stateCond);
382	MutexUnlock(&threadContext->stateMutex);
383}
384
385void mCoreThreadContinue(struct mCoreThread* threadContext) {
386	if (!threadContext) {
387		return;
388	}
389	MutexLock(&threadContext->stateMutex);
390	--threadContext->interruptDepth;
391	if (threadContext->interruptDepth < 1 && mCoreThreadIsActive(threadContext)) {
392		threadContext->state = threadContext->savedState;
393		ConditionWake(&threadContext->stateCond);
394	}
395	MutexUnlock(&threadContext->stateMutex);
396}
397
398void mCoreThreadRunFunction(struct mCoreThread* threadContext, void (*run)(struct mCoreThread*)) {
399	MutexLock(&threadContext->stateMutex);
400	threadContext->run = run;
401	_waitOnInterrupt(threadContext);
402	threadContext->savedState = threadContext->state;
403	threadContext->state = THREAD_RUN_ON;
404	ConditionWake(&threadContext->stateCond);
405	_waitUntilNotState(threadContext, THREAD_RUN_ON);
406	MutexUnlock(&threadContext->stateMutex);
407}
408
409void mCoreThreadPause(struct mCoreThread* threadContext) {
410	bool frameOn = threadContext->sync.videoFrameOn;
411	MutexLock(&threadContext->stateMutex);
412	_waitOnInterrupt(threadContext);
413	if (threadContext->state == THREAD_RUNNING) {
414		_pauseThread(threadContext);
415		threadContext->frameWasOn = frameOn;
416		frameOn = false;
417	}
418	MutexUnlock(&threadContext->stateMutex);
419
420	mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
421}
422
423void mCoreThreadUnpause(struct mCoreThread* threadContext) {
424	bool frameOn = threadContext->sync.videoFrameOn;
425	MutexLock(&threadContext->stateMutex);
426	_waitOnInterrupt(threadContext);
427	if (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_PAUSING) {
428		threadContext->state = THREAD_RUNNING;
429		ConditionWake(&threadContext->stateCond);
430		frameOn = threadContext->frameWasOn;
431	}
432	MutexUnlock(&threadContext->stateMutex);
433
434	mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
435}
436
437bool mCoreThreadIsPaused(struct mCoreThread* threadContext) {
438	bool isPaused;
439	MutexLock(&threadContext->stateMutex);
440	if (threadContext->state == THREAD_INTERRUPTED || threadContext->state == THREAD_INTERRUPTING) {
441		isPaused = threadContext->savedState == THREAD_PAUSED;
442	} else {
443		isPaused = threadContext->state == THREAD_PAUSED;
444	}
445	MutexUnlock(&threadContext->stateMutex);
446	return isPaused;
447}
448
449void mCoreThreadTogglePause(struct mCoreThread* threadContext) {
450	bool frameOn = threadContext->sync.videoFrameOn;
451	MutexLock(&threadContext->stateMutex);
452	_waitOnInterrupt(threadContext);
453	if (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_PAUSING) {
454		threadContext->state = THREAD_RUNNING;
455		ConditionWake(&threadContext->stateCond);
456		frameOn = threadContext->frameWasOn;
457	} else if (threadContext->state == THREAD_RUNNING) {
458		_pauseThread(threadContext);
459		threadContext->frameWasOn = frameOn;
460		frameOn = false;
461	}
462	MutexUnlock(&threadContext->stateMutex);
463
464	mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
465}
466
467void mCoreThreadPauseFromThread(struct mCoreThread* threadContext) {
468	bool frameOn = true;
469	MutexLock(&threadContext->stateMutex);
470	if (threadContext->state == THREAD_RUNNING || (threadContext->state == THREAD_INTERRUPTING && threadContext->savedState == THREAD_RUNNING)) {
471		threadContext->state = THREAD_PAUSING;
472		frameOn = false;
473	}
474	MutexUnlock(&threadContext->stateMutex);
475
476	mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
477}
478
479void mCoreThreadSetRewinding(struct mCoreThread* threadContext, bool rewinding) {
480	MutexLock(&threadContext->stateMutex);
481	if (rewinding && (threadContext->state == THREAD_REWINDING || (threadContext->state == THREAD_INTERRUPTING && threadContext->savedState == THREAD_REWINDING))) {
482		MutexUnlock(&threadContext->stateMutex);
483		return;
484	}
485	if (!rewinding && (threadContext->state == THREAD_RUNNING || (threadContext->state == THREAD_INTERRUPTING && threadContext->savedState == THREAD_RUNNING))) {
486		MutexUnlock(&threadContext->stateMutex);
487		return;
488	}
489	_waitOnInterrupt(threadContext);
490	if (rewinding && threadContext->state == THREAD_RUNNING) {
491		threadContext->state = THREAD_REWINDING;
492	}
493	if (!rewinding && threadContext->state == THREAD_REWINDING) {
494		threadContext->state = THREAD_RUNNING;
495	}
496	MutexUnlock(&threadContext->stateMutex);
497}
498
499void mCoreThreadWaitFromThread(struct mCoreThread* threadContext) {
500	MutexLock(&threadContext->stateMutex);
501	if ((threadContext->state == THREAD_INTERRUPTED || threadContext->state == THREAD_INTERRUPTING) && threadContext->savedState == THREAD_RUNNING) {
502		threadContext->savedState = THREAD_WAITING;
503	} else if (threadContext->state == THREAD_RUNNING) {
504		threadContext->state = THREAD_WAITING;
505	}
506	MutexUnlock(&threadContext->stateMutex);
507}
508
509void mCoreThreadStopWaiting(struct mCoreThread* threadContext) {
510	MutexLock(&threadContext->stateMutex);
511	if ((threadContext->state == THREAD_INTERRUPTED || threadContext->state == THREAD_INTERRUPTING) && threadContext->savedState == THREAD_WAITING) {
512		threadContext->savedState = THREAD_RUNNING;
513	} else if (threadContext->state == THREAD_WAITING) {
514		threadContext->state = THREAD_RUNNING;
515		ConditionWake(&threadContext->stateCond);
516	}
517	MutexUnlock(&threadContext->stateMutex);
518}
519
520#ifdef USE_PTHREADS
521struct mCoreThread* mCoreThreadGet(void) {
522	pthread_once(&_contextOnce, _createTLS);
523	return pthread_getspecific(_contextKey);
524}
525#elif _WIN32
526struct mCoreThread* mCoreThreadGet(void) {
527	InitOnceExecuteOnce(&_contextOnce, _createTLS, NULL, 0);
528	return TlsGetValue(_contextKey);
529}
530#else
531struct mCoreThread* mCoreThreadGet(void) {
532	return NULL;
533}
534#endif
535
536#else
537struct mCoreThread* mCoreThreadGet(void) {
538	return NULL;
539}
540#endif
541
542static void _mCoreLog(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args) {
543	UNUSED(logger);
544	struct mCoreThread* thread = mCoreThreadGet();
545	if (thread && !(thread->logLevel & level)) {
546		return;
547	}
548	printf("%s: ", mLogCategoryName(category));
549	vprintf(format, args);
550	printf("\n");
551}
552
553struct mLogger* mCoreThreadLogger(void) {
554	struct mCoreThread* thread = mCoreThreadGet();
555	if (thread) {
556		if (!thread->logger.d.log) {
557			thread->logger.d.log = _mCoreLog;
558		}
559		return &thread->logger.d;
560	}
561	return NULL;
562}
563