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