all repos — mgba @ a8999958dafb0486c4d5474ed13d21e36e17cc3c

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