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