all repos — mgba @ 9fef8ec49d559b13f9cca644b53da749b5ea52d5

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