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 mCoreThreadMarkCrashed(struct mCoreThread* threadContext) {
253 MutexLock(&threadContext->stateMutex);
254 threadContext->state = THREAD_CRASHED;
255 MutexUnlock(&threadContext->stateMutex);
256}
257
258void mCoreThreadEnd(struct mCoreThread* threadContext) {
259 MutexLock(&threadContext->stateMutex);
260 _waitOnInterrupt(threadContext);
261 threadContext->state = THREAD_EXITING;
262 ConditionWake(&threadContext->stateCond);
263 MutexUnlock(&threadContext->stateMutex);
264 MutexLock(&threadContext->sync.audioBufferMutex);
265 threadContext->sync.audioWait = 0;
266 ConditionWake(&threadContext->sync.audioRequiredCond);
267 MutexUnlock(&threadContext->sync.audioBufferMutex);
268
269 MutexLock(&threadContext->sync.videoFrameMutex);
270 threadContext->sync.videoFrameWait = false;
271 threadContext->sync.videoFrameOn = false;
272 ConditionWake(&threadContext->sync.videoFrameRequiredCond);
273 ConditionWake(&threadContext->sync.videoFrameAvailableCond);
274 MutexUnlock(&threadContext->sync.videoFrameMutex);
275}
276
277void mCoreThreadReset(struct mCoreThread* threadContext) {
278 MutexLock(&threadContext->stateMutex);
279 if (threadContext->state == THREAD_INTERRUPTED || threadContext->state == THREAD_INTERRUPTING) {
280 threadContext->savedState = THREAD_RESETING;
281 } else {
282 threadContext->state = THREAD_RESETING;
283 }
284 ConditionWake(&threadContext->stateCond);
285 MutexUnlock(&threadContext->stateMutex);
286}
287
288void mCoreThreadJoin(struct mCoreThread* threadContext) {
289 ThreadJoin(threadContext->thread);
290
291 MutexDeinit(&threadContext->stateMutex);
292 ConditionDeinit(&threadContext->stateCond);
293
294 MutexDeinit(&threadContext->sync.videoFrameMutex);
295 ConditionWake(&threadContext->sync.videoFrameAvailableCond);
296 ConditionDeinit(&threadContext->sync.videoFrameAvailableCond);
297 ConditionWake(&threadContext->sync.videoFrameRequiredCond);
298 ConditionDeinit(&threadContext->sync.videoFrameRequiredCond);
299
300 ConditionWake(&threadContext->sync.audioRequiredCond);
301 ConditionDeinit(&threadContext->sync.audioRequiredCond);
302 MutexDeinit(&threadContext->sync.audioBufferMutex);
303}
304
305bool mCoreThreadIsActive(struct mCoreThread* threadContext) {
306 return threadContext->state >= THREAD_RUNNING && threadContext->state < THREAD_EXITING;
307}
308
309void mCoreThreadInterrupt(struct mCoreThread* threadContext) {
310 if (!threadContext) {
311 return;
312 }
313 MutexLock(&threadContext->stateMutex);
314 ++threadContext->interruptDepth;
315 if (threadContext->interruptDepth > 1 || !mCoreThreadIsActive(threadContext)) {
316 MutexUnlock(&threadContext->stateMutex);
317 return;
318 }
319 threadContext->savedState = threadContext->state;
320 _waitOnInterrupt(threadContext);
321 threadContext->state = THREAD_INTERRUPTING;
322 ConditionWake(&threadContext->stateCond);
323 _waitUntilNotState(threadContext, THREAD_INTERRUPTING);
324 MutexUnlock(&threadContext->stateMutex);
325}
326
327void mCoreThreadInterruptFromThread(struct mCoreThread* threadContext) {
328 if (!threadContext) {
329 return;
330 }
331 MutexLock(&threadContext->stateMutex);
332 ++threadContext->interruptDepth;
333 if (threadContext->interruptDepth > 1 || !mCoreThreadIsActive(threadContext)) {
334 MutexUnlock(&threadContext->stateMutex);
335 return;
336 }
337 threadContext->savedState = threadContext->state;
338 threadContext->state = THREAD_INTERRUPTING;
339 ConditionWake(&threadContext->stateCond);
340 MutexUnlock(&threadContext->stateMutex);
341}
342
343void mCoreThreadContinue(struct mCoreThread* threadContext) {
344 if (!threadContext) {
345 return;
346 }
347 MutexLock(&threadContext->stateMutex);
348 --threadContext->interruptDepth;
349 if (threadContext->interruptDepth < 1 && mCoreThreadIsActive(threadContext)) {
350 threadContext->state = threadContext->savedState;
351 ConditionWake(&threadContext->stateCond);
352 }
353 MutexUnlock(&threadContext->stateMutex);
354}
355
356void mCoreThreadRunFunction(struct mCoreThread* threadContext, void (*run)(struct mCoreThread*)) {
357 MutexLock(&threadContext->stateMutex);
358 threadContext->run = run;
359 _waitOnInterrupt(threadContext);
360 threadContext->savedState = threadContext->state;
361 threadContext->state = THREAD_RUN_ON;
362 ConditionWake(&threadContext->stateCond);
363 _waitUntilNotState(threadContext, THREAD_RUN_ON);
364 MutexUnlock(&threadContext->stateMutex);
365}
366
367void mCoreThreadPause(struct mCoreThread* threadContext) {
368 bool frameOn = threadContext->sync.videoFrameOn;
369 MutexLock(&threadContext->stateMutex);
370 _waitOnInterrupt(threadContext);
371 if (threadContext->state == THREAD_RUNNING) {
372 _pauseThread(threadContext);
373 threadContext->frameWasOn = frameOn;
374 frameOn = false;
375 }
376 MutexUnlock(&threadContext->stateMutex);
377
378 mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
379}
380
381void mCoreThreadUnpause(struct mCoreThread* threadContext) {
382 bool frameOn = threadContext->sync.videoFrameOn;
383 MutexLock(&threadContext->stateMutex);
384 _waitOnInterrupt(threadContext);
385 if (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_PAUSING) {
386 threadContext->state = THREAD_RUNNING;
387 ConditionWake(&threadContext->stateCond);
388 frameOn = threadContext->frameWasOn;
389 }
390 MutexUnlock(&threadContext->stateMutex);
391
392 mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
393}
394
395bool mCoreThreadIsPaused(struct mCoreThread* threadContext) {
396 bool isPaused;
397 MutexLock(&threadContext->stateMutex);
398 if (threadContext->state == THREAD_INTERRUPTED || threadContext->state == THREAD_INTERRUPTING) {
399 isPaused = threadContext->savedState == THREAD_PAUSED;
400 } else {
401 isPaused = threadContext->state == THREAD_PAUSED;
402 }
403 MutexUnlock(&threadContext->stateMutex);
404 return isPaused;
405}
406
407void mCoreThreadTogglePause(struct mCoreThread* threadContext) {
408 bool frameOn = threadContext->sync.videoFrameOn;
409 MutexLock(&threadContext->stateMutex);
410 _waitOnInterrupt(threadContext);
411 if (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_PAUSING) {
412 threadContext->state = THREAD_RUNNING;
413 ConditionWake(&threadContext->stateCond);
414 frameOn = threadContext->frameWasOn;
415 } else if (threadContext->state == THREAD_RUNNING) {
416 _pauseThread(threadContext);
417 threadContext->frameWasOn = frameOn;
418 frameOn = false;
419 }
420 MutexUnlock(&threadContext->stateMutex);
421
422 mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
423}
424
425void mCoreThreadPauseFromThread(struct mCoreThread* threadContext) {
426 bool frameOn = true;
427 MutexLock(&threadContext->stateMutex);
428 if (threadContext->state == THREAD_RUNNING || (threadContext->state == THREAD_INTERRUPTING && threadContext->savedState == THREAD_RUNNING)) {
429 threadContext->state = THREAD_PAUSING;
430 frameOn = false;
431 }
432 MutexUnlock(&threadContext->stateMutex);
433
434 mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
435}
436
437void mCoreThreadSetRewinding(struct mCoreThread* threadContext, bool rewinding) {
438 MutexLock(&threadContext->stateMutex);
439 _waitOnInterrupt(threadContext);
440 if (rewinding && threadContext->state == THREAD_RUNNING) {
441 threadContext->state = THREAD_REWINDING;
442 }
443 if (!rewinding && threadContext->state == THREAD_REWINDING) {
444 threadContext->state = THREAD_RUNNING;
445 }
446 MutexUnlock(&threadContext->stateMutex);
447}
448
449void mCoreThreadWaitFromThread(struct mCoreThread* threadContext) {
450 MutexLock(&threadContext->stateMutex);
451 if ((threadContext->state == THREAD_INTERRUPTED || threadContext->state == THREAD_INTERRUPTING) && threadContext->savedState == THREAD_RUNNING) {
452 threadContext->savedState = THREAD_WAITING;
453 } else if (threadContext->state == THREAD_RUNNING) {
454 threadContext->state = THREAD_WAITING;
455 }
456 MutexUnlock(&threadContext->stateMutex);
457}
458
459void mCoreThreadStopWaiting(struct mCoreThread* threadContext) {
460 MutexLock(&threadContext->stateMutex);
461 if ((threadContext->state == THREAD_INTERRUPTED || threadContext->state == THREAD_INTERRUPTING) && threadContext->savedState == THREAD_WAITING) {
462 threadContext->savedState = THREAD_RUNNING;
463 } else if (threadContext->state == THREAD_WAITING) {
464 threadContext->state = THREAD_RUNNING;
465 ConditionWake(&threadContext->stateCond);
466 }
467 MutexUnlock(&threadContext->stateMutex);
468}
469
470#ifdef USE_PTHREADS
471struct mCoreThread* mCoreThreadGet(void) {
472 pthread_once(&_contextOnce, _createTLS);
473 return pthread_getspecific(_contextKey);
474}
475#elif _WIN32
476struct mCoreThread* mCoreThreadGet(void) {
477 InitOnceExecuteOnce(&_contextOnce, _createTLS, NULL, 0);
478 return TlsGetValue(_contextKey);
479}
480#else
481struct mCoreThread* mCoreThreadGet(void) {
482 return NULL;
483}
484#endif
485
486void mCoreThreadFrameStarted(struct mCoreThread* thread) {
487 if (!thread) {
488 return;
489 }
490 if (thread->core->opts.rewindEnable && thread->state != THREAD_REWINDING) {
491 mCoreRewindAppend(&thread->rewind, thread->core);
492 } else if (thread->state == THREAD_REWINDING) {
493 if (!mCoreRewindRestore(&thread->rewind, thread->core)) {
494 mCoreRewindAppend(&thread->rewind, thread->core);
495 }
496 }
497}
498
499void mCoreThreadFrameEnded(struct mCoreThread* thread) {
500 if (!thread) {
501 return;
502 }
503 if (thread->frameCallback) {
504 thread->frameCallback(thread);
505 }
506}
507
508#else
509struct mCoreThread* mCoreThreadGet(void) {
510 return NULL;
511}
512
513void mCoreThreadFrameStarted(struct mCoreThread* thread) {
514 UNUSED(thread);
515}
516
517void mCoreThreadFrameEnded(struct mCoreThread* thread) {
518 UNUSED(thread);
519}
520
521#endif
522
523static void _mCoreLog(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args) {
524 UNUSED(logger);
525 struct mCoreThread* thread = mCoreThreadGet();
526 if (thread && !(thread->logLevel & level)) {
527 return;
528 }
529 printf("%s: ", mLogCategoryName(category));
530 vprintf(format, args);
531 printf("\n");
532}
533
534struct mLogger* mCoreThreadLogger(void) {
535 struct mCoreThread* thread = mCoreThreadGet();
536 if (thread) {
537 if (!thread->logger.d.log) {
538 thread->logger.d.log = _mCoreLog;
539 }
540 return &thread->logger.d;
541 }
542 return NULL;
543}
544