all repos — mgba @ 070f318c15524a477efaa6755aa84809059411c4

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