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