all repos — mgba @ 6822a0d7709b19438fb51b5ff0fd8a6a0f8a1275

mGBA Game Boy Advance Emulator

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