Commit | Line | Data |
---|---|---|
c81b3e15 JW |
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_ | |
fc99395c | 25 | |
f72db18e | 26 | #include "3ds_utils.h" |
fc99395c | 27 | |
c81b3e15 JW |
28 | #include <time.h> |
29 | #include <errno.h> | |
fc99395c | 30 | |
c81b3e15 JW |
31 | #define STACKSIZE (4 * 1024) |
32 | #define FALSE 0 | |
31f52a9f | 33 | |
c81b3e15 | 34 | #ifndef PTHREAD_SCOPE_PROCESS |
31f52a9f JW |
35 | /* An earlier version of devkitARM does not define the pthread types. Can remove in r54+. */ |
36 | ||
c81b3e15 JW |
37 | typedef Thread pthread_t; |
38 | typedef LightLock pthread_mutex_t; | |
39 | typedef void* pthread_mutexattr_t; | |
40 | typedef int pthread_attr_t; | |
41 | typedef LightEvent pthread_cond_t; | |
42 | typedef int pthread_condattr_t; | |
43 | #endif | |
44 | ||
45 | #ifndef USE_CTRULIB_2 | |
46 | /* Backported CondVar API from libctru 2.0, and under its license: | |
47 | https://github.com/devkitPro/libctru | |
48 | Slightly modified for compatibility with older libctru. */ | |
49 | ||
50 | typedef s32 CondVar; | |
51 | ||
52 | static inline Result syncArbitrateAddress(s32* addr, ArbitrationType type, s32 value) | |
53 | { | |
54 | return svcArbitrateAddress(__sync_get_arbiter(), (u32)addr, type, value, 0); | |
55 | } | |
56 | ||
57 | static inline Result syncArbitrateAddressWithTimeout(s32* addr, ArbitrationType type, s32 value, s64 timeout_ns) | |
58 | { | |
59 | return svcArbitrateAddress(__sync_get_arbiter(), (u32)addr, type, value, timeout_ns); | |
60 | } | |
61 | ||
62 | static inline void __dmb(void) | |
63 | { | |
64 | __asm__ __volatile__("mcr p15, 0, %[val], c7, c10, 5" :: [val] "r" (0) : "memory"); | |
65 | } | |
66 | ||
67 | static inline void CondVar_BeginWait(CondVar* cv, LightLock* lock) | |
68 | { | |
69 | s32 val; | |
70 | do | |
71 | val = __ldrex(cv) - 1; | |
72 | while (__strex(cv, val)); | |
73 | LightLock_Unlock(lock); | |
74 | } | |
75 | ||
76 | static inline bool CondVar_EndWait(CondVar* cv, s32 num_threads) | |
77 | { | |
78 | bool hasWaiters; | |
79 | s32 val; | |
80 | ||
81 | do { | |
82 | val = __ldrex(cv); | |
83 | hasWaiters = val < 0; | |
84 | if (hasWaiters) | |
85 | { | |
86 | if (num_threads < 0) | |
87 | val = 0; | |
88 | else if (val <= -num_threads) | |
89 | val += num_threads; | |
90 | else | |
91 | val = 0; | |
92 | } | |
93 | } while (__strex(cv, val)); | |
94 | ||
95 | return hasWaiters; | |
96 | } | |
97 | ||
98 | static inline void CondVar_Init(CondVar* cv) | |
99 | { | |
100 | *cv = 0; | |
101 | } | |
102 | ||
103 | static inline void CondVar_Wait(CondVar* cv, LightLock* lock) | |
104 | { | |
105 | CondVar_BeginWait(cv, lock); | |
106 | syncArbitrateAddress(cv, ARBITRATION_WAIT_IF_LESS_THAN, 0); | |
107 | LightLock_Lock(lock); | |
108 | } | |
109 | ||
110 | static inline int CondVar_WaitTimeout(CondVar* cv, LightLock* lock, s64 timeout_ns) | |
111 | { | |
112 | CondVar_BeginWait(cv, lock); | |
113 | ||
114 | bool timedOut = false; | |
115 | Result rc = syncArbitrateAddressWithTimeout(cv, ARBITRATION_WAIT_IF_LESS_THAN_TIMEOUT, 0, timeout_ns); | |
116 | if (R_DESCRIPTION(rc) == RD_TIMEOUT) | |
117 | { | |
118 | timedOut = CondVar_EndWait(cv, 1); | |
119 | __dmb(); | |
120 | } | |
121 | ||
122 | LightLock_Lock(lock); | |
123 | return timedOut; | |
124 | } | |
679b71e2 | 125 | |
c81b3e15 JW |
126 | static inline void CondVar_WakeUp(CondVar* cv, s32 num_threads) |
127 | { | |
128 | __dmb(); | |
129 | if (CondVar_EndWait(cv, num_threads)) | |
130 | syncArbitrateAddress(cv, ARBITRATION_SIGNAL, num_threads); | |
131 | else | |
132 | __dmb(); | |
133 | } | |
31f52a9f | 134 | |
c81b3e15 JW |
135 | static inline void CondVar_Signal(CondVar* cv) |
136 | { | |
137 | CondVar_WakeUp(cv, 1); | |
138 | } | |
679b71e2 | 139 | |
c81b3e15 JW |
140 | static inline void CondVar_Broadcast(CondVar* cv) |
141 | { | |
142 | CondVar_WakeUp(cv, ARBITRATION_SIGNAL_ALL); | |
143 | } | |
144 | /* End libctru 2.0 backport */ | |
31f52a9f JW |
145 | #endif |
146 | ||
c81b3e15 JW |
147 | /* libctru threads return void but pthreads return void pointer */ |
148 | static bool mutex_inited = false; | |
149 | static LightLock safe_double_thread_launch; | |
150 | static void *(*start_routine_jump)(void*); | |
151 | ||
152 | static void ctr_thread_launcher(void* data) | |
153 | { | |
154 | void *(*start_routine_jump_safe)(void*) = start_routine_jump; | |
155 | LightLock_Unlock(&safe_double_thread_launch); | |
156 | start_routine_jump_safe(data); | |
157 | } | |
158 | ||
fc99395c | 159 | static inline int pthread_create(pthread_t *thread, |
160 | const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg) | |
161 | { | |
c81b3e15 JW |
162 | s32 prio = 0; |
163 | Thread new_ctr_thread; | |
679b71e2 JW |
164 | int procnum = -2; // use default cpu |
165 | bool isNew3DS; | |
c81b3e15 | 166 | |
679b71e2 JW |
167 | APT_CheckNew3DS(&isNew3DS); |
168 | ||
169 | if (isNew3DS) | |
c81b3e15 JW |
170 | procnum = 2; |
171 | ||
172 | if (!mutex_inited) | |
173 | { | |
174 | LightLock_Init(&safe_double_thread_launch); | |
175 | mutex_inited = true; | |
176 | } | |
177 | ||
178 | /*Must wait if attempting to launch 2 threads at once to prevent corruption of function pointer*/ | |
179 | while (LightLock_TryLock(&safe_double_thread_launch) != 0); | |
180 | ||
181 | svcGetThreadPriority(&prio, CUR_THREAD_HANDLE); | |
182 | ||
183 | start_routine_jump = start_routine; | |
184 | new_ctr_thread = threadCreate(ctr_thread_launcher, arg, STACKSIZE, prio - 1, procnum, FALSE); | |
679b71e2 | 185 | |
c81b3e15 JW |
186 | if (!new_ctr_thread) |
187 | { | |
188 | LightLock_Unlock(&safe_double_thread_launch); | |
189 | return EAGAIN; | |
190 | } | |
191 | ||
192 | *thread = (pthread_t)new_ctr_thread; | |
679b71e2 | 193 | return 0; |
fc99395c | 194 | } |
195 | ||
c81b3e15 | 196 | static inline pthread_t pthread_self(void) |
fc99395c | 197 | { |
c81b3e15 JW |
198 | return (pthread_t)threadGetCurrent(); |
199 | } | |
fc99395c | 200 | |
c81b3e15 JW |
201 | static inline int pthread_mutex_init(pthread_mutex_t *mutex, |
202 | const pthread_mutexattr_t *attr) | |
203 | { | |
204 | LightLock_Init((LightLock *)mutex); | |
205 | return 0; | |
206 | } | |
fc99395c | 207 | |
c81b3e15 JW |
208 | static inline int pthread_mutex_destroy(pthread_mutex_t *mutex) |
209 | { | |
210 | /*Nothing to destroy*/ | |
211 | return 0; | |
212 | } | |
fc99395c | 213 | |
c81b3e15 JW |
214 | static inline int pthread_mutex_lock(pthread_mutex_t *mutex) |
215 | { | |
216 | LightLock_Lock((LightLock *)mutex); | |
fc99395c | 217 | return 0; |
218 | } | |
219 | ||
c81b3e15 JW |
220 | static inline int pthread_mutex_unlock(pthread_mutex_t *mutex) |
221 | { | |
222 | LightLock_Unlock((LightLock *)mutex); | |
223 | return 0; | |
224 | } | |
fc99395c | 225 | |
226 | static inline void pthread_exit(void *retval) | |
c81b3e15 JW |
227 | { |
228 | /*Yes the pointer to int cast is not ideal*/ | |
229 | /*threadExit((int)retval);*/ | |
fc99395c | 230 | (void)retval; |
231 | ||
93c24a56 | 232 | threadExit(0); |
fc99395c | 233 | } |
234 | ||
c81b3e15 JW |
235 | static inline int pthread_detach(pthread_t thread) |
236 | { | |
237 | threadDetach((Thread)thread); | |
238 | return 0; | |
679b71e2 JW |
239 | } |
240 | ||
c81b3e15 JW |
241 | static inline int pthread_join(pthread_t thread, void **retval) |
242 | { | |
243 | /*retval is ignored*/ | |
244 | if(threadJoin((Thread)thread, INT64_MAX)) | |
245 | return -1; | |
679b71e2 | 246 | |
c81b3e15 | 247 | threadFree((Thread)thread); |
679b71e2 | 248 | |
c81b3e15 | 249 | return 0; |
679b71e2 JW |
250 | } |
251 | ||
c81b3e15 JW |
252 | static inline int pthread_mutex_trylock(pthread_mutex_t *mutex) |
253 | { | |
254 | return LightLock_TryLock((LightLock *)mutex); | |
255 | } | |
679b71e2 | 256 | |
c81b3e15 JW |
257 | static inline int pthread_cond_wait(pthread_cond_t *cond, |
258 | pthread_mutex_t *mutex) | |
259 | { | |
260 | CondVar_Wait((CondVar *)cond, (LightLock *)mutex); | |
261 | return 0; | |
679b71e2 JW |
262 | } |
263 | ||
c81b3e15 JW |
264 | static inline int pthread_cond_timedwait(pthread_cond_t *cond, |
265 | pthread_mutex_t *mutex, const struct timespec *abstime) | |
266 | { | |
267 | struct timespec now = {0}; | |
268 | /* Missing clock_gettime*/ | |
269 | struct timeval tm; | |
270 | int retval = 0; | |
271 | ||
272 | gettimeofday(&tm, NULL); | |
273 | now.tv_sec = tm.tv_sec; | |
274 | now.tv_nsec = tm.tv_usec * 1000; | |
275 | s64 timeout = (abstime->tv_sec - now.tv_sec) * 1000000000 + (abstime->tv_nsec - now.tv_nsec); | |
276 | ||
277 | if (timeout < 0) | |
278 | { | |
279 | retval = ETIMEDOUT; | |
280 | } | |
281 | else if (CondVar_WaitTimeout((CondVar *)cond, (LightLock *)mutex, timeout)) | |
282 | { | |
283 | retval = ETIMEDOUT; | |
284 | } | |
285 | ||
286 | return retval; | |
679b71e2 JW |
287 | } |
288 | ||
c81b3e15 JW |
289 | static inline int pthread_cond_init(pthread_cond_t *cond, |
290 | const pthread_condattr_t *attr) | |
291 | { | |
292 | CondVar_Init((CondVar *)cond); | |
293 | return 0; | |
679b71e2 JW |
294 | } |
295 | ||
c81b3e15 JW |
296 | static inline int pthread_cond_signal(pthread_cond_t *cond) |
297 | { | |
298 | CondVar_Signal((CondVar *)cond); | |
299 | return 0; | |
300 | } | |
31f52a9f | 301 | |
c81b3e15 JW |
302 | static inline int pthread_cond_broadcast(pthread_cond_t *cond) |
303 | { | |
304 | CondVar_Broadcast((CondVar *)cond); | |
305 | return 0; | |
679b71e2 JW |
306 | } |
307 | ||
c81b3e15 JW |
308 | static inline int pthread_cond_destroy(pthread_cond_t *cond) |
309 | { | |
310 | /*Nothing to destroy*/ | |
311 | return 0; | |
312 | } | |
fc99395c | 313 | |
c81b3e15 JW |
314 | static inline int pthread_equal(pthread_t t1, pthread_t t2) |
315 | { | |
316 | if (threadGetHandle((Thread)t1) == threadGetHandle((Thread)t2)) | |
317 | return 1; | |
318 | return 0; | |
319 | } | |
fc99395c | 320 | |
c81b3e15 | 321 | #endif |