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