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