all repos — mgba @ 70afe23fe4b7609ca85ee080b8dca255ff6d325a

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