xref: /aosp_15_r20/external/mesa3d/src/c11/impl/threads_posix.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /*
2  * C11 <threads.h> emulation library
3  *
4  * (C) Copyright yohhoy 2012.
5  * Distributed under the Boost Software License, Version 1.0.
6  *
7  * Permission is hereby granted, free of charge, to any person or organization
8  * obtaining a copy of the software and accompanying documentation covered by
9  * this license (the "Software") to use, reproduce, display, distribute,
10  * execute, and transmit the Software, and to prepare [[derivative work]]s of the
11  * Software, and to permit third-parties to whom the Software is furnished to
12  * do so, all subject to the following:
13  *
14  * The copyright notices in the Software and this entire statement, including
15  * the above license grant, this restriction and the following disclaimer,
16  * must be included in all copies of the Software, in whole or in part, and
17  * all derivative works of the Software, unless such copies or derivative
18  * works are solely in the form of machine-executable object code generated by
19  * a source language processor.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
24  * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
25  * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
26  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  */
29 #include <stdlib.h>
30 #include <assert.h>
31 #include <limits.h>
32 #include <errno.h>
33 #include <unistd.h>
34 #include <sched.h>
35 #include <stdint.h> /* for intptr_t */
36 
37 #include "c11/threads.h"
38 
39 /*
40 Configuration macro:
41 
42   EMULATED_THREADS_USE_NATIVE_TIMEDLOCK
43     Use pthread_mutex_timedlock() for `mtx_timedlock()'
44     Otherwise use mtx_trylock() + *busy loop* emulation.
45 */
46 #if !defined(__CYGWIN__) && !defined(__APPLE__) && !defined(__NetBSD__)
47 #define EMULATED_THREADS_USE_NATIVE_TIMEDLOCK
48 #endif
49 
50 /*---------------------------- types ----------------------------*/
51 
52 /*
53 Implementation limits:
54   - Conditionally emulation for "mutex with timeout"
55     (see EMULATED_THREADS_USE_NATIVE_TIMEDLOCK macro)
56 */
57 struct impl_thrd_param {
58     thrd_start_t func;
59     void *arg;
60 };
61 
62 static void *
impl_thrd_routine(void * p)63 impl_thrd_routine(void *p)
64 {
65     struct impl_thrd_param pack = *((struct impl_thrd_param *)p);
66     free(p);
67     return (void*)(intptr_t)pack.func(pack.arg);
68 }
69 
70 
71 /*--------------- 7.25.2 Initialization functions ---------------*/
72 // 7.25.2.1
73 void
call_once(once_flag * flag,void (* func)(void))74 call_once(once_flag *flag, void (*func)(void))
75 {
76     pthread_once(flag, func);
77 }
78 
79 
80 /*------------- 7.25.3 Condition variable functions -------------*/
81 // 7.25.3.1
82 int
cnd_broadcast(cnd_t * cond)83 cnd_broadcast(cnd_t *cond)
84 {
85     assert(cond != NULL);
86     return (pthread_cond_broadcast(cond) == 0) ? thrd_success : thrd_error;
87 }
88 
89 // 7.25.3.2
90 void
cnd_destroy(cnd_t * cond)91 cnd_destroy(cnd_t *cond)
92 {
93     assert(cond);
94     pthread_cond_destroy(cond);
95 }
96 
97 // 7.25.3.3
98 int
cnd_init(cnd_t * cond)99 cnd_init(cnd_t *cond)
100 {
101     assert(cond != NULL);
102     return (pthread_cond_init(cond, NULL) == 0) ? thrd_success : thrd_error;
103 }
104 
105 // 7.25.3.4
106 int
cnd_signal(cnd_t * cond)107 cnd_signal(cnd_t *cond)
108 {
109     assert(cond != NULL);
110     return (pthread_cond_signal(cond) == 0) ? thrd_success : thrd_error;
111 }
112 
113 // 7.25.3.5
114 int
cnd_timedwait(cnd_t * cond,mtx_t * mtx,const struct timespec * abs_time)115 cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *abs_time)
116 {
117     int rt;
118 
119     assert(mtx != NULL);
120     assert(cond != NULL);
121     assert(abs_time != NULL);
122 
123     rt = pthread_cond_timedwait(cond, mtx, abs_time);
124     if (rt == ETIMEDOUT)
125         return thrd_timedout;
126     return (rt == 0) ? thrd_success : thrd_error;
127 }
128 
129 // 7.25.3.6
130 int
cnd_wait(cnd_t * cond,mtx_t * mtx)131 cnd_wait(cnd_t *cond, mtx_t *mtx)
132 {
133     assert(mtx != NULL);
134     assert(cond != NULL);
135     return (pthread_cond_wait(cond, mtx) == 0) ? thrd_success : thrd_error;
136 }
137 
138 
139 /*-------------------- 7.25.4 Mutex functions --------------------*/
140 // 7.25.4.1
141 void
mtx_destroy(mtx_t * mtx)142 mtx_destroy(mtx_t *mtx)
143 {
144     assert(mtx != NULL);
145     pthread_mutex_destroy(mtx);
146 }
147 
148 /*
149  * XXX: Workaround when building with -O0 and without pthreads link.
150  *
151  * In such cases constant folding and dead code elimination won't be
152  * available, thus the compiler will always add the pthread_mutexattr*
153  * functions into the binary. As we try to link, we'll fail as the
154  * symbols are unresolved.
155  *
156  * Ideally we'll enable the optimisations locally, yet that does not
157  * seem to work.
158  *
159  * So the alternative workaround is to annotate the symbols as weak.
160  * Thus the linker will be happy and things don't clash when building
161  * with -O1 or greater.
162  */
163 #if defined(HAVE_FUNC_ATTRIBUTE_WEAK) && !defined(__CYGWIN__)
164 __attribute__((weak))
165 int pthread_mutexattr_init(pthread_mutexattr_t *attr);
166 
167 __attribute__((weak))
168 int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type);
169 
170 __attribute__((weak))
171 int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
172 #endif
173 
174 // 7.25.4.2
175 int
mtx_init(mtx_t * mtx,int type)176 mtx_init(mtx_t *mtx, int type)
177 {
178     pthread_mutexattr_t attr;
179     assert(mtx != NULL);
180     if (type != mtx_plain && type != mtx_timed
181       && type != (mtx_plain|mtx_recursive)
182       && type != (mtx_timed|mtx_recursive))
183         return thrd_error;
184 
185     if ((type & mtx_recursive) == 0) {
186         pthread_mutex_init(mtx, NULL);
187         return thrd_success;
188     }
189 
190     pthread_mutexattr_init(&attr);
191     pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
192     pthread_mutex_init(mtx, &attr);
193     pthread_mutexattr_destroy(&attr);
194     return thrd_success;
195 }
196 
197 // 7.25.4.3
198 int
mtx_lock(mtx_t * mtx)199 mtx_lock(mtx_t *mtx)
200 {
201     assert(mtx != NULL);
202     return (pthread_mutex_lock(mtx) == 0) ? thrd_success : thrd_error;
203 }
204 
205 static int
threads_timespec_compare(const struct timespec * a,const struct timespec * b)206 threads_timespec_compare(const struct timespec *a, const struct timespec *b)
207 {
208     if (a->tv_sec < b->tv_sec) {
209         return -1;
210     } else if (a->tv_sec > b->tv_sec) {
211         return 1;
212     } else if (a->tv_nsec < b->tv_nsec) {
213         return -1;
214     } else if (a->tv_nsec > b->tv_nsec) {
215         return 1;
216     }
217     return 0;
218 }
219 
220 // 7.25.4.4
221 int
mtx_timedlock(mtx_t * mtx,const struct timespec * ts)222 mtx_timedlock(mtx_t *mtx, const struct timespec *ts)
223 {
224     assert(mtx != NULL);
225     assert(ts != NULL);
226 
227     {
228 #ifdef EMULATED_THREADS_USE_NATIVE_TIMEDLOCK
229     int rt;
230     rt = pthread_mutex_timedlock(mtx, ts);
231     if (rt == 0)
232         return thrd_success;
233     return (rt == ETIMEDOUT) ? thrd_timedout : thrd_error;
234 #else
235     while (mtx_trylock(mtx) != thrd_success) {
236         struct timespec now;
237         if (timespec_get(&now, TIME_UTC) != TIME_UTC) {
238             return thrd_error;
239         }
240         if (threads_timespec_compare(ts, &now) < 0)
241             return thrd_timedout;
242         // busy loop!
243         thrd_yield();
244     }
245     return thrd_success;
246 #endif
247     }
248 }
249 
250 // 7.25.4.5
251 int
mtx_trylock(mtx_t * mtx)252 mtx_trylock(mtx_t *mtx)
253 {
254     assert(mtx != NULL);
255     return (pthread_mutex_trylock(mtx) == 0) ? thrd_success : thrd_busy;
256 }
257 
258 // 7.25.4.6
259 int
mtx_unlock(mtx_t * mtx)260 mtx_unlock(mtx_t *mtx)
261 {
262     assert(mtx != NULL);
263     return (pthread_mutex_unlock(mtx) == 0) ? thrd_success : thrd_error;
264 }
265 
266 
267 /*------------------- 7.25.5 Thread functions -------------------*/
268 // 7.25.5.1
269 int
thrd_create(thrd_t * thr,thrd_start_t func,void * arg)270 thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
271 {
272     struct impl_thrd_param *pack;
273     assert(thr != NULL);
274     pack = (struct impl_thrd_param *)malloc(sizeof(struct impl_thrd_param));
275     if (!pack) return thrd_nomem;
276     pack->func = func;
277     pack->arg = arg;
278     if (pthread_create(thr, NULL, impl_thrd_routine, pack) != 0) {
279         free(pack);
280         return thrd_error;
281     }
282     return thrd_success;
283 }
284 
285 // 7.25.5.2
286 thrd_t
thrd_current(void)287 thrd_current(void)
288 {
289     return pthread_self();
290 }
291 
292 // 7.25.5.3
293 int
thrd_detach(thrd_t thr)294 thrd_detach(thrd_t thr)
295 {
296     return (pthread_detach(thr) == 0) ? thrd_success : thrd_error;
297 }
298 
299 // 7.25.5.4
300 int
thrd_equal(thrd_t thr0,thrd_t thr1)301 thrd_equal(thrd_t thr0, thrd_t thr1)
302 {
303     return pthread_equal(thr0, thr1);
304 }
305 
306 // 7.25.5.5
307 _Noreturn
308 void
thrd_exit(int res)309 thrd_exit(int res)
310 {
311     pthread_exit((void*)(intptr_t)res);
312 }
313 
314 // 7.25.5.6
315 int
thrd_join(thrd_t thr,int * res)316 thrd_join(thrd_t thr, int *res)
317 {
318     void *code;
319     if (pthread_join(thr, &code) != 0)
320         return thrd_error;
321     if (res)
322         *res = (int)(intptr_t)code;
323     return thrd_success;
324 }
325 
326 // 7.25.5.7
327 int
thrd_sleep(const struct timespec * time_point,struct timespec * remaining)328 thrd_sleep(const struct timespec *time_point, struct timespec *remaining)
329 {
330     assert(time_point != NULL);
331     return nanosleep(time_point, remaining);
332 }
333 
334 // 7.25.5.8
335 void
thrd_yield(void)336 thrd_yield(void)
337 {
338     sched_yield();
339 }
340 
341 
342 /*----------- 7.25.6 Thread-specific storage functions -----------*/
343 // 7.25.6.1
344 int
tss_create(tss_t * key,tss_dtor_t dtor)345 tss_create(tss_t *key, tss_dtor_t dtor)
346 {
347     assert(key != NULL);
348     return (pthread_key_create(key, dtor) == 0) ? thrd_success : thrd_error;
349 }
350 
351 // 7.25.6.2
352 void
tss_delete(tss_t key)353 tss_delete(tss_t key)
354 {
355     pthread_key_delete(key);
356 }
357 
358 // 7.25.6.3
359 void *
tss_get(tss_t key)360 tss_get(tss_t key)
361 {
362     return pthread_getspecific(key);
363 }
364 
365 // 7.25.6.4
366 int
tss_set(tss_t key,void * val)367 tss_set(tss_t key, void *val)
368 {
369     return (pthread_setspecific(key, val) == 0) ? thrd_success : thrd_error;
370 }
371