all repos — mgba @ e12db1e9d8db34cc6cc070a5d4b2f00d1c42fb78

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#include <mgba/internal/gba/io.h>
 11
 12#ifndef DISABLE_THREADING
 13
 14static void mVideoThreadProxyInit(struct mVideoLogger* logger);
 15static void mVideoThreadProxyReset(struct mVideoLogger* logger);
 16static void mVideoThreadProxyDeinit(struct mVideoLogger* logger);
 17
 18static THREAD_ENTRY _proxyThread(void* renderer);
 19
 20static bool _writeData(struct mVideoLogger* logger, const void* data, size_t length);
 21static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bool block);
 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}
 43
 44void mVideoThreadProxyInit(struct mVideoLogger* logger) {
 45	struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
 46	ConditionInit(&proxyRenderer->fromThreadCond);
 47	ConditionInit(&proxyRenderer->toThreadCond);
 48	MutexInit(&proxyRenderer->mutex);
 49	RingFIFOInit(&proxyRenderer->dirtyQueue, 0x40000);
 50
 51	proxyRenderer->threadState = PROXY_THREAD_IDLE;
 52	ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer);
 53}
 54
 55void mVideoThreadProxyReset(struct mVideoLogger* logger) {
 56	struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
 57	MutexLock(&proxyRenderer->mutex);
 58	while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
 59		ConditionWake(&proxyRenderer->toThreadCond);
 60		ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
 61	}
 62	MutexUnlock(&proxyRenderer->mutex);
 63}
 64
 65void mVideoThreadProxyDeinit(struct mVideoLogger* logger) {
 66	struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
 67	bool waiting = false;
 68	MutexLock(&proxyRenderer->mutex);
 69	while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
 70		ConditionWake(&proxyRenderer->toThreadCond);
 71		ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
 72	}
 73	if (proxyRenderer->threadState == PROXY_THREAD_IDLE) {
 74		proxyRenderer->threadState = PROXY_THREAD_STOPPED;
 75		ConditionWake(&proxyRenderer->toThreadCond);
 76		waiting = true;
 77	}
 78	MutexUnlock(&proxyRenderer->mutex);
 79	if (waiting) {
 80		ThreadJoin(proxyRenderer->thread);
 81	}
 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, "Proxy thread can't read VRAM. CPU thread asleep?");
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 (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
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