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