all repos — mgba @ d02e8a2a8780433a54e3b39cc01e5eb4b688fe3a

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