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