git subrepo clone https://github.com/libretro/libretro-common.git deps/libretro-common
[pcsx_rearmed.git] / deps / libretro-common / rthreads / ctr_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/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