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