all repos — mgba @ fdfab146a0deb24616034f6b9bb7d76a5f065e56

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	ConditionDeinit(&proxyRenderer->fromThreadCond);
 82	ConditionDeinit(&proxyRenderer->toThreadCond);
 83	MutexDeinit(&proxyRenderer->mutex);
 84}
 85
 86void _proxyThreadRecover(struct mVideoThreadProxy* proxyRenderer) {
 87	MutexLock(&proxyRenderer->mutex);
 88	if (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
 89		MutexUnlock(&proxyRenderer->mutex);
 90		return;
 91	}
 92	RingFIFOClear(&proxyRenderer->dirtyQueue);
 93	MutexUnlock(&proxyRenderer->mutex);
 94	ThreadJoin(proxyRenderer->thread);
 95	proxyRenderer->threadState = PROXY_THREAD_IDLE;
 96	ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer);
 97}
 98
 99static bool _writeData(struct mVideoLogger* logger, const void* data, size_t length) {
100	struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
101	while (!RingFIFOWrite(&proxyRenderer->dirtyQueue, data, length)) {
102		mLOG(GBA_VIDEO, DEBUG, "Can't write %"PRIz"u bytes. Proxy thread asleep?", length);
103		MutexLock(&proxyRenderer->mutex);
104		if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
105			mLOG(GBA_VIDEO, ERROR, "Proxy thread stopped prematurely!");
106			MutexUnlock(&proxyRenderer->mutex);
107			return false;
108		}
109		ConditionWake(&proxyRenderer->toThreadCond);
110		ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
111		MutexUnlock(&proxyRenderer->mutex);
112	}
113	return true;
114}
115
116static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bool block) {
117	struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
118	bool read = false;
119	while (true) {
120		read = RingFIFORead(&proxyRenderer->dirtyQueue, data, length);
121		if (!block || read) {
122			break;
123		}
124		mLOG(GBA_VIDEO, DEBUG, "Can't read %"PRIz"u bytes. CPU thread asleep?", length);
125		MutexLock(&proxyRenderer->mutex);
126		ConditionWake(&proxyRenderer->fromThreadCond);
127		ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
128		MutexUnlock(&proxyRenderer->mutex);
129	}
130	return read;
131}
132
133static void _lock(struct mVideoLogger* logger) {
134	struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
135	MutexLock(&proxyRenderer->mutex);
136}
137
138static void _wait(struct mVideoLogger* logger) {
139	struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
140	if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
141		mLOG(GBA_VIDEO, ERROR, "Proxy thread stopped prematurely!");
142		_proxyThreadRecover(proxyRenderer);
143		return;
144	}
145	while (RingFIFOSize(&proxyRenderer->dirtyQueue)) {
146		ConditionWake(&proxyRenderer->toThreadCond);
147		ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
148	}
149}
150
151static void _unlock(struct mVideoLogger* logger) {
152	struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
153	MutexUnlock(&proxyRenderer->mutex);
154}
155
156static void _wake(struct mVideoLogger* logger, int y) {
157	struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
158	if ((y & 15) == 15) {
159		ConditionWake(&proxyRenderer->toThreadCond);
160	}
161}
162
163static THREAD_ENTRY _proxyThread(void* logger) {
164	struct mVideoThreadProxy* proxyRenderer = logger;
165	ThreadSetName("Proxy Renderer Thread");
166
167	MutexLock(&proxyRenderer->mutex);
168	while (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
169		ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
170		if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
171			break;
172		}
173		proxyRenderer->threadState = PROXY_THREAD_BUSY;
174		MutexUnlock(&proxyRenderer->mutex);
175		if (!mVideoLoggerRendererRun(&proxyRenderer->d, false)) {
176			// FIFO was corrupted
177			proxyRenderer->threadState = PROXY_THREAD_STOPPED;
178			mLOG(GBA_VIDEO, ERROR, "Proxy thread queue got corrupted!");
179		}
180		MutexLock(&proxyRenderer->mutex);
181		ConditionWake(&proxyRenderer->fromThreadCond);
182		if (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
183			proxyRenderer->threadState = PROXY_THREAD_IDLE;
184		}
185	}
186	MutexUnlock(&proxyRenderer->mutex);
187
188#ifdef _3DS
189	svcExitThread();
190#endif
191	return 0;
192}
193
194#endif