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 MutexLock(&threadContext->stateMutex);
166 ThreadCreate(&threadContext->thread, _mCoreThreadRun, threadContext);
167 while (threadContext->state < THREAD_RUNNING) {
168 ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
169 }
170 MutexUnlock(&threadContext->stateMutex);
171
172 return true;
173}
174
175bool mCoreThreadHasStarted(struct mCoreThread* threadContext) {
176 bool hasStarted;
177 MutexLock(&threadContext->stateMutex);
178 hasStarted = threadContext->state > THREAD_INITIALIZED;
179 MutexUnlock(&threadContext->stateMutex);
180 return hasStarted;
181}
182
183bool mCoreThreadHasExited(struct mCoreThread* threadContext) {
184 bool hasExited;
185 MutexLock(&threadContext->stateMutex);
186 hasExited = threadContext->state > THREAD_EXITING;
187 MutexUnlock(&threadContext->stateMutex);
188 return hasExited;
189}
190
191bool mCoreThreadHasCrashed(struct mCoreThread* threadContext) {
192 bool hasExited;
193 MutexLock(&threadContext->stateMutex);
194 hasExited = threadContext->state == THREAD_CRASHED;
195 MutexUnlock(&threadContext->stateMutex);
196 return hasExited;
197}
198
199void mCoreThreadEnd(struct mCoreThread* threadContext) {
200 MutexLock(&threadContext->stateMutex);
201 _waitOnInterrupt(threadContext);
202 threadContext->state = THREAD_EXITING;
203 ConditionWake(&threadContext->stateCond);
204 MutexUnlock(&threadContext->stateMutex);
205 MutexLock(&threadContext->sync.audioBufferMutex);
206 threadContext->sync.audioWait = 0;
207 ConditionWake(&threadContext->sync.audioRequiredCond);
208 MutexUnlock(&threadContext->sync.audioBufferMutex);
209
210 MutexLock(&threadContext->sync.videoFrameMutex);
211 threadContext->sync.videoFrameWait = false;
212 threadContext->sync.videoFrameOn = false;
213 ConditionWake(&threadContext->sync.videoFrameRequiredCond);
214 ConditionWake(&threadContext->sync.videoFrameAvailableCond);
215 MutexUnlock(&threadContext->sync.videoFrameMutex);
216}
217
218void mCoreThreadReset(struct mCoreThread* threadContext) {
219 MutexLock(&threadContext->stateMutex);
220 _waitOnInterrupt(threadContext);
221 threadContext->state = THREAD_RESETING;
222 ConditionWake(&threadContext->stateCond);
223 MutexUnlock(&threadContext->stateMutex);
224}
225
226void mCoreThreadJoin(struct mCoreThread* threadContext) {
227 ThreadJoin(threadContext->thread);
228
229 MutexDeinit(&threadContext->stateMutex);
230 ConditionDeinit(&threadContext->stateCond);
231
232 MutexDeinit(&threadContext->sync.videoFrameMutex);
233 ConditionWake(&threadContext->sync.videoFrameAvailableCond);
234 ConditionDeinit(&threadContext->sync.videoFrameAvailableCond);
235 ConditionWake(&threadContext->sync.videoFrameRequiredCond);
236 ConditionDeinit(&threadContext->sync.videoFrameRequiredCond);
237
238 ConditionWake(&threadContext->sync.audioRequiredCond);
239 ConditionDeinit(&threadContext->sync.audioRequiredCond);
240 MutexDeinit(&threadContext->sync.audioBufferMutex);
241}
242
243bool mCoreThreadIsActive(struct mCoreThread* threadContext) {
244 return threadContext->state >= THREAD_RUNNING && threadContext->state < THREAD_EXITING;
245}
246
247void mCoreThreadInterrupt(struct mCoreThread* threadContext) {
248 if (!threadContext) {
249 return;
250 }
251 MutexLock(&threadContext->stateMutex);
252 ++threadContext->interruptDepth;
253 if (threadContext->interruptDepth > 1 || !mCoreThreadIsActive(threadContext)) {
254 MutexUnlock(&threadContext->stateMutex);
255 return;
256 }
257 threadContext->savedState = threadContext->state;
258 _waitOnInterrupt(threadContext);
259 threadContext->state = THREAD_INTERRUPTING;
260 ConditionWake(&threadContext->stateCond);
261 _waitUntilNotState(threadContext, THREAD_INTERRUPTING);
262 MutexUnlock(&threadContext->stateMutex);
263}
264
265void mCoreThreadContinue(struct mCoreThread* threadContext) {
266 if (!threadContext) {
267 return;
268 }
269 MutexLock(&threadContext->stateMutex);
270 --threadContext->interruptDepth;
271 if (threadContext->interruptDepth < 1 && mCoreThreadIsActive(threadContext)) {
272 threadContext->state = threadContext->savedState;
273 ConditionWake(&threadContext->stateCond);
274 }
275 MutexUnlock(&threadContext->stateMutex);
276}
277
278void mCoreThreadRunFunction(struct mCoreThread* threadContext, void (*run)(struct mCoreThread*)) {
279 MutexLock(&threadContext->stateMutex);
280 threadContext->run = run;
281 _waitOnInterrupt(threadContext);
282 threadContext->savedState = threadContext->state;
283 threadContext->state = THREAD_RUN_ON;
284 ConditionWake(&threadContext->stateCond);
285 _waitUntilNotState(threadContext, THREAD_RUN_ON);
286 MutexUnlock(&threadContext->stateMutex);
287}
288
289void mCoreThreadPause(struct mCoreThread* threadContext) {
290 bool frameOn = threadContext->sync.videoFrameOn;
291 MutexLock(&threadContext->stateMutex);
292 _waitOnInterrupt(threadContext);
293 if (threadContext->state == THREAD_RUNNING) {
294 _pauseThread(threadContext, false);
295 threadContext->frameWasOn = frameOn;
296 frameOn = false;
297 }
298 MutexUnlock(&threadContext->stateMutex);
299
300 mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
301}
302
303void mCoreThreadUnpause(struct mCoreThread* threadContext) {
304 bool frameOn = threadContext->sync.videoFrameOn;
305 MutexLock(&threadContext->stateMutex);
306 _waitOnInterrupt(threadContext);
307 if (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_PAUSING) {
308 threadContext->state = THREAD_RUNNING;
309 ConditionWake(&threadContext->stateCond);
310 frameOn = threadContext->frameWasOn;
311 }
312 MutexUnlock(&threadContext->stateMutex);
313
314 mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
315}
316
317bool mCoreThreadIsPaused(struct mCoreThread* threadContext) {
318 bool isPaused;
319 MutexLock(&threadContext->stateMutex);
320 _waitOnInterrupt(threadContext);
321 isPaused = threadContext->state == THREAD_PAUSED;
322 MutexUnlock(&threadContext->stateMutex);
323 return isPaused;
324}
325
326void mCoreThreadTogglePause(struct mCoreThread* threadContext) {
327 bool frameOn = threadContext->sync.videoFrameOn;
328 MutexLock(&threadContext->stateMutex);
329 _waitOnInterrupt(threadContext);
330 if (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_PAUSING) {
331 threadContext->state = THREAD_RUNNING;
332 ConditionWake(&threadContext->stateCond);
333 frameOn = threadContext->frameWasOn;
334 } else if (threadContext->state == THREAD_RUNNING) {
335 _pauseThread(threadContext, false);
336 threadContext->frameWasOn = frameOn;
337 frameOn = false;
338 }
339 MutexUnlock(&threadContext->stateMutex);
340
341 mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
342}
343
344void mCoreThreadPauseFromThread(struct mCoreThread* threadContext) {
345 bool frameOn = true;
346 MutexLock(&threadContext->stateMutex);
347 _waitOnInterrupt(threadContext);
348 if (threadContext->state == THREAD_RUNNING) {
349 _pauseThread(threadContext, true);
350 frameOn = false;
351 }
352 MutexUnlock(&threadContext->stateMutex);
353
354 mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
355}
356
357#ifdef USE_PTHREADS
358struct mCoreThread* mCoreThreadGet(void) {
359 pthread_once(&_contextOnce, _createTLS);
360 return pthread_getspecific(_contextKey);
361}
362#elif _WIN32
363struct mCoreThread* mCoreThreadGet(void) {
364 InitOnceExecuteOnce(&_contextOnce, _createTLS, NULL, 0);
365 return TlsGetValue(_contextKey);
366}
367#endif
368
369#else
370struct mCoreThread* mCoreThreadGet(void) {
371 return NULL;
372}
373#endif
374
375static void _mCoreLog(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args) {
376 printf("%s: ", mLogCategoryName(category));
377 vprintf(format, args);
378 printf("\n");
379
380}
381
382struct mLogger* mCoreThreadLogger(void) {
383 struct mCoreThread* thread = mCoreThreadGet();
384 if (thread) {
385 if (!thread->logger.log) {
386 thread->logger.log = _mCoreLog;
387 }
388 return &thread->logger;
389 }
390 return NULL;
391}
392