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