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