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