all repos — mgba @ c3259da78db245fd77901cd16a8ccd8014f80072

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