all repos — mgba @ 51c3fca3bf79d1c4d200fcb1fd48d24dc03e5d7a

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