all repos — mgba @ 42813bb197dee028b64ba70b0a9b63aea2cf264c

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);
 21
 22static void _lock(struct mVideoLogger* logger);
 23static void _unlock(struct mVideoLogger* logger);
 24static void _wait(struct mVideoLogger* logger);
 25static void _wake(struct mVideoLogger* logger, int y);
 26
 27void mVideoThreadProxyCreate(struct mVideoThreadProxy* renderer) {
 28	mVideoLoggerRendererCreate(&renderer->d, false);
 29	renderer->d.block = true;
 30
 31	renderer->d.init = mVideoThreadProxyInit;
 32	renderer->d.reset = mVideoThreadProxyReset;
 33	renderer->d.deinit = mVideoThreadProxyDeinit;
 34	renderer->d.lock = _lock;
 35	renderer->d.unlock = _unlock;
 36	renderer->d.wait = _wait;
 37	renderer->d.wake = _wake;
 38
 39	renderer->d.writeData = _writeData;
 40	renderer->d.readData = _readData;
 41}
 42
 43void mVideoThreadProxyInit(struct mVideoLogger* logger) {
 44	struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
 45	ConditionInit(&proxyRenderer->fromThreadCond);
 46	ConditionInit(&proxyRenderer->toThreadCond);
 47	MutexInit(&proxyRenderer->mutex);
 48	RingFIFOInit(&proxyRenderer->dirtyQueue, 0x40000);
 49
 50	proxyRenderer->threadState = PROXY_THREAD_IDLE;
 51	ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer);
 52}
 53
 54void mVideoThreadProxyReset(struct mVideoLogger* logger) {
 55	struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
 56	MutexLock(&proxyRenderer->mutex);
 57	while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
 58		ConditionWake(&proxyRenderer->toThreadCond);
 59		ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
 60	}
 61	MutexUnlock(&proxyRenderer->mutex);
 62}
 63
 64void mVideoThreadProxyDeinit(struct mVideoLogger* logger) {
 65	struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
 66	bool waiting = false;
 67	MutexLock(&proxyRenderer->mutex);
 68	while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
 69		ConditionWake(&proxyRenderer->toThreadCond);
 70		ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
 71	}
 72	if (proxyRenderer->threadState == PROXY_THREAD_IDLE) {
 73		proxyRenderer->threadState = PROXY_THREAD_STOPPED;
 74		ConditionWake(&proxyRenderer->toThreadCond);
 75		waiting = true;
 76	}
 77	MutexUnlock(&proxyRenderer->mutex);
 78	if (waiting) {
 79		ThreadJoin(proxyRenderer->thread);
 80	}
 81	RingFIFODeinit(&proxyRenderer->dirtyQueue);
 82	ConditionDeinit(&proxyRenderer->fromThreadCond);
 83	ConditionDeinit(&proxyRenderer->toThreadCond);
 84	MutexDeinit(&proxyRenderer->mutex);
 85}
 86
 87void _proxyThreadRecover(struct mVideoThreadProxy* proxyRenderer) {
 88	MutexLock(&proxyRenderer->mutex);
 89	if (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
 90		MutexUnlock(&proxyRenderer->mutex);
 91		return;
 92	}
 93	RingFIFOClear(&proxyRenderer->dirtyQueue);
 94	MutexUnlock(&proxyRenderer->mutex);
 95	ThreadJoin(proxyRenderer->thread);
 96	proxyRenderer->threadState = PROXY_THREAD_IDLE;
 97	ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer);
 98}
 99
100static bool _writeData(struct mVideoLogger* logger, const void* data, size_t length) {
101	struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
102	while (!RingFIFOWrite(&proxyRenderer->dirtyQueue, data, length)) {
103		mLOG(GBA_VIDEO, DEBUG, "Can't write %"PRIz"u bytes. Proxy thread asleep?", length);
104		MutexLock(&proxyRenderer->mutex);
105		if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
106			mLOG(GBA_VIDEO, ERROR, "Proxy thread stopped prematurely!");
107			MutexUnlock(&proxyRenderer->mutex);
108			return false;
109		}
110		ConditionWake(&proxyRenderer->toThreadCond);
111		ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
112		MutexUnlock(&proxyRenderer->mutex);
113	}
114	return true;
115}
116
117static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bool block) {
118	struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
119	bool read = false;
120	while (true) {
121		read = RingFIFORead(&proxyRenderer->dirtyQueue, data, length);
122		if (!block || read) {
123			break;
124		}
125		mLOG(GBA_VIDEO, DEBUG, "Can't read %"PRIz"u bytes. CPU thread asleep?", length);
126		MutexLock(&proxyRenderer->mutex);
127		ConditionWake(&proxyRenderer->fromThreadCond);
128		ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
129		MutexUnlock(&proxyRenderer->mutex);
130	}
131	return read;
132}
133
134static void _lock(struct mVideoLogger* logger) {
135	struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
136	MutexLock(&proxyRenderer->mutex);
137}
138
139static void _wait(struct mVideoLogger* logger) {
140	struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
141	if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
142		mLOG(GBA_VIDEO, ERROR, "Proxy thread stopped prematurely!");
143		_proxyThreadRecover(proxyRenderer);
144		return;
145	}
146	while (RingFIFOSize(&proxyRenderer->dirtyQueue)) {
147		ConditionWake(&proxyRenderer->toThreadCond);
148		ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
149	}
150}
151
152static void _unlock(struct mVideoLogger* logger) {
153	struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
154	MutexUnlock(&proxyRenderer->mutex);
155}
156
157static void _wake(struct mVideoLogger* logger, int y) {
158	struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
159	if ((y & 15) == 15) {
160		ConditionWake(&proxyRenderer->toThreadCond);
161	}
162}
163
164static THREAD_ENTRY _proxyThread(void* logger) {
165	struct mVideoThreadProxy* proxyRenderer = logger;
166	ThreadSetName("Proxy Renderer Thread");
167
168	MutexLock(&proxyRenderer->mutex);
169	while (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
170		ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
171		if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
172			break;
173		}
174		proxyRenderer->threadState = PROXY_THREAD_BUSY;
175		MutexUnlock(&proxyRenderer->mutex);
176		if (!mVideoLoggerRendererRun(&proxyRenderer->d, false)) {
177			// FIFO was corrupted
178			proxyRenderer->threadState = PROXY_THREAD_STOPPED;
179			mLOG(GBA_VIDEO, ERROR, "Proxy thread queue got corrupted!");
180		}
181		MutexLock(&proxyRenderer->mutex);
182		ConditionWake(&proxyRenderer->fromThreadCond);
183		if (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
184			proxyRenderer->threadState = PROXY_THREAD_IDLE;
185		}
186	}
187	MutexUnlock(&proxyRenderer->mutex);
188
189#ifdef _3DS
190	svcExitThread();
191#endif
192	return 0;
193}
194
195#endif