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