src/gba/gba-thread.c (view raw)
1#include "gba-thread.h"
2
3#include "arm.h"
4#include "debugger.h"
5#include "gba.h"
6
7#include <stdlib.h>
8#include <signal.h>
9
10#ifdef USE_PTHREADS
11static pthread_key_t _contextKey;
12static pthread_once_t _contextOnce = PTHREAD_ONCE_INIT;
13
14static void _createTLS(void) {
15 pthread_key_create(&_contextKey, 0);
16}
17#else
18static DWORD _contextKey;
19static INIT_ONCE _contextOnce = INIT_ONCE_STATIC_INIT;
20
21static BOOL CALLBACK _createTLS(PINIT_ONCE once, PVOID param, PVOID* context) {
22 (void) (once);
23 (void) (param);
24 (void) (context);
25 _contextKey = TlsAlloc();
26 return TRUE;
27}
28#endif
29
30static void _changeState(struct GBAThread* threadContext, enum ThreadState newState, int broadcast) {
31 MutexLock(&threadContext->stateMutex);
32 threadContext->state = newState;
33 if (broadcast) {
34 ConditionWake(&threadContext->stateCond);
35 }
36 MutexUnlock(&threadContext->stateMutex);
37}
38
39static THREAD_ENTRY _GBAThreadRun(void* context) {
40#ifdef USE_PTHREADS
41 pthread_once(&_contextOnce, _createTLS);
42#else
43 InitOnceExecuteOnce(&_contextOnce, _createTLS, NULL, 0);
44#endif
45
46#ifdef USE_DEBUGGER
47 struct ARMDebugger debugger;
48#endif
49 struct GBA gba;
50 struct GBAThread* threadContext = context;
51 char* savedata = 0;
52
53#if !defined(_WIN32) && defined(USE_PTHREADS)
54 sigset_t signals;
55 sigemptyset(&signals);
56 pthread_sigmask(SIG_SETMASK, &signals, 0);
57#endif
58
59 GBAInit(&gba);
60 threadContext->gba = &gba;
61 gba.sync = &threadContext->sync;
62#ifdef USE_PTHREADS
63 pthread_setspecific(_contextKey, threadContext);
64#else
65 TlsSetValue(_contextKey, threadContext);
66#endif
67 if (threadContext->renderer) {
68 GBAVideoAssociateRenderer(&gba.video, threadContext->renderer);
69 }
70
71 if (threadContext->fd >= 0) {
72 if (threadContext->fname) {
73 char* dotPoint = strrchr(threadContext->fname, '.');
74 if (dotPoint > strrchr(threadContext->fname, '/') && dotPoint[1] && dotPoint[2] && dotPoint[3]) {
75 savedata = strdup(threadContext->fname);
76 dotPoint = strrchr(savedata, '.');
77 dotPoint[1] = 's';
78 dotPoint[2] = 'a';
79 dotPoint[3] = 'v';
80 dotPoint[4] = '\0';
81 } else if (dotPoint) {
82 savedata = malloc((dotPoint - threadContext->fname + 5) * sizeof(char));
83 strncpy(savedata, threadContext->fname, dotPoint - threadContext->fname + 1);
84 strcat(savedata, "sav");
85 } else {
86 savedata = malloc(strlen(threadContext->fname + 5));
87 strcpy(savedata, threadContext->fname);
88 strcat(savedata, "sav");
89 }
90 }
91 gba.savefile = savedata;
92 GBALoadROM(&gba, threadContext->fd, threadContext->fname);
93 }
94
95#ifdef USE_DEBUGGER
96 if (threadContext->useDebugger) {
97 threadContext->debugger = &debugger;
98 GBAAttachDebugger(&gba, &debugger);
99 } else {
100 threadContext->debugger = 0;
101 }
102#else
103 threadContext->debugger = 0;
104#endif
105
106 gba.keySource = &threadContext->activeKeys;
107
108 if (threadContext->startCallback) {
109 threadContext->startCallback(threadContext);
110 }
111
112 _changeState(threadContext, THREAD_RUNNING, 1);
113
114 while (threadContext->state < THREAD_EXITING) {
115#ifdef USE_DEBUGGER
116 if (threadContext->useDebugger) {
117 ARMDebuggerRun(&debugger);
118 if (debugger.state == DEBUGGER_SHUTDOWN) {
119 _changeState(threadContext, THREAD_EXITING, 0);
120 }
121 } else {
122#endif
123 while (threadContext->state == THREAD_RUNNING) {
124 ARMRun(&gba.cpu);
125 }
126#ifdef USE_DEBUGGER
127 }
128#endif
129 MutexLock(&threadContext->stateMutex);
130 while (threadContext->state == THREAD_PAUSED) {
131 ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
132 }
133 MutexUnlock(&threadContext->stateMutex);
134 }
135
136 while (threadContext->state != THREAD_SHUTDOWN) {
137 _changeState(threadContext, THREAD_SHUTDOWN, 0);
138 }
139
140 if (threadContext->cleanCallback) {
141 threadContext->cleanCallback(threadContext);
142 }
143
144 GBADeinit(&gba);
145
146 ConditionWake(&threadContext->sync.videoFrameAvailableCond);
147 ConditionWake(&threadContext->sync.audioRequiredCond);
148 free(savedata);
149
150 return 0;
151}
152
153int GBAThreadStart(struct GBAThread* threadContext) {
154 // TODO: error check
155 threadContext->activeKeys = 0;
156 threadContext->state = THREAD_INITIALIZED;
157 threadContext->sync.videoFrameOn = 1;
158 threadContext->sync.videoFrameSkip = 0;
159
160 MutexInit(&threadContext->stateMutex);
161 ConditionInit(&threadContext->stateCond);
162
163 MutexInit(&threadContext->sync.videoFrameMutex);
164 ConditionInit(&threadContext->sync.videoFrameAvailableCond);
165 ConditionInit(&threadContext->sync.videoFrameRequiredCond);
166 MutexInit(&threadContext->sync.audioBufferMutex);
167 ConditionInit(&threadContext->sync.audioRequiredCond);
168
169#ifndef _WIN32
170 sigset_t signals;
171 sigemptyset(&signals);
172 sigaddset(&signals, SIGINT);
173 sigaddset(&signals, SIGTRAP);
174 pthread_sigmask(SIG_BLOCK, &signals, 0);
175#endif
176
177 MutexLock(&threadContext->stateMutex);
178 ThreadCreate(&threadContext->thread, _GBAThreadRun, threadContext);
179 while (threadContext->state < THREAD_RUNNING) {
180 ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
181 }
182 MutexUnlock(&threadContext->stateMutex);
183
184 return 0;
185}
186
187void GBAThreadJoin(struct GBAThread* threadContext) {
188 MutexLock(&threadContext->sync.videoFrameMutex);
189 threadContext->sync.videoFrameWait = 0;
190 ConditionWake(&threadContext->sync.videoFrameRequiredCond);
191 MutexUnlock(&threadContext->sync.videoFrameMutex);
192
193 ThreadJoin(threadContext->thread);
194
195 MutexDeinit(&threadContext->stateMutex);
196 ConditionDeinit(&threadContext->stateCond);
197
198 MutexDeinit(&threadContext->sync.videoFrameMutex);
199 ConditionWake(&threadContext->sync.videoFrameAvailableCond);
200 ConditionDeinit(&threadContext->sync.videoFrameAvailableCond);
201 ConditionWake(&threadContext->sync.videoFrameRequiredCond);
202 ConditionDeinit(&threadContext->sync.videoFrameRequiredCond);
203
204 ConditionWake(&threadContext->sync.audioRequiredCond);
205 ConditionDeinit(&threadContext->sync.audioRequiredCond);
206 MutexDeinit(&threadContext->sync.audioBufferMutex);
207}
208
209void GBAThreadPause(struct GBAThread* threadContext) {
210 int frameOn = 1;
211 MutexLock(&threadContext->stateMutex);
212 if (threadContext->state == THREAD_RUNNING) {
213 if (threadContext->debugger && threadContext->debugger->state == DEBUGGER_RUNNING) {
214 threadContext->debugger->state = DEBUGGER_EXITING;
215 }
216 threadContext->state = THREAD_PAUSED;
217 frameOn = 0;
218 }
219 MutexUnlock(&threadContext->stateMutex);
220 MutexLock(&threadContext->sync.videoFrameMutex);
221 if (frameOn != threadContext->sync.videoFrameOn) {
222 threadContext->sync.videoFrameOn = frameOn;
223 ConditionWake(&threadContext->sync.videoFrameAvailableCond);
224 }
225 MutexUnlock(&threadContext->sync.videoFrameMutex);
226}
227
228void GBAThreadUnpause(struct GBAThread* threadContext) {
229 int frameOn = 1;
230 MutexLock(&threadContext->stateMutex);
231 if (threadContext->state == THREAD_PAUSED) {
232 threadContext->state = THREAD_RUNNING;
233 ConditionWake(&threadContext->stateCond);
234 }
235 MutexUnlock(&threadContext->stateMutex);
236 MutexLock(&threadContext->sync.videoFrameMutex);
237 if (frameOn != threadContext->sync.videoFrameOn) {
238 threadContext->sync.videoFrameOn = frameOn;
239 ConditionWake(&threadContext->sync.videoFrameAvailableCond);
240 }
241 MutexUnlock(&threadContext->sync.videoFrameMutex);
242}
243
244void GBAThreadTogglePause(struct GBAThread* threadContext) {
245 int frameOn = 1;
246 MutexLock(&threadContext->stateMutex);
247 if (threadContext->state == THREAD_PAUSED) {
248 threadContext->state = THREAD_RUNNING;
249 ConditionWake(&threadContext->stateCond);
250 } else if (threadContext->state == THREAD_RUNNING) {
251 if (threadContext->debugger && threadContext->debugger->state == DEBUGGER_RUNNING) {
252 threadContext->debugger->state = DEBUGGER_EXITING;
253 }
254 threadContext->state = THREAD_PAUSED;
255 frameOn = 0;
256 }
257 MutexUnlock(&threadContext->stateMutex);
258 MutexLock(&threadContext->sync.videoFrameMutex);
259 if (frameOn != threadContext->sync.videoFrameOn) {
260 threadContext->sync.videoFrameOn = frameOn;
261 ConditionWake(&threadContext->sync.videoFrameAvailableCond);
262 }
263 MutexUnlock(&threadContext->sync.videoFrameMutex);
264}
265
266
267#ifdef USE_PTHREADS
268struct GBAThread* GBAThreadGetContext(void) {
269 pthread_once(&_contextOnce, _createTLS);
270 return pthread_getspecific(_contextKey);
271}
272#else
273struct GBAThread* GBAThreadGetContext(void) {
274 InitOnceExecuteOnce(&_contextOnce, _createTLS, NULL, 0);
275 return TlsGetValue(_contextKey);
276}
277#endif
278
279void GBASyncPostFrame(struct GBASync* sync) {
280 if (!sync) {
281 return;
282 }
283
284 MutexLock(&sync->videoFrameMutex);
285 ++sync->videoFramePending;
286 --sync->videoFrameSkip;
287 if (sync->videoFrameSkip < 0) {
288 ConditionWake(&sync->videoFrameAvailableCond);
289 while (sync->videoFrameWait && sync->videoFramePending) {
290 ConditionWait(&sync->videoFrameRequiredCond, &sync->videoFrameMutex);
291 }
292 }
293 MutexUnlock(&sync->videoFrameMutex);
294
295 struct GBAThread* thread = GBAThreadGetContext();
296 if (thread->frameCallback) {
297 thread->frameCallback(thread);
298 }
299}
300
301int GBASyncWaitFrameStart(struct GBASync* sync, int frameskip) {
302 if (!sync) {
303 return 1;
304 }
305
306 MutexLock(&sync->videoFrameMutex);
307 ConditionWake(&sync->videoFrameRequiredCond);
308 if (!sync->videoFrameOn) {
309 return 0;
310 }
311 ConditionWait(&sync->videoFrameAvailableCond, &sync->videoFrameMutex);
312 sync->videoFramePending = 0;
313 sync->videoFrameSkip = frameskip;
314 return 1;
315}
316
317void GBASyncWaitFrameEnd(struct GBASync* sync) {
318 if (!sync) {
319 return;
320 }
321
322 MutexUnlock(&sync->videoFrameMutex);
323}
324
325int GBASyncDrawingFrame(struct GBASync* sync) {
326 return sync->videoFrameSkip <= 0;
327}
328
329void GBASyncProduceAudio(struct GBASync* sync, int wait) {
330 if (sync->audioWait && wait) {
331 // TODO loop properly in event of spurious wakeups
332 ConditionWait(&sync->audioRequiredCond, &sync->audioBufferMutex);
333 }
334 MutexUnlock(&sync->audioBufferMutex);
335}
336
337void GBASyncLockAudio(struct GBASync* sync) {
338 MutexLock(&sync->audioBufferMutex);
339}
340
341void GBASyncConsumeAudio(struct GBASync* sync) {
342 ConditionWake(&sync->audioRequiredCond);
343 MutexUnlock(&sync->audioBufferMutex);
344}