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