all repos — mgba @ b1828dbc59b8a9cc080c8c7f9472c46a3d39e90c

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