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