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