all repos — mgba @ eeee6fe44eb11e77774c7bb3f08c2016682d33cd

mGBA Game Boy Advance Emulator

src/feature/thread-proxy.c (view raw)

  1/* Copyright (c) 2013-2017 Jeffrey Pfau
  2 *
  3 * This Source Code Form is subject to the terms of the Mozilla Public
  4 * License, v. 2.0. If a copy of the MPL was not distributed with this
  5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6#include <mgba/feature/thread-proxy.h>
  7
  8#include <mgba/core/tile-cache.h>
  9#include <mgba/internal/gba/gba.h>
 10
 11#ifndef DISABLE_THREADING
 12
 13static void mVideoThreadProxyInit(struct mVideoLogger* logger);
 14static void mVideoThreadProxyReset(struct mVideoLogger* logger);
 15static void mVideoThreadProxyDeinit(struct mVideoLogger* logger);
 16
 17static THREAD_ENTRY _proxyThread(void* renderer);
 18
 19static bool _writeData(struct mVideoLogger* logger, const void* data, size_t length);
 20static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bool block);
 21static void _postEvent(struct mVideoLogger* logger, enum mVideoLoggerEvent);
 22
 23static void _lock(struct mVideoLogger* logger);
 24static void _unlock(struct mVideoLogger* logger);
 25static void _wait(struct mVideoLogger* logger);
 26static void _wake(struct mVideoLogger* logger, int y);
 27
 28void mVideoThreadProxyCreate(struct mVideoThreadProxy* renderer) {
 29	mVideoLoggerRendererCreate(&renderer->d, false);
 30	renderer->d.block = true;
 31
 32	renderer->d.init = mVideoThreadProxyInit;
 33	renderer->d.reset = mVideoThreadProxyReset;
 34	renderer->d.deinit = mVideoThreadProxyDeinit;
 35	renderer->d.lock = _lock;
 36	renderer->d.unlock = _unlock;
 37	renderer->d.wait = _wait;
 38	renderer->d.wake = _wake;
 39
 40	renderer->d.writeData = _writeData;
 41	renderer->d.readData = _readData;
 42	renderer->d.postEvent = _postEvent;
 43}
 44
 45void mVideoThreadProxyInit(struct mVideoLogger* logger) {
 46	struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
 47	ConditionInit(&proxyRenderer->fromThreadCond);
 48	ConditionInit(&proxyRenderer->toThreadCond);
 49	MutexInit(&proxyRenderer->mutex);
 50	RingFIFOInit(&proxyRenderer->dirtyQueue, 0x40000);
 51
 52	proxyRenderer->threadState = PROXY_THREAD_IDLE;
 53	ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer);
 54}
 55
 56void mVideoThreadProxyReset(struct mVideoLogger* logger) {
 57	struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
 58	MutexLock(&proxyRenderer->mutex);
 59	while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
 60		ConditionWake(&proxyRenderer->toThreadCond);
 61		ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
 62	}
 63	MutexUnlock(&proxyRenderer->mutex);
 64}
 65
 66void mVideoThreadProxyDeinit(struct mVideoLogger* logger) {
 67	struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
 68	bool waiting = false;
 69	MutexLock(&proxyRenderer->mutex);
 70	while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
 71		ConditionWake(&proxyRenderer->toThreadCond);
 72		ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
 73	}
 74	if (proxyRenderer->threadState == PROXY_THREAD_IDLE) {
 75		proxyRenderer->threadState = PROXY_THREAD_STOPPED;
 76		ConditionWake(&proxyRenderer->toThreadCond);
 77		waiting = true;
 78	}
 79	MutexUnlock(&proxyRenderer->mutex);
 80	if (waiting) {
 81		ThreadJoin(proxyRenderer->thread);
 82	}
 83	RingFIFODeinit(&proxyRenderer->dirtyQueue);
 84	ConditionDeinit(&proxyRenderer->fromThreadCond);
 85	ConditionDeinit(&proxyRenderer->toThreadCond);
 86	MutexDeinit(&proxyRenderer->mutex);
 87}
 88
 89void _proxyThreadRecover(struct mVideoThreadProxy* proxyRenderer) {
 90	MutexLock(&proxyRenderer->mutex);
 91	if (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
 92		MutexUnlock(&proxyRenderer->mutex);
 93		return;
 94	}
 95	RingFIFOClear(&proxyRenderer->dirtyQueue);
 96	MutexUnlock(&proxyRenderer->mutex);
 97	ThreadJoin(proxyRenderer->thread);
 98	proxyRenderer->threadState = PROXY_THREAD_IDLE;
 99	ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer);
100}
101
102static bool _writeData(struct mVideoLogger* logger, const void* data, size_t length) {
103	struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
104	while (!RingFIFOWrite(&proxyRenderer->dirtyQueue, data, length)) {
105		mLOG(GBA_VIDEO, DEBUG, "Can't write %"PRIz"u bytes. Proxy thread asleep?", length);
106		MutexLock(&proxyRenderer->mutex);
107		if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
108			mLOG(GBA_VIDEO, ERROR, "Proxy thread stopped prematurely!");
109			MutexUnlock(&proxyRenderer->mutex);
110			return false;
111		}
112		ConditionWake(&proxyRenderer->toThreadCond);
113		ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
114		MutexUnlock(&proxyRenderer->mutex);
115	}
116	return true;
117}
118
119static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bool block) {
120	struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
121	bool read = false;
122	while (true) {
123		read = RingFIFORead(&proxyRenderer->dirtyQueue, data, length);
124		if (!block || read) {
125			break;
126		}
127		mLOG(GBA_VIDEO, DEBUG, "Can't read %"PRIz"u bytes. CPU thread asleep?", length);
128		MutexLock(&proxyRenderer->mutex);
129		ConditionWake(&proxyRenderer->fromThreadCond);
130		ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
131		MutexUnlock(&proxyRenderer->mutex);
132	}
133	return read;
134}
135
136static void _postEvent(struct mVideoLogger* logger, enum mVideoLoggerEvent event) {
137	struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
138	MutexLock(&proxyRenderer->mutex);
139	proxyRenderer->event = event;
140	ConditionWake(&proxyRenderer->toThreadCond);
141	MutexUnlock(&proxyRenderer->mutex);
142}
143
144static void _lock(struct mVideoLogger* logger) {
145	struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
146	MutexLock(&proxyRenderer->mutex);
147}
148
149static void _wait(struct mVideoLogger* logger) {
150	struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
151	if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
152		mLOG(GBA_VIDEO, ERROR, "Proxy thread stopped prematurely!");
153		_proxyThreadRecover(proxyRenderer);
154		return;
155	}
156	MutexLock(&proxyRenderer->mutex);
157	while (RingFIFOSize(&proxyRenderer->dirtyQueue)) {
158		ConditionWake(&proxyRenderer->toThreadCond);
159		ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
160	}
161	MutexUnlock(&proxyRenderer->mutex);
162}
163
164static void _unlock(struct mVideoLogger* logger) {
165	struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
166	MutexUnlock(&proxyRenderer->mutex);
167}
168
169static void _wake(struct mVideoLogger* logger, int y) {
170	struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
171	if ((y & 15) == 15) {
172		ConditionWake(&proxyRenderer->toThreadCond);
173	}
174}
175
176static THREAD_ENTRY _proxyThread(void* logger) {
177	struct mVideoThreadProxy* proxyRenderer = logger;
178	ThreadSetName("Proxy Renderer Thread");
179
180	MutexLock(&proxyRenderer->mutex);
181	while (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
182		ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
183		if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
184			break;
185		}
186		proxyRenderer->threadState = PROXY_THREAD_BUSY;
187		if (proxyRenderer->event) {
188			proxyRenderer->d.handleEvent(&proxyRenderer->d, proxyRenderer->event);
189			proxyRenderer->event = 0;
190		} else {
191			MutexUnlock(&proxyRenderer->mutex);
192			if (!mVideoLoggerRendererRun(&proxyRenderer->d, false)) {
193				// FIFO was corrupted
194				proxyRenderer->threadState = PROXY_THREAD_STOPPED;
195				mLOG(GBA_VIDEO, ERROR, "Proxy thread queue got corrupted!");
196			}
197			MutexLock(&proxyRenderer->mutex);
198		}
199		ConditionWake(&proxyRenderer->fromThreadCond);
200		if (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
201			proxyRenderer->threadState = PROXY_THREAD_IDLE;
202		}
203	}
204	MutexUnlock(&proxyRenderer->mutex);
205
206#ifdef _3DS
207	svcExitThread();
208#endif
209	return 0;
210}
211
212#endif