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