all repos — mgba @ 53d9e6b43213d9eed2c7bc5b38d16f4fdad37bc3

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	while (RingFIFOSize(&proxyRenderer->dirtyQueue)) {
157		ConditionWake(&proxyRenderer->toThreadCond);
158		ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
159	}
160}
161
162static void _unlock(struct mVideoLogger* logger) {
163	struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
164	MutexUnlock(&proxyRenderer->mutex);
165}
166
167static void _wake(struct mVideoLogger* logger, int y) {
168	struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
169	if ((y & 15) == 15) {
170		ConditionWake(&proxyRenderer->toThreadCond);
171	}
172}
173
174static THREAD_ENTRY _proxyThread(void* logger) {
175	struct mVideoThreadProxy* proxyRenderer = logger;
176	ThreadSetName("Proxy Renderer Thread");
177
178	MutexLock(&proxyRenderer->mutex);
179	while (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
180		ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
181		if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
182			break;
183		}
184		proxyRenderer->threadState = PROXY_THREAD_BUSY;
185		if (proxyRenderer->event) {
186			proxyRenderer->d.handleEvent(&proxyRenderer->d, proxyRenderer->event);
187			proxyRenderer->event = 0;
188		} else {
189			MutexUnlock(&proxyRenderer->mutex);
190			if (!mVideoLoggerRendererRun(&proxyRenderer->d, false)) {
191				// FIFO was corrupted
192				proxyRenderer->threadState = PROXY_THREAD_STOPPED;
193				mLOG(GBA_VIDEO, ERROR, "Proxy thread queue got corrupted!");
194			}
195			MutexLock(&proxyRenderer->mutex);
196		}
197		ConditionWake(&proxyRenderer->fromThreadCond);
198		if (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
199			proxyRenderer->threadState = PROXY_THREAD_IDLE;
200		}
201	}
202	MutexUnlock(&proxyRenderer->mutex);
203
204#ifdef _3DS
205	svcExitThread();
206#endif
207	return 0;
208}
209
210#endif