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