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