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);
21
22static void _lock(struct mVideoLogger* logger);
23static void _unlock(struct mVideoLogger* logger);
24static void _wait(struct mVideoLogger* logger);
25static void _wake(struct mVideoLogger* logger, int y);
26
27void mVideoThreadProxyCreate(struct mVideoThreadProxy* renderer) {
28 mVideoLoggerRendererCreate(&renderer->d, false);
29 renderer->d.block = true;
30
31 renderer->d.init = mVideoThreadProxyInit;
32 renderer->d.reset = mVideoThreadProxyReset;
33 renderer->d.deinit = mVideoThreadProxyDeinit;
34 renderer->d.lock = _lock;
35 renderer->d.unlock = _unlock;
36 renderer->d.wait = _wait;
37 renderer->d.wake = _wake;
38
39 renderer->d.writeData = _writeData;
40 renderer->d.readData = _readData;
41}
42
43void mVideoThreadProxyInit(struct mVideoLogger* logger) {
44 struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
45 ConditionInit(&proxyRenderer->fromThreadCond);
46 ConditionInit(&proxyRenderer->toThreadCond);
47 MutexInit(&proxyRenderer->mutex);
48 RingFIFOInit(&proxyRenderer->dirtyQueue, 0x40000);
49
50 proxyRenderer->threadState = PROXY_THREAD_IDLE;
51 ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer);
52}
53
54void mVideoThreadProxyReset(struct mVideoLogger* logger) {
55 struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
56 MutexLock(&proxyRenderer->mutex);
57 while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
58 ConditionWake(&proxyRenderer->toThreadCond);
59 ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
60 }
61 MutexUnlock(&proxyRenderer->mutex);
62}
63
64void mVideoThreadProxyDeinit(struct mVideoLogger* logger) {
65 struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
66 bool waiting = false;
67 MutexLock(&proxyRenderer->mutex);
68 while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
69 ConditionWake(&proxyRenderer->toThreadCond);
70 ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
71 }
72 if (proxyRenderer->threadState == PROXY_THREAD_IDLE) {
73 proxyRenderer->threadState = PROXY_THREAD_STOPPED;
74 ConditionWake(&proxyRenderer->toThreadCond);
75 waiting = true;
76 }
77 MutexUnlock(&proxyRenderer->mutex);
78 if (waiting) {
79 ThreadJoin(proxyRenderer->thread);
80 }
81 ConditionDeinit(&proxyRenderer->fromThreadCond);
82 ConditionDeinit(&proxyRenderer->toThreadCond);
83 MutexDeinit(&proxyRenderer->mutex);
84}
85
86void _proxyThreadRecover(struct mVideoThreadProxy* proxyRenderer) {
87 MutexLock(&proxyRenderer->mutex);
88 if (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
89 MutexUnlock(&proxyRenderer->mutex);
90 return;
91 }
92 RingFIFOClear(&proxyRenderer->dirtyQueue);
93 MutexUnlock(&proxyRenderer->mutex);
94 ThreadJoin(proxyRenderer->thread);
95 proxyRenderer->threadState = PROXY_THREAD_IDLE;
96 ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer);
97}
98
99static bool _writeData(struct mVideoLogger* logger, const void* data, size_t length) {
100 struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
101 while (!RingFIFOWrite(&proxyRenderer->dirtyQueue, data, length)) {
102 mLOG(GBA_VIDEO, DEBUG, "Can't write %"PRIz"u bytes. Proxy thread asleep?", length);
103 MutexLock(&proxyRenderer->mutex);
104 if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
105 mLOG(GBA_VIDEO, ERROR, "Proxy thread stopped prematurely!");
106 MutexUnlock(&proxyRenderer->mutex);
107 return false;
108 }
109 ConditionWake(&proxyRenderer->toThreadCond);
110 ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
111 MutexUnlock(&proxyRenderer->mutex);
112 }
113 return true;
114}
115
116static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bool block) {
117 struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
118 bool read = false;
119 while (true) {
120 read = RingFIFORead(&proxyRenderer->dirtyQueue, data, length);
121 if (!block || read) {
122 break;
123 }
124 mLOG(GBA_VIDEO, DEBUG, "Can't read %"PRIz"u bytes. CPU thread asleep?", length);
125 MutexLock(&proxyRenderer->mutex);
126 ConditionWake(&proxyRenderer->fromThreadCond);
127 ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
128 MutexUnlock(&proxyRenderer->mutex);
129 }
130 return read;
131}
132
133static void _lock(struct mVideoLogger* logger) {
134 struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
135 MutexLock(&proxyRenderer->mutex);
136}
137
138static void _wait(struct mVideoLogger* logger) {
139 struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
140 if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
141 mLOG(GBA_VIDEO, ERROR, "Proxy thread stopped prematurely!");
142 _proxyThreadRecover(proxyRenderer);
143 return;
144 }
145 while (RingFIFOSize(&proxyRenderer->dirtyQueue)) {
146 ConditionWake(&proxyRenderer->toThreadCond);
147 ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
148 }
149}
150
151static void _unlock(struct mVideoLogger* logger) {
152 struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
153 MutexUnlock(&proxyRenderer->mutex);
154}
155
156static void _wake(struct mVideoLogger* logger, int y) {
157 struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
158 if ((y & 15) == 15) {
159 ConditionWake(&proxyRenderer->toThreadCond);
160 }
161}
162
163static THREAD_ENTRY _proxyThread(void* logger) {
164 struct mVideoThreadProxy* proxyRenderer = logger;
165 ThreadSetName("Proxy Renderer Thread");
166
167 MutexLock(&proxyRenderer->mutex);
168 while (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
169 ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
170 if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
171 break;
172 }
173 proxyRenderer->threadState = PROXY_THREAD_BUSY;
174 MutexUnlock(&proxyRenderer->mutex);
175 if (!mVideoLoggerRendererRun(&proxyRenderer->d, false)) {
176 // FIFO was corrupted
177 proxyRenderer->threadState = PROXY_THREAD_STOPPED;
178 mLOG(GBA_VIDEO, ERROR, "Proxy thread queue got corrupted!");
179 }
180 MutexLock(&proxyRenderer->mutex);
181 ConditionWake(&proxyRenderer->fromThreadCond);
182 if (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
183 proxyRenderer->threadState = PROXY_THREAD_IDLE;
184 }
185 }
186 MutexUnlock(&proxyRenderer->mutex);
187
188#ifdef _3DS
189 svcExitThread();
190#endif
191 return 0;
192}
193
194#endif