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