all repos — mgba @ 4d49aa095b000f2f869d53d7e1c0f527d6858e88

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