release r24
[pcsx_rearmed.git] / frontend / 3ds / pthread.h
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_utils.h"
27
28 #include <time.h>
29 #include <errno.h>
30
31 #define STACKSIZE (4 * 1024)
32 #define FALSE 0
33
34 #ifndef PTHREAD_SCOPE_PROCESS
35 /* An earlier version of devkitARM does not define the pthread types. Can remove in r54+. */
36
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 }
125
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 }
134
135 static inline void CondVar_Signal(CondVar* cv)
136 {
137         CondVar_WakeUp(cv, 1);
138 }
139
140 static inline void CondVar_Broadcast(CondVar* cv)
141 {
142         CondVar_WakeUp(cv, ARBITRATION_SIGNAL_ALL);
143 }
144 /* End libctru 2.0 backport */
145 #endif
146
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
159 static inline int pthread_create(pthread_t *thread,
160       const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg)
161 {
162    s32 prio = 0;
163    Thread new_ctr_thread;
164    int procnum = -2; // use default cpu
165    bool isNew3DS;
166
167    APT_CheckNew3DS(&isNew3DS);
168
169    if (isNew3DS)
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);
185
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;
193    return 0;
194 }
195
196 static inline pthread_t pthread_self(void)
197 {
198    return (pthread_t)threadGetCurrent();
199 }
200
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 }
207
208 static inline int pthread_mutex_destroy(pthread_mutex_t *mutex)
209 {
210    /*Nothing to destroy*/
211    return 0;
212 }
213
214 static inline int pthread_mutex_lock(pthread_mutex_t *mutex)
215 {
216    LightLock_Lock((LightLock *)mutex);
217    return 0;
218 }
219
220 static inline int pthread_mutex_unlock(pthread_mutex_t *mutex)
221 {
222    LightLock_Unlock((LightLock *)mutex);
223    return 0;
224 }
225
226 static inline void pthread_exit(void *retval)
227 {
228    /*Yes the pointer to int cast is not ideal*/
229    /*threadExit((int)retval);*/
230    (void)retval;
231
232    threadExit(0);
233 }
234
235 static inline int pthread_detach(pthread_t thread)
236 {
237    threadDetach((Thread)thread);
238    return 0;
239 }
240
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;
246
247    threadFree((Thread)thread);
248
249    return 0;
250 }
251
252 static inline int pthread_mutex_trylock(pthread_mutex_t *mutex)
253 {
254    return LightLock_TryLock((LightLock *)mutex);
255 }
256
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;
262 }
263
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;
287 }
288
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;
294 }
295
296 static inline int pthread_cond_signal(pthread_cond_t *cond)
297 {
298    CondVar_Signal((CondVar *)cond);
299    return 0;
300 }
301
302 static inline int pthread_cond_broadcast(pthread_cond_t *cond)
303 {
304    CondVar_Broadcast((CondVar *)cond);
305    return 0;
306 }
307
308 static inline int pthread_cond_destroy(pthread_cond_t *cond)
309 {
310    /*Nothing to destroy*/
311    return 0;
312 }
313
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 }
320
321 #endif