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