Commit | Line | Data |
---|---|---|
3719602c PC |
1 | /* Copyright (C) 2010-2020 The RetroArch team |
2 | * | |
3 | * --------------------------------------------------------------------------------------- | |
4 | * The following license statement only applies to this file (gx_pthread.h). | |
5 | * --------------------------------------------------------------------------------------- | |
6 | * | |
7 | * Permission is hereby granted, free of charge, | |
8 | * to any person obtaining a copy of this software and associated documentation files (the "Software"), | |
9 | * to deal in the Software without restriction, including without limitation the rights to | |
10 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | |
11 | * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | |
14 | * | |
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | |
16 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
19 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
21 | */ | |
22 | ||
23 | #ifndef _CTR_PTHREAD_WRAP_CTR_ | |
24 | #define _CTR_PTHREAD_WRAP_CTR_ | |
25 | ||
26 | #include <3ds/thread.h> | |
27 | #include <3ds/synchronization.h> | |
28 | #include <3ds/svc.h> | |
29 | #include <time.h> | |
30 | #include <errno.h> | |
31 | #include <retro_inline.h> | |
32 | ||
33 | #define STACKSIZE (32 * 1024) | |
34 | ||
35 | #ifndef PTHREAD_SCOPE_PROCESS | |
36 | /* An earlier version of devkitARM does not define the pthread types. Can remove in r54+. */ | |
37 | ||
38 | typedef Thread pthread_t; | |
39 | typedef LightLock pthread_mutex_t; | |
40 | typedef void* pthread_mutexattr_t; | |
41 | typedef int pthread_attr_t; | |
42 | typedef uint32_t pthread_cond_t; | |
43 | typedef int pthread_condattr_t; | |
44 | #endif | |
45 | ||
46 | #ifndef USE_CTRULIB_2 | |
47 | /* Backported CondVar API from libctru 2.0, and under its license: | |
48 | https://github.com/devkitPro/libctru | |
49 | Slightly modified for compatibility with older libctru. */ | |
50 | ||
51 | typedef s32 CondVar; | |
52 | ||
53 | static INLINE Result syncArbitrateAddress(s32* addr, ArbitrationType type, s32 value) | |
54 | { | |
55 | return svcArbitrateAddress(__sync_get_arbiter(), (u32)addr, type, value, 0); | |
56 | } | |
57 | ||
58 | static INLINE Result syncArbitrateAddressWithTimeout(s32* addr, ArbitrationType type, s32 value, s64 timeout_ns) | |
59 | { | |
60 | return svcArbitrateAddress(__sync_get_arbiter(), (u32)addr, type, value, timeout_ns); | |
61 | } | |
62 | ||
63 | static INLINE void __dmb(void) | |
64 | { | |
65 | __asm__ __volatile__("mcr p15, 0, %[val], c7, c10, 5" :: [val] "r" (0) : "memory"); | |
66 | } | |
67 | ||
68 | static INLINE void CondVar_BeginWait(CondVar* cv, LightLock* lock) | |
69 | { | |
70 | s32 val; | |
71 | do | |
72 | val = __ldrex(cv) - 1; | |
73 | while (__strex(cv, val)); | |
74 | LightLock_Unlock(lock); | |
75 | } | |
76 | ||
77 | static INLINE bool CondVar_EndWait(CondVar* cv, s32 num_threads) | |
78 | { | |
79 | bool hasWaiters; | |
80 | s32 val; | |
81 | ||
82 | do { | |
83 | val = __ldrex(cv); | |
84 | hasWaiters = val < 0; | |
85 | if (hasWaiters) | |
86 | { | |
87 | if (num_threads < 0) | |
88 | val = 0; | |
89 | else if (val <= -num_threads) | |
90 | val += num_threads; | |
91 | else | |
92 | val = 0; | |
93 | } | |
94 | } while (__strex(cv, val)); | |
95 | ||
96 | return hasWaiters; | |
97 | } | |
98 | ||
99 | static INLINE void CondVar_Init(CondVar* cv) | |
100 | { | |
101 | *cv = 0; | |
102 | } | |
103 | ||
104 | static INLINE void CondVar_Wait(CondVar* cv, LightLock* lock) | |
105 | { | |
106 | CondVar_BeginWait(cv, lock); | |
107 | syncArbitrateAddress(cv, ARBITRATION_WAIT_IF_LESS_THAN, 0); | |
108 | LightLock_Lock(lock); | |
109 | } | |
110 | ||
111 | static INLINE int CondVar_WaitTimeout(CondVar* cv, LightLock* lock, s64 timeout_ns) | |
112 | { | |
113 | CondVar_BeginWait(cv, lock); | |
114 | ||
115 | bool timedOut = false; | |
116 | Result rc = syncArbitrateAddressWithTimeout(cv, ARBITRATION_WAIT_IF_LESS_THAN_TIMEOUT, 0, timeout_ns); | |
117 | if (R_DESCRIPTION(rc) == RD_TIMEOUT) | |
118 | { | |
119 | timedOut = CondVar_EndWait(cv, 1); | |
120 | __dmb(); | |
121 | } | |
122 | ||
123 | LightLock_Lock(lock); | |
124 | return timedOut; | |
125 | } | |
126 | ||
127 | static INLINE void CondVar_WakeUp(CondVar* cv, s32 num_threads) | |
128 | { | |
129 | __dmb(); | |
130 | if (CondVar_EndWait(cv, num_threads)) | |
131 | syncArbitrateAddress(cv, ARBITRATION_SIGNAL, num_threads); | |
132 | else | |
133 | __dmb(); | |
134 | } | |
135 | ||
136 | static INLINE void CondVar_Signal(CondVar* cv) | |
137 | { | |
138 | CondVar_WakeUp(cv, 1); | |
139 | } | |
140 | ||
141 | static INLINE void CondVar_Broadcast(CondVar* cv) | |
142 | { | |
143 | CondVar_WakeUp(cv, ARBITRATION_SIGNAL_ALL); | |
144 | } | |
145 | /* End libctru 2.0 backport */ | |
146 | #endif | |
147 | ||
148 | /* libctru threads return void but pthreads return void pointer */ | |
149 | static bool mutex_inited = false; | |
150 | static LightLock safe_double_thread_launch; | |
151 | static void *(*start_routine_jump)(void*); | |
152 | ||
153 | static void ctr_thread_launcher(void* data) | |
154 | { | |
155 | void *(*start_routine_jump_safe)(void*) = start_routine_jump; | |
156 | LightLock_Unlock(&safe_double_thread_launch); | |
157 | start_routine_jump_safe(data); | |
158 | } | |
159 | ||
160 | static INLINE int pthread_create(pthread_t *thread, | |
161 | const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg) | |
162 | { | |
163 | s32 prio = 0; | |
164 | Thread new_ctr_thread; | |
165 | int procnum = -2; // use default cpu | |
166 | bool isNew3DS; | |
167 | ||
168 | APT_CheckNew3DS(&isNew3DS); | |
169 | ||
170 | if (isNew3DS) | |
171 | procnum = 2; | |
172 | ||
173 | if (!mutex_inited) | |
174 | { | |
175 | LightLock_Init(&safe_double_thread_launch); | |
176 | mutex_inited = true; | |
177 | } | |
178 | ||
179 | /*Must wait if attempting to launch 2 threads at once to prevent corruption of function pointer*/ | |
180 | while (LightLock_TryLock(&safe_double_thread_launch) != 0); | |
181 | ||
182 | svcGetThreadPriority(&prio, CUR_THREAD_HANDLE); | |
183 | ||
184 | start_routine_jump = start_routine; | |
185 | new_ctr_thread = threadCreate(ctr_thread_launcher, arg, STACKSIZE, prio - 1, procnum, false); | |
186 | ||
187 | if (!new_ctr_thread) | |
188 | { | |
189 | LightLock_Unlock(&safe_double_thread_launch); | |
190 | return EAGAIN; | |
191 | } | |
192 | ||
193 | *thread = (pthread_t)new_ctr_thread; | |
194 | return 0; | |
195 | } | |
196 | ||
197 | static INLINE pthread_t pthread_self(void) | |
198 | { | |
199 | return (pthread_t)threadGetCurrent(); | |
200 | } | |
201 | ||
202 | static INLINE int pthread_mutex_init(pthread_mutex_t *mutex, | |
203 | const pthread_mutexattr_t *attr) | |
204 | { | |
205 | LightLock_Init((LightLock *)mutex); | |
206 | return 0; | |
207 | } | |
208 | ||
209 | static INLINE int pthread_mutex_destroy(pthread_mutex_t *mutex) | |
210 | { | |
211 | /*Nothing to destroy*/ | |
212 | return 0; | |
213 | } | |
214 | ||
215 | static INLINE int pthread_mutex_lock(pthread_mutex_t *mutex) | |
216 | { | |
217 | LightLock_Lock((LightLock *)mutex); | |
218 | return 0; | |
219 | } | |
220 | ||
221 | static INLINE int pthread_mutex_unlock(pthread_mutex_t *mutex) | |
222 | { | |
223 | LightLock_Unlock((LightLock *)mutex); | |
224 | return 0; | |
225 | } | |
226 | ||
227 | static INLINE void pthread_exit(void *retval) | |
228 | { | |
229 | /*Yes the pointer to int cast is not ideal*/ | |
230 | /*threadExit((int)retval);*/ | |
231 | (void)retval; | |
232 | ||
233 | threadExit(0); | |
234 | } | |
235 | ||
236 | static INLINE int pthread_detach(pthread_t thread) | |
237 | { | |
238 | threadDetach((Thread)thread); | |
239 | return 0; | |
240 | } | |
241 | ||
242 | static INLINE int pthread_join(pthread_t thread, void **retval) | |
243 | { | |
244 | /*retval is ignored*/ | |
245 | if (threadJoin((Thread)thread, INT64_MAX)) | |
246 | return -1; | |
247 | ||
248 | threadFree((Thread)thread); | |
249 | ||
250 | return 0; | |
251 | } | |
252 | ||
253 | static INLINE int pthread_mutex_trylock(pthread_mutex_t *mutex) | |
254 | { | |
255 | return LightLock_TryLock((LightLock *)mutex); | |
256 | } | |
257 | ||
258 | static INLINE int pthread_cond_wait(pthread_cond_t *cond, | |
259 | pthread_mutex_t *mutex) | |
260 | { | |
261 | CondVar_Wait((CondVar *)cond, (LightLock *)mutex); | |
262 | return 0; | |
263 | } | |
264 | ||
265 | static INLINE int pthread_cond_timedwait(pthread_cond_t *cond, | |
266 | pthread_mutex_t *mutex, const struct timespec *abstime) | |
267 | { | |
268 | /* Missing clock_gettime*/ | |
269 | struct timespec now; | |
270 | struct timeval tm; | |
271 | int retval = 0; | |
272 | ||
273 | do | |
274 | { | |
275 | s64 timeout; | |
276 | gettimeofday(&tm, NULL); | |
277 | now.tv_sec = tm.tv_sec; | |
278 | now.tv_nsec = tm.tv_usec * 1000; | |
279 | ||
280 | if ((timeout = (abstime->tv_sec - now.tv_sec) * 1000000000 + | |
281 | (abstime->tv_nsec - now.tv_nsec)) < 0) | |
282 | { | |
283 | retval = ETIMEDOUT; | |
284 | break; | |
285 | } | |
286 | ||
287 | if (!CondVar_WaitTimeout((CondVar *)cond, (LightLock *)mutex, timeout)) | |
288 | break; | |
289 | } while (1); | |
290 | ||
291 | return retval; | |
292 | } | |
293 | ||
294 | static INLINE int pthread_cond_init(pthread_cond_t *cond, | |
295 | const pthread_condattr_t *attr) | |
296 | { | |
297 | CondVar_Init((CondVar *)cond); | |
298 | return 0; | |
299 | } | |
300 | ||
301 | static INLINE int pthread_cond_signal(pthread_cond_t *cond) | |
302 | { | |
303 | CondVar_Signal((CondVar *)cond); | |
304 | return 0; | |
305 | } | |
306 | ||
307 | static INLINE int pthread_cond_broadcast(pthread_cond_t *cond) | |
308 | { | |
309 | CondVar_Broadcast((CondVar *)cond); | |
310 | return 0; | |
311 | } | |
312 | ||
313 | static INLINE int pthread_cond_destroy(pthread_cond_t *cond) | |
314 | { | |
315 | /*Nothing to destroy*/ | |
316 | return 0; | |
317 | } | |
318 | ||
319 | static INLINE int pthread_equal(pthread_t t1, pthread_t t2) | |
320 | { | |
321 | if (threadGetHandle((Thread)t1) == threadGetHandle((Thread)t2)) | |
322 | return 1; | |
323 | return 0; | |
324 | } | |
325 | ||
326 | #endif |