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 <mgba/core/thread.h>
7 
8 #include <mgba/core/blip_buf.h>
9 #include <mgba/core/core.h>
10 #include <mgba/core/serialize.h>
11 #include <mgba-util/patch.h>
12 #include <mgba-util/vfs.h>
13 
14 #ifdef USE_PTHREADS
15 #include <signal.h>
16 #endif
17 
18 #ifndef DISABLE_THREADING
19 
20 static const float _defaultFPSTarget = 60.f;
21 
22 #ifdef USE_PTHREADS
23 static pthread_key_t _contextKey;
24 static pthread_once_t _contextOnce = PTHREAD_ONCE_INIT;
25 
_createTLS(void)26 static void _createTLS(void) {
27 	pthread_key_create(&_contextKey, 0);
28 }
29 #elif _WIN32
30 static DWORD _contextKey;
31 static INIT_ONCE _contextOnce = INIT_ONCE_STATIC_INIT;
32 
_createTLS(PINIT_ONCE once,PVOID param,PVOID * context)33 static BOOL CALLBACK _createTLS(PINIT_ONCE once, PVOID param, PVOID* context) {
34 	UNUSED(once);
35 	UNUSED(param);
36 	UNUSED(context);
37 	_contextKey = TlsAlloc();
38 	return TRUE;
39 }
40 #endif
41 
42 static void _mCoreLog(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args);
43 
_changeState(struct mCoreThreadInternal * threadContext,enum mCoreThreadState newState,bool broadcast)44 static void _changeState(struct mCoreThreadInternal* threadContext, enum mCoreThreadState newState, bool broadcast) {
45 	MutexLock(&threadContext->stateMutex);
46 	threadContext->state = newState;
47 	if (broadcast) {
48 		ConditionWake(&threadContext->stateCond);
49 	}
50 	MutexUnlock(&threadContext->stateMutex);
51 }
52 
_waitOnInterrupt(struct mCoreThreadInternal * threadContext)53 static void _waitOnInterrupt(struct mCoreThreadInternal* threadContext) {
54 	while (threadContext->state == THREAD_INTERRUPTED || threadContext->state == THREAD_INTERRUPTING) {
55 		ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
56 	}
57 }
58 
_waitUntilNotState(struct mCoreThreadInternal * threadContext,enum mCoreThreadState oldState)59 static void _waitUntilNotState(struct mCoreThreadInternal* threadContext, enum mCoreThreadState oldState) {
60 	MutexLock(&threadContext->sync.videoFrameMutex);
61 	bool videoFrameWait = threadContext->sync.videoFrameWait;
62 	threadContext->sync.videoFrameWait = false;
63 	MutexUnlock(&threadContext->sync.videoFrameMutex);
64 
65 	MutexLock(&threadContext->sync.audioBufferMutex);
66 	bool audioWait = threadContext->sync.audioWait;
67 	threadContext->sync.audioWait = false;
68 	MutexUnlock(&threadContext->sync.audioBufferMutex);
69 
70 	while (threadContext->state == oldState) {
71 		MutexUnlock(&threadContext->stateMutex);
72 
73 		if (!MutexTryLock(&threadContext->sync.videoFrameMutex)) {
74 			ConditionWake(&threadContext->sync.videoFrameRequiredCond);
75 			MutexUnlock(&threadContext->sync.videoFrameMutex);
76 		}
77 
78 		if (!MutexTryLock(&threadContext->sync.audioBufferMutex)) {
79 			ConditionWake(&threadContext->sync.audioRequiredCond);
80 			MutexUnlock(&threadContext->sync.audioBufferMutex);
81 		}
82 
83 		MutexLock(&threadContext->stateMutex);
84 		ConditionWake(&threadContext->stateCond);
85 	}
86 
87 	MutexLock(&threadContext->sync.audioBufferMutex);
88 	threadContext->sync.audioWait = audioWait;
89 	MutexUnlock(&threadContext->sync.audioBufferMutex);
90 
91 	MutexLock(&threadContext->sync.videoFrameMutex);
92 	threadContext->sync.videoFrameWait = videoFrameWait;
93 	MutexUnlock(&threadContext->sync.videoFrameMutex);
94 }
95 
_pauseThread(struct mCoreThreadInternal * threadContext)96 static void _pauseThread(struct mCoreThreadInternal* threadContext) {
97 	threadContext->state = THREAD_PAUSING;
98 	_waitUntilNotState(threadContext, THREAD_PAUSING);
99 }
100 
_frameStarted(void * context)101 void _frameStarted(void* context) {
102 	struct mCoreThread* thread = context;
103 	if (!thread) {
104 		return;
105 	}
106 	if (thread->core->opts.rewindEnable && thread->core->opts.rewindBufferCapacity > 0) {
107 		if (thread->impl->state != THREAD_REWINDING) {
108 			mCoreRewindAppend(&thread->impl->rewind, thread->core);
109 		} else if (thread->impl->state == THREAD_REWINDING) {
110 			if (!mCoreRewindRestore(&thread->impl->rewind, thread->core)) {
111 				mCoreRewindAppend(&thread->impl->rewind, thread->core);
112 			}
113 		}
114 	}
115 }
116 
_frameEnded(void * context)117 void _frameEnded(void* context) {
118 	struct mCoreThread* thread = context;
119 	if (!thread) {
120 		return;
121 	}
122 	if (thread->frameCallback) {
123 		thread->frameCallback(thread);
124 	}
125 }
126 
_crashed(void * context)127 void _crashed(void* context) {
128 	struct mCoreThread* thread = context;
129 	if (!thread) {
130 		return;
131 	}
132 	_changeState(thread->impl, THREAD_CRASHED, true);
133 }
134 
_coreSleep(void * context)135 void _coreSleep(void* context) {
136 	struct mCoreThread* thread = context;
137 	if (!thread) {
138 		return;
139 	}
140 	if (thread->sleepCallback) {
141 		thread->sleepCallback(thread);
142 	}
143 }
144 
_mCoreThreadRun(void * context)145 static THREAD_ENTRY _mCoreThreadRun(void* context) {
146 	struct mCoreThread* threadContext = context;
147 #ifdef USE_PTHREADS
148 	pthread_once(&_contextOnce, _createTLS);
149 	pthread_setspecific(_contextKey, threadContext);
150 #elif _WIN32
151 	InitOnceExecuteOnce(&_contextOnce, _createTLS, NULL, 0);
152 	TlsSetValue(_contextKey, threadContext);
153 #endif
154 
155 	ThreadSetName("CPU Thread");
156 
157 #if !defined(_WIN32) && defined(USE_PTHREADS)
158 	sigset_t signals;
159 	sigemptyset(&signals);
160 	pthread_sigmask(SIG_SETMASK, &signals, 0);
161 #endif
162 
163 	struct mCore* core = threadContext->core;
164 	struct mCoreCallbacks callbacks = {
165 		.videoFrameStarted = _frameStarted,
166 		.videoFrameEnded = _frameEnded,
167 		.coreCrashed = _crashed,
168 		.sleep = _coreSleep,
169 		.context = threadContext
170 	};
171 	core->addCoreCallbacks(core, &callbacks);
172 	core->setSync(core, &threadContext->impl->sync);
173 
174 	struct mLogFilter filter;
175 	if (!threadContext->logger.d.filter) {
176 		threadContext->logger.d.filter = &filter;
177 		mLogFilterInit(threadContext->logger.d.filter);
178 		mLogFilterLoad(threadContext->logger.d.filter, &core->config);
179 	}
180 
181 	mCoreThreadRewindParamsChanged(threadContext);
182 	if (threadContext->startCallback) {
183 		threadContext->startCallback(threadContext);
184 	}
185 
186 	core->reset(core);
187 	_changeState(threadContext->impl, THREAD_RUNNING, true);
188 
189 	if (threadContext->resetCallback) {
190 		threadContext->resetCallback(threadContext);
191 	}
192 
193 	struct mCoreThreadInternal* impl = threadContext->impl;
194 	while (impl->state < THREAD_EXITING) {
195 #ifdef USE_DEBUGGERS
196 		struct mDebugger* debugger = core->debugger;
197 		if (debugger) {
198 			mDebuggerRun(debugger);
199 			if (debugger->state == DEBUGGER_SHUTDOWN) {
200 				_changeState(impl, THREAD_EXITING, false);
201 			}
202 		} else
203 #endif
204 		{
205 			while (impl->state <= THREAD_MAX_RUNNING) {
206 				core->runLoop(core);
207 			}
208 		}
209 
210 		enum mCoreThreadState deferred = THREAD_RUNNING;
211 		MutexLock(&impl->stateMutex);
212 		while (impl->state > THREAD_MAX_RUNNING && impl->state < THREAD_EXITING) {
213 			deferred = impl->state;
214 
215 			switch (deferred) {
216 			case THREAD_INTERRUPTING:
217 				impl->state = THREAD_INTERRUPTED;
218 				ConditionWake(&impl->stateCond);
219 				break;
220 			case THREAD_PAUSING:
221 				impl->state = THREAD_PAUSED;
222 				break;
223 			case THREAD_RESETING:
224 				impl->state = THREAD_RUNNING;
225 				break;
226 			default:
227 				break;
228 			}
229 
230 			if (deferred >= THREAD_MIN_DEFERRED && deferred <= THREAD_MAX_DEFERRED) {
231 				break;
232 			}
233 
234 			deferred = impl->state;
235 			if (deferred == THREAD_INTERRUPTED) {
236 				deferred = impl->savedState;
237 			}
238 			while (impl->state >= THREAD_WAITING && impl->state <= THREAD_MAX_WAITING) {
239 				ConditionWait(&impl->stateCond, &impl->stateMutex);
240 
241 				if (impl->sync.audioWait) {
242 					MutexUnlock(&impl->stateMutex);
243 					mCoreSyncLockAudio(&impl->sync);
244 					mCoreSyncProduceAudio(&impl->sync, core->getAudioChannel(core, 0), core->getAudioBufferSize(core));
245 					MutexLock(&impl->stateMutex);
246 				}
247 			}
248 		}
249 		MutexUnlock(&impl->stateMutex);
250 		switch (deferred) {
251 		case THREAD_PAUSING:
252 			if (threadContext->pauseCallback) {
253 				threadContext->pauseCallback(threadContext);
254 			}
255 			break;
256 		case THREAD_PAUSED:
257 			if (threadContext->unpauseCallback) {
258 				threadContext->unpauseCallback(threadContext);
259 			}
260 			break;
261 		case THREAD_RUN_ON:
262 			if (threadContext->run) {
263 				threadContext->run(threadContext);
264 			}
265 			threadContext->impl->state = threadContext->impl->savedState;
266 			break;
267 		case THREAD_RESETING:
268 			core->reset(core);
269 			if (threadContext->resetCallback) {
270 				threadContext->resetCallback(threadContext);
271 			}
272 			break;
273 		default:
274 			break;
275 		}
276 	}
277 
278 	while (impl->state < THREAD_SHUTDOWN) {
279 		_changeState(impl, THREAD_SHUTDOWN, false);
280 	}
281 
282 	if (core->opts.rewindEnable) {
283 		 mCoreRewindContextDeinit(&impl->rewind);
284 	}
285 
286 	if (threadContext->cleanCallback) {
287 		threadContext->cleanCallback(threadContext);
288 	}
289 	core->clearCoreCallbacks(core);
290 
291 	if (threadContext->logger.d.filter == &filter) {
292 		mLogFilterDeinit(&filter);
293 	}
294 	threadContext->logger.d.filter = NULL;
295 
296 	return 0;
297 }
298 
mCoreThreadStart(struct mCoreThread * threadContext)299 bool mCoreThreadStart(struct mCoreThread* threadContext) {
300 	threadContext->impl = calloc(sizeof(*threadContext->impl), 1);
301 	threadContext->impl->state = THREAD_INITIALIZED;
302 	threadContext->logger.p = threadContext;
303 	if (!threadContext->logger.d.log) {
304 		threadContext->logger.d.log = _mCoreLog;
305 		threadContext->logger.d.filter = NULL;
306 	}
307 
308 	if (!threadContext->impl->sync.fpsTarget) {
309 		threadContext->impl->sync.fpsTarget = _defaultFPSTarget;
310 	}
311 
312 	MutexInit(&threadContext->impl->stateMutex);
313 	ConditionInit(&threadContext->impl->stateCond);
314 
315 	MutexInit(&threadContext->impl->sync.videoFrameMutex);
316 	ConditionInit(&threadContext->impl->sync.videoFrameAvailableCond);
317 	ConditionInit(&threadContext->impl->sync.videoFrameRequiredCond);
318 	MutexInit(&threadContext->impl->sync.audioBufferMutex);
319 	ConditionInit(&threadContext->impl->sync.audioRequiredCond);
320 
321 	threadContext->impl->interruptDepth = 0;
322 
323 #ifdef USE_PTHREADS
324 	sigset_t signals;
325 	sigemptyset(&signals);
326 	sigaddset(&signals, SIGINT);
327 	sigaddset(&signals, SIGTRAP);
328 	pthread_sigmask(SIG_BLOCK, &signals, 0);
329 #endif
330 
331 	threadContext->impl->sync.audioWait = threadContext->core->opts.audioSync;
332 	threadContext->impl->sync.videoFrameWait = threadContext->core->opts.videoSync;
333 	threadContext->impl->sync.fpsTarget = threadContext->core->opts.fpsTarget;
334 
335 	MutexLock(&threadContext->impl->stateMutex);
336 	ThreadCreate(&threadContext->impl->thread, _mCoreThreadRun, threadContext);
337 	while (threadContext->impl->state < THREAD_RUNNING) {
338 		ConditionWait(&threadContext->impl->stateCond, &threadContext->impl->stateMutex);
339 	}
340 	MutexUnlock(&threadContext->impl->stateMutex);
341 
342 	return true;
343 }
344 
mCoreThreadHasStarted(struct mCoreThread * threadContext)345 bool mCoreThreadHasStarted(struct mCoreThread* threadContext) {
346 	if (!threadContext->impl) {
347 		return false;
348 	}
349 	bool hasStarted;
350 	MutexLock(&threadContext->impl->stateMutex);
351 	hasStarted = threadContext->impl->state > THREAD_INITIALIZED;
352 	MutexUnlock(&threadContext->impl->stateMutex);
353 	return hasStarted;
354 }
355 
mCoreThreadHasExited(struct mCoreThread * threadContext)356 bool mCoreThreadHasExited(struct mCoreThread* threadContext) {
357 	if (!threadContext->impl) {
358 		return false;
359 	}
360 	bool hasExited;
361 	MutexLock(&threadContext->impl->stateMutex);
362 	hasExited = threadContext->impl->state > THREAD_EXITING;
363 	MutexUnlock(&threadContext->impl->stateMutex);
364 	return hasExited;
365 }
366 
mCoreThreadHasCrashed(struct mCoreThread * threadContext)367 bool mCoreThreadHasCrashed(struct mCoreThread* threadContext) {
368 	if (!threadContext->impl) {
369 		return false;
370 	}
371 	bool hasExited;
372 	MutexLock(&threadContext->impl->stateMutex);
373 	hasExited = threadContext->impl->state == THREAD_CRASHED;
374 	MutexUnlock(&threadContext->impl->stateMutex);
375 	return hasExited;
376 }
377 
mCoreThreadMarkCrashed(struct mCoreThread * threadContext)378 void mCoreThreadMarkCrashed(struct mCoreThread* threadContext) {
379 	MutexLock(&threadContext->impl->stateMutex);
380 	threadContext->impl->state = THREAD_CRASHED;
381 	MutexUnlock(&threadContext->impl->stateMutex);
382 }
383 
mCoreThreadEnd(struct mCoreThread * threadContext)384 void mCoreThreadEnd(struct mCoreThread* threadContext) {
385 	MutexLock(&threadContext->impl->stateMutex);
386 	_waitOnInterrupt(threadContext->impl);
387 	threadContext->impl->state = THREAD_EXITING;
388 	ConditionWake(&threadContext->impl->stateCond);
389 	MutexUnlock(&threadContext->impl->stateMutex);
390 	MutexLock(&threadContext->impl->sync.audioBufferMutex);
391 	threadContext->impl->sync.audioWait = 0;
392 	ConditionWake(&threadContext->impl->sync.audioRequiredCond);
393 	MutexUnlock(&threadContext->impl->sync.audioBufferMutex);
394 
395 	MutexLock(&threadContext->impl->sync.videoFrameMutex);
396 	threadContext->impl->sync.videoFrameWait = false;
397 	threadContext->impl->sync.videoFrameOn = false;
398 	ConditionWake(&threadContext->impl->sync.videoFrameRequiredCond);
399 	ConditionWake(&threadContext->impl->sync.videoFrameAvailableCond);
400 	MutexUnlock(&threadContext->impl->sync.videoFrameMutex);
401 }
402 
mCoreThreadReset(struct mCoreThread * threadContext)403 void mCoreThreadReset(struct mCoreThread* threadContext) {
404 	MutexLock(&threadContext->impl->stateMutex);
405 	if (threadContext->impl->state == THREAD_INTERRUPTED || threadContext->impl->state == THREAD_INTERRUPTING) {
406 		threadContext->impl->savedState = THREAD_RESETING;
407 	} else {
408 		threadContext->impl->state = THREAD_RESETING;
409 	}
410 	ConditionWake(&threadContext->impl->stateCond);
411 	MutexUnlock(&threadContext->impl->stateMutex);
412 }
413 
mCoreThreadJoin(struct mCoreThread * threadContext)414 void mCoreThreadJoin(struct mCoreThread* threadContext) {
415 	if (!threadContext->impl) {
416 		return;
417 	}
418 	ThreadJoin(&threadContext->impl->thread);
419 
420 	MutexDeinit(&threadContext->impl->stateMutex);
421 	ConditionDeinit(&threadContext->impl->stateCond);
422 
423 	MutexDeinit(&threadContext->impl->sync.videoFrameMutex);
424 	ConditionWake(&threadContext->impl->sync.videoFrameAvailableCond);
425 	ConditionDeinit(&threadContext->impl->sync.videoFrameAvailableCond);
426 	ConditionWake(&threadContext->impl->sync.videoFrameRequiredCond);
427 	ConditionDeinit(&threadContext->impl->sync.videoFrameRequiredCond);
428 
429 	ConditionWake(&threadContext->impl->sync.audioRequiredCond);
430 	ConditionDeinit(&threadContext->impl->sync.audioRequiredCond);
431 	MutexDeinit(&threadContext->impl->sync.audioBufferMutex);
432 
433 	free(threadContext->impl);
434 	threadContext->impl = NULL;
435 }
436 
mCoreThreadIsActive(struct mCoreThread * threadContext)437 bool mCoreThreadIsActive(struct mCoreThread* threadContext) {
438 	if (!threadContext->impl) {
439 		return false;
440 	}
441 	return threadContext->impl->state >= THREAD_RUNNING && threadContext->impl->state < THREAD_EXITING;
442 }
443 
mCoreThreadInterrupt(struct mCoreThread * threadContext)444 void mCoreThreadInterrupt(struct mCoreThread* threadContext) {
445 	if (!threadContext) {
446 		return;
447 	}
448 	MutexLock(&threadContext->impl->stateMutex);
449 	++threadContext->impl->interruptDepth;
450 	if (threadContext->impl->interruptDepth > 1 || !mCoreThreadIsActive(threadContext)) {
451 		MutexUnlock(&threadContext->impl->stateMutex);
452 		return;
453 	}
454 	threadContext->impl->savedState = threadContext->impl->state;
455 	_waitOnInterrupt(threadContext->impl);
456 	threadContext->impl->state = THREAD_INTERRUPTING;
457 	ConditionWake(&threadContext->impl->stateCond);
458 	_waitUntilNotState(threadContext->impl, THREAD_INTERRUPTING);
459 	MutexUnlock(&threadContext->impl->stateMutex);
460 }
461 
mCoreThreadInterruptFromThread(struct mCoreThread * threadContext)462 void mCoreThreadInterruptFromThread(struct mCoreThread* threadContext) {
463 	if (!threadContext) {
464 		return;
465 	}
466 	MutexLock(&threadContext->impl->stateMutex);
467 	++threadContext->impl->interruptDepth;
468 	if (threadContext->impl->interruptDepth > 1 || !mCoreThreadIsActive(threadContext)) {
469 		if (threadContext->impl->state == THREAD_INTERRUPTING) {
470 			threadContext->impl->state = THREAD_INTERRUPTED;
471 		}
472 		MutexUnlock(&threadContext->impl->stateMutex);
473 		return;
474 	}
475 	threadContext->impl->savedState = threadContext->impl->state;
476 	threadContext->impl->state = THREAD_INTERRUPTING;
477 	ConditionWake(&threadContext->impl->stateCond);
478 	MutexUnlock(&threadContext->impl->stateMutex);
479 }
480 
mCoreThreadContinue(struct mCoreThread * threadContext)481 void mCoreThreadContinue(struct mCoreThread* threadContext) {
482 	if (!threadContext) {
483 		return;
484 	}
485 	MutexLock(&threadContext->impl->stateMutex);
486 	--threadContext->impl->interruptDepth;
487 	if (threadContext->impl->interruptDepth < 1 && mCoreThreadIsActive(threadContext)) {
488 		threadContext->impl->state = threadContext->impl->savedState;
489 		ConditionWake(&threadContext->impl->stateCond);
490 	}
491 	MutexUnlock(&threadContext->impl->stateMutex);
492 }
493 
mCoreThreadRunFunction(struct mCoreThread * threadContext,void (* run)(struct mCoreThread *))494 void mCoreThreadRunFunction(struct mCoreThread* threadContext, void (*run)(struct mCoreThread*)) {
495 	MutexLock(&threadContext->impl->stateMutex);
496 	threadContext->run = run;
497 	_waitOnInterrupt(threadContext->impl);
498 	threadContext->impl->savedState = threadContext->impl->state;
499 	threadContext->impl->state = THREAD_RUN_ON;
500 	ConditionWake(&threadContext->impl->stateCond);
501 	_waitUntilNotState(threadContext->impl, THREAD_RUN_ON);
502 	MutexUnlock(&threadContext->impl->stateMutex);
503 }
504 
mCoreThreadPause(struct mCoreThread * threadContext)505 void mCoreThreadPause(struct mCoreThread* threadContext) {
506 	bool frameOn = threadContext->impl->sync.videoFrameOn;
507 	MutexLock(&threadContext->impl->stateMutex);
508 	_waitOnInterrupt(threadContext->impl);
509 	if (threadContext->impl->state == THREAD_RUNNING) {
510 		_pauseThread(threadContext->impl);
511 		threadContext->impl->frameWasOn = frameOn;
512 		frameOn = false;
513 	}
514 	MutexUnlock(&threadContext->impl->stateMutex);
515 
516 	mCoreSyncSetVideoSync(&threadContext->impl->sync, frameOn);
517 }
518 
mCoreThreadUnpause(struct mCoreThread * threadContext)519 void mCoreThreadUnpause(struct mCoreThread* threadContext) {
520 	bool frameOn = threadContext->impl->sync.videoFrameOn;
521 	MutexLock(&threadContext->impl->stateMutex);
522 	_waitOnInterrupt(threadContext->impl);
523 	if (threadContext->impl->state == THREAD_PAUSED || threadContext->impl->state == THREAD_PAUSING) {
524 		threadContext->impl->state = THREAD_RUNNING;
525 		ConditionWake(&threadContext->impl->stateCond);
526 		frameOn = threadContext->impl->frameWasOn;
527 	}
528 	MutexUnlock(&threadContext->impl->stateMutex);
529 
530 	mCoreSyncSetVideoSync(&threadContext->impl->sync, frameOn);
531 }
532 
mCoreThreadIsPaused(struct mCoreThread * threadContext)533 bool mCoreThreadIsPaused(struct mCoreThread* threadContext) {
534 	bool isPaused;
535 	MutexLock(&threadContext->impl->stateMutex);
536 	if (threadContext->impl->interruptDepth) {
537 		isPaused = threadContext->impl->savedState == THREAD_PAUSED;
538 	} else {
539 		isPaused = threadContext->impl->state == THREAD_PAUSED;
540 	}
541 	MutexUnlock(&threadContext->impl->stateMutex);
542 	return isPaused;
543 }
544 
mCoreThreadTogglePause(struct mCoreThread * threadContext)545 void mCoreThreadTogglePause(struct mCoreThread* threadContext) {
546 	bool frameOn = threadContext->impl->sync.videoFrameOn;
547 	MutexLock(&threadContext->impl->stateMutex);
548 	_waitOnInterrupt(threadContext->impl);
549 	if (threadContext->impl->state == THREAD_PAUSED || threadContext->impl->state == THREAD_PAUSING) {
550 		threadContext->impl->state = THREAD_RUNNING;
551 		ConditionWake(&threadContext->impl->stateCond);
552 		frameOn = threadContext->impl->frameWasOn;
553 	} else if (threadContext->impl->state == THREAD_RUNNING) {
554 		_pauseThread(threadContext->impl);
555 		threadContext->impl->frameWasOn = frameOn;
556 		frameOn = false;
557 	}
558 	MutexUnlock(&threadContext->impl->stateMutex);
559 
560 	mCoreSyncSetVideoSync(&threadContext->impl->sync, frameOn);
561 }
562 
mCoreThreadPauseFromThread(struct mCoreThread * threadContext)563 void mCoreThreadPauseFromThread(struct mCoreThread* threadContext) {
564 	bool frameOn = true;
565 	MutexLock(&threadContext->impl->stateMutex);
566 	if (threadContext->impl->state == THREAD_RUNNING || (threadContext->impl->interruptDepth && threadContext->impl->savedState == THREAD_RUNNING)) {
567 		threadContext->impl->state = THREAD_PAUSING;
568 		frameOn = false;
569 	}
570 	MutexUnlock(&threadContext->impl->stateMutex);
571 
572 	mCoreSyncSetVideoSync(&threadContext->impl->sync, frameOn);
573 }
574 
mCoreThreadSetRewinding(struct mCoreThread * threadContext,bool rewinding)575 void mCoreThreadSetRewinding(struct mCoreThread* threadContext, bool rewinding) {
576 	MutexLock(&threadContext->impl->stateMutex);
577 	if (rewinding && (threadContext->impl->state == THREAD_REWINDING || (threadContext->impl->interruptDepth && threadContext->impl->savedState == THREAD_REWINDING))) {
578 		MutexUnlock(&threadContext->impl->stateMutex);
579 		return;
580 	}
581 	if (!rewinding && ((!threadContext->impl->interruptDepth && threadContext->impl->state != THREAD_REWINDING) || (threadContext->impl->interruptDepth && threadContext->impl->savedState != THREAD_REWINDING))) {
582 		MutexUnlock(&threadContext->impl->stateMutex);
583 		return;
584 	}
585 	_waitOnInterrupt(threadContext->impl);
586 	if (rewinding && threadContext->impl->state == THREAD_RUNNING) {
587 		threadContext->impl->state = THREAD_REWINDING;
588 	}
589 	if (!rewinding && threadContext->impl->state == THREAD_REWINDING) {
590 		threadContext->impl->state = THREAD_RUNNING;
591 	}
592 	MutexUnlock(&threadContext->impl->stateMutex);
593 }
594 
mCoreThreadRewindParamsChanged(struct mCoreThread * threadContext)595 void mCoreThreadRewindParamsChanged(struct mCoreThread* threadContext) {
596 	struct mCore* core = threadContext->core;
597 	if (core->opts.rewindEnable && core->opts.rewindBufferCapacity > 0) {
598 		 mCoreRewindContextInit(&threadContext->impl->rewind, core->opts.rewindBufferCapacity, true);
599 	} else {
600 		 mCoreRewindContextDeinit(&threadContext->impl->rewind);
601 	}
602 }
603 
mCoreThreadWaitFromThread(struct mCoreThread * threadContext)604 void mCoreThreadWaitFromThread(struct mCoreThread* threadContext) {
605 	MutexLock(&threadContext->impl->stateMutex);
606 	if (threadContext->impl->interruptDepth && threadContext->impl->savedState == THREAD_RUNNING) {
607 		threadContext->impl->savedState = THREAD_WAITING;
608 	} else if (threadContext->impl->state == THREAD_RUNNING) {
609 		threadContext->impl->state = THREAD_WAITING;
610 	}
611 	MutexUnlock(&threadContext->impl->stateMutex);
612 }
613 
mCoreThreadStopWaiting(struct mCoreThread * threadContext)614 void mCoreThreadStopWaiting(struct mCoreThread* threadContext) {
615 	MutexLock(&threadContext->impl->stateMutex);
616 	if (threadContext->impl->interruptDepth && threadContext->impl->savedState == THREAD_WAITING) {
617 		threadContext->impl->savedState = THREAD_RUNNING;
618 	} else if (threadContext->impl->state == THREAD_WAITING) {
619 		threadContext->impl->state = THREAD_RUNNING;
620 		ConditionWake(&threadContext->impl->stateCond);
621 	}
622 	MutexUnlock(&threadContext->impl->stateMutex);
623 }
624 
625 #ifdef USE_PTHREADS
mCoreThreadGet(void)626 struct mCoreThread* mCoreThreadGet(void) {
627 	pthread_once(&_contextOnce, _createTLS);
628 	return pthread_getspecific(_contextKey);
629 }
630 #elif _WIN32
mCoreThreadGet(void)631 struct mCoreThread* mCoreThreadGet(void) {
632 	InitOnceExecuteOnce(&_contextOnce, _createTLS, NULL, 0);
633 	return TlsGetValue(_contextKey);
634 }
635 #else
mCoreThreadGet(void)636 struct mCoreThread* mCoreThreadGet(void) {
637 	return NULL;
638 }
639 #endif
640 
_mCoreLog(struct mLogger * logger,int category,enum mLogLevel level,const char * format,va_list args)641 static void _mCoreLog(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args) {
642 	UNUSED(logger);
643 	UNUSED(level);
644 	printf("%s: ", mLogCategoryName(category));
645 	vprintf(format, args);
646 	printf("\n");
647 	struct mCoreThread* thread = mCoreThreadGet();
648 	if (thread && level == mLOG_FATAL) {
649 		mCoreThreadMarkCrashed(thread);
650 	}
651 }
652 #else
mCoreThreadGet(void)653 struct mCoreThread* mCoreThreadGet(void) {
654 	return NULL;
655 }
656 #endif
657 
mCoreThreadLogger(void)658 struct mLogger* mCoreThreadLogger(void) {
659 	struct mCoreThread* thread = mCoreThreadGet();
660 	if (thread) {
661 		return &thread->logger.d;
662 	}
663 	return NULL;
664 }
665 
666