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