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 "platform/commandline.h"
13
14#include <signal.h>
15
16#ifndef DISABLE_THREADING
17
18#ifdef USE_PTHREADS
19static pthread_key_t _contextKey;
20static pthread_once_t _contextOnce = PTHREAD_ONCE_INIT;
21
22static void _createTLS(void) {
23 pthread_key_create(&_contextKey, 0);
24}
25#elif _WIN32
26static DWORD _contextKey;
27static INIT_ONCE _contextOnce = INIT_ONCE_STATIC_INIT;
28
29static BOOL CALLBACK _createTLS(PINIT_ONCE once, PVOID param, PVOID* context) {
30 UNUSED(once);
31 UNUSED(param);
32 UNUSED(context);
33 _contextKey = TlsAlloc();
34 return TRUE;
35}
36#endif
37
38static void _changeState(struct mCoreThread* threadContext, enum mCoreThreadState newState, bool broadcast) {
39 MutexLock(&threadContext->stateMutex);
40 threadContext->state = newState;
41 if (broadcast) {
42 ConditionWake(&threadContext->stateCond);
43 }
44 MutexUnlock(&threadContext->stateMutex);
45}
46
47static void _waitOnInterrupt(struct mCoreThread* threadContext) {
48 while (threadContext->state == THREAD_INTERRUPTED) {
49 ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
50 }
51}
52
53static void _waitUntilNotState(struct mCoreThread* threadContext, enum mCoreThreadState oldState) {
54 while (threadContext->state == oldState) {
55 MutexUnlock(&threadContext->stateMutex);
56
57 MutexLock(&threadContext->stateMutex);
58 ConditionWake(&threadContext->stateCond);
59 }
60}
61
62static void _pauseThread(struct mCoreThread* threadContext, bool onThread) {
63 threadContext->state = THREAD_PAUSING;
64 if (!onThread) {
65 _waitUntilNotState(threadContext, THREAD_PAUSING);
66 }
67}
68
69static THREAD_ENTRY _mCoreThreadRun(void* context) {
70 struct mCoreThread* threadContext = context;
71#ifdef USE_PTHREADS
72 pthread_once(&_contextOnce, _createTLS);
73 pthread_setspecific(_contextKey, threadContext);
74#elif _WIN32
75 InitOnceExecuteOnce(&_contextOnce, _createTLS, NULL, 0);
76 TlsSetValue(_contextKey, threadContext);
77#endif
78
79 ThreadSetName("CPU Thread");
80
81#if !defined(_WIN32) && defined(USE_PTHREADS)
82 sigset_t signals;
83 sigemptyset(&signals);
84 pthread_sigmask(SIG_SETMASK, &signals, 0);
85#endif
86
87 struct mCore* core = threadContext->core;
88 core->setSync(core, &threadContext->sync);
89 core->reset(core);
90
91 if (threadContext->startCallback) {
92 threadContext->startCallback(threadContext);
93 }
94
95 _changeState(threadContext, THREAD_RUNNING, true);
96
97 while (threadContext->state < THREAD_EXITING) {
98 core->runLoop(core);
99
100 int resetScheduled = 0;
101 MutexLock(&threadContext->stateMutex);
102 while (threadContext->state > THREAD_RUNNING && threadContext->state < THREAD_EXITING) {
103 if (threadContext->state == THREAD_PAUSING) {
104 threadContext->state = THREAD_PAUSED;
105 ConditionWake(&threadContext->stateCond);
106 }
107 if (threadContext->state == THREAD_INTERRUPTING) {
108 threadContext->state = THREAD_INTERRUPTED;
109 ConditionWake(&threadContext->stateCond);
110 }
111 if (threadContext->state == THREAD_RUN_ON) {
112 if (threadContext->run) {
113 threadContext->run(threadContext);
114 }
115 threadContext->state = threadContext->savedState;
116 ConditionWake(&threadContext->stateCond);
117 }
118 if (threadContext->state == THREAD_RESETING) {
119 threadContext->state = THREAD_RUNNING;
120 resetScheduled = 1;
121 }
122 while (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_INTERRUPTED) {
123 ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
124 }
125 }
126 MutexUnlock(&threadContext->stateMutex);
127 if (resetScheduled) {
128 core->reset(core);
129 }
130 }
131
132 while (threadContext->state < THREAD_SHUTDOWN) {
133 _changeState(threadContext, THREAD_SHUTDOWN, false);
134 }
135
136 if (threadContext->cleanCallback) {
137 threadContext->cleanCallback(threadContext);
138 }
139
140 return 0;
141}
142
143bool mCoreThreadStart(struct mCoreThread* threadContext) {
144 threadContext->state = THREAD_INITIALIZED;
145
146 MutexInit(&threadContext->stateMutex);
147 ConditionInit(&threadContext->stateCond);
148
149 MutexInit(&threadContext->sync.videoFrameMutex);
150 ConditionInit(&threadContext->sync.videoFrameAvailableCond);
151 ConditionInit(&threadContext->sync.videoFrameRequiredCond);
152 MutexInit(&threadContext->sync.audioBufferMutex);
153 ConditionInit(&threadContext->sync.audioRequiredCond);
154
155 threadContext->interruptDepth = 0;
156
157#ifdef USE_PTHREADS
158 sigset_t signals;
159 sigemptyset(&signals);
160 sigaddset(&signals, SIGINT);
161 sigaddset(&signals, SIGTRAP);
162 pthread_sigmask(SIG_BLOCK, &signals, 0);
163#endif
164
165 threadContext->sync.audioWait = threadContext->core->opts.audioSync;
166 threadContext->sync.videoFrameWait = threadContext->core->opts.videoSync;
167
168 MutexLock(&threadContext->stateMutex);
169 ThreadCreate(&threadContext->thread, _mCoreThreadRun, threadContext);
170 while (threadContext->state < THREAD_RUNNING) {
171 ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
172 }
173 MutexUnlock(&threadContext->stateMutex);
174
175 return true;
176}
177
178bool mCoreThreadHasStarted(struct mCoreThread* threadContext) {
179 bool hasStarted;
180 MutexLock(&threadContext->stateMutex);
181 hasStarted = threadContext->state > THREAD_INITIALIZED;
182 MutexUnlock(&threadContext->stateMutex);
183 return hasStarted;
184}
185
186bool mCoreThreadHasExited(struct mCoreThread* threadContext) {
187 bool hasExited;
188 MutexLock(&threadContext->stateMutex);
189 hasExited = threadContext->state > THREAD_EXITING;
190 MutexUnlock(&threadContext->stateMutex);
191 return hasExited;
192}
193
194bool mCoreThreadHasCrashed(struct mCoreThread* threadContext) {
195 bool hasExited;
196 MutexLock(&threadContext->stateMutex);
197 hasExited = threadContext->state == THREAD_CRASHED;
198 MutexUnlock(&threadContext->stateMutex);
199 return hasExited;
200}
201
202void mCoreThreadEnd(struct mCoreThread* threadContext) {
203 MutexLock(&threadContext->stateMutex);
204 _waitOnInterrupt(threadContext);
205 threadContext->state = THREAD_EXITING;
206 ConditionWake(&threadContext->stateCond);
207 MutexUnlock(&threadContext->stateMutex);
208 MutexLock(&threadContext->sync.audioBufferMutex);
209 threadContext->sync.audioWait = 0;
210 ConditionWake(&threadContext->sync.audioRequiredCond);
211 MutexUnlock(&threadContext->sync.audioBufferMutex);
212
213 MutexLock(&threadContext->sync.videoFrameMutex);
214 threadContext->sync.videoFrameWait = false;
215 threadContext->sync.videoFrameOn = false;
216 ConditionWake(&threadContext->sync.videoFrameRequiredCond);
217 ConditionWake(&threadContext->sync.videoFrameAvailableCond);
218 MutexUnlock(&threadContext->sync.videoFrameMutex);
219}
220
221void mCoreThreadReset(struct mCoreThread* threadContext) {
222 MutexLock(&threadContext->stateMutex);
223 _waitOnInterrupt(threadContext);
224 threadContext->state = THREAD_RESETING;
225 ConditionWake(&threadContext->stateCond);
226 MutexUnlock(&threadContext->stateMutex);
227}
228
229void mCoreThreadJoin(struct mCoreThread* threadContext) {
230 ThreadJoin(threadContext->thread);
231
232 MutexDeinit(&threadContext->stateMutex);
233 ConditionDeinit(&threadContext->stateCond);
234
235 MutexDeinit(&threadContext->sync.videoFrameMutex);
236 ConditionWake(&threadContext->sync.videoFrameAvailableCond);
237 ConditionDeinit(&threadContext->sync.videoFrameAvailableCond);
238 ConditionWake(&threadContext->sync.videoFrameRequiredCond);
239 ConditionDeinit(&threadContext->sync.videoFrameRequiredCond);
240
241 ConditionWake(&threadContext->sync.audioRequiredCond);
242 ConditionDeinit(&threadContext->sync.audioRequiredCond);
243 MutexDeinit(&threadContext->sync.audioBufferMutex);
244}
245
246bool mCoreThreadIsActive(struct mCoreThread* threadContext) {
247 return threadContext->state >= THREAD_RUNNING && threadContext->state < THREAD_EXITING;
248}
249
250void mCoreThreadInterrupt(struct mCoreThread* threadContext) {
251 if (!threadContext) {
252 return;
253 }
254 MutexLock(&threadContext->stateMutex);
255 ++threadContext->interruptDepth;
256 if (threadContext->interruptDepth > 1 || !mCoreThreadIsActive(threadContext)) {
257 MutexUnlock(&threadContext->stateMutex);
258 return;
259 }
260 threadContext->savedState = threadContext->state;
261 _waitOnInterrupt(threadContext);
262 threadContext->state = THREAD_INTERRUPTING;
263 ConditionWake(&threadContext->stateCond);
264 _waitUntilNotState(threadContext, THREAD_INTERRUPTING);
265 MutexUnlock(&threadContext->stateMutex);
266}
267
268void mCoreThreadContinue(struct mCoreThread* threadContext) {
269 if (!threadContext) {
270 return;
271 }
272 MutexLock(&threadContext->stateMutex);
273 --threadContext->interruptDepth;
274 if (threadContext->interruptDepth < 1 && mCoreThreadIsActive(threadContext)) {
275 threadContext->state = threadContext->savedState;
276 ConditionWake(&threadContext->stateCond);
277 }
278 MutexUnlock(&threadContext->stateMutex);
279}
280
281void mCoreThreadRunFunction(struct mCoreThread* threadContext, void (*run)(struct mCoreThread*)) {
282 MutexLock(&threadContext->stateMutex);
283 threadContext->run = run;
284 _waitOnInterrupt(threadContext);
285 threadContext->savedState = threadContext->state;
286 threadContext->state = THREAD_RUN_ON;
287 ConditionWake(&threadContext->stateCond);
288 _waitUntilNotState(threadContext, THREAD_RUN_ON);
289 MutexUnlock(&threadContext->stateMutex);
290}
291
292void mCoreThreadPause(struct mCoreThread* threadContext) {
293 bool frameOn = threadContext->sync.videoFrameOn;
294 MutexLock(&threadContext->stateMutex);
295 _waitOnInterrupt(threadContext);
296 if (threadContext->state == THREAD_RUNNING) {
297 _pauseThread(threadContext, false);
298 threadContext->frameWasOn = frameOn;
299 frameOn = false;
300 }
301 MutexUnlock(&threadContext->stateMutex);
302
303 mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
304}
305
306void mCoreThreadUnpause(struct mCoreThread* threadContext) {
307 bool frameOn = threadContext->sync.videoFrameOn;
308 MutexLock(&threadContext->stateMutex);
309 _waitOnInterrupt(threadContext);
310 if (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_PAUSING) {
311 threadContext->state = THREAD_RUNNING;
312 ConditionWake(&threadContext->stateCond);
313 frameOn = threadContext->frameWasOn;
314 }
315 MutexUnlock(&threadContext->stateMutex);
316
317 mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
318}
319
320bool mCoreThreadIsPaused(struct mCoreThread* threadContext) {
321 bool isPaused;
322 MutexLock(&threadContext->stateMutex);
323 _waitOnInterrupt(threadContext);
324 isPaused = threadContext->state == THREAD_PAUSED;
325 MutexUnlock(&threadContext->stateMutex);
326 return isPaused;
327}
328
329void mCoreThreadTogglePause(struct mCoreThread* threadContext) {
330 bool frameOn = threadContext->sync.videoFrameOn;
331 MutexLock(&threadContext->stateMutex);
332 _waitOnInterrupt(threadContext);
333 if (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_PAUSING) {
334 threadContext->state = THREAD_RUNNING;
335 ConditionWake(&threadContext->stateCond);
336 frameOn = threadContext->frameWasOn;
337 } else if (threadContext->state == THREAD_RUNNING) {
338 _pauseThread(threadContext, false);
339 threadContext->frameWasOn = frameOn;
340 frameOn = false;
341 }
342 MutexUnlock(&threadContext->stateMutex);
343
344 mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
345}
346
347void mCoreThreadPauseFromThread(struct mCoreThread* threadContext) {
348 bool frameOn = true;
349 MutexLock(&threadContext->stateMutex);
350 _waitOnInterrupt(threadContext);
351 if (threadContext->state == THREAD_RUNNING) {
352 _pauseThread(threadContext, true);
353 frameOn = false;
354 }
355 MutexUnlock(&threadContext->stateMutex);
356
357 mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
358}
359
360#ifdef USE_PTHREADS
361struct mCoreThread* mCoreThreadGet(void) {
362 pthread_once(&_contextOnce, _createTLS);
363 return pthread_getspecific(_contextKey);
364}
365#elif _WIN32
366struct mCoreThread* mCoreThreadGet(void) {
367 InitOnceExecuteOnce(&_contextOnce, _createTLS, NULL, 0);
368 return TlsGetValue(_contextKey);
369}
370#endif
371
372#else
373struct mCoreThread* mCoreThreadGet(void) {
374 return NULL;
375}
376#endif
377
378static void _mCoreLog(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args) {
379 printf("%s: ", mLogCategoryName(category));
380 vprintf(format, args);
381 printf("\n");
382
383}
384
385struct mLogger* mCoreThreadLogger(void) {
386 struct mCoreThread* thread = mCoreThreadGet();
387 if (thread) {
388 if (!thread->logger.log) {
389 thread->logger.log = _mCoreLog;
390 }
391 return &thread->logger;
392 }
393 return NULL;
394}
395