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