2 * Copyright (C) 2024 Mikulas Patocka
4 * This file is part of Ajla.
6 * Ajla is free software: you can redistribute it and/or modify it under the
7 * terms of the GNU General Public License as published by the Free Software
8 * Foundation, either version 3 of the License, or (at your option) any later
11 * Ajla is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along with
16 * Ajla. If not, see <https://www.gnu.org/licenses/>.
30 #if defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) && defined(HAVE_CLOCK_GETTIME) && defined(HAVE_CLOCK_MONOTONIC)
31 #define USE_PTHREAD_CONDATTR
34 #ifndef USE_PTHREAD_CONDATTR
38 static clockid_t clock_id
;
41 #if defined(HAVE_SYS_PARAM_H)
42 #include <sys/param.h>
44 #if defined(HAVE_SYS_PSTAT_H)
45 #include <sys/pstat.h>
47 #if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_SYSCTL) && defined(CTL_HW) && defined(HW_NCPU)
48 #include <sys/sysctl.h>
51 uchar_efficient_t thread_needs_barriers
= true;
53 static inline unsigned thread_concurrency_getdynamic(void)
55 #if defined(HAVE_SYS_PSTAT_H) && defined(HAVE_PSTAT_GETDYNAMIC)
56 struct pst_dynamic pst
;
58 EINTR_LOOP(ir
, pstat_getdynamic(&pst
, sizeof(pst
), 1, 0));
60 if (unlikely(pst
.psd_proc_cnt
<= 0)) {
61 warning("pstat_getdynamic returned invalid value %d", (int)pst
.psd_proc_cnt
);
64 return (unsigned)pst
.psd_proc_cnt
;
71 static inline unsigned thread_concurrency_sysctl(void)
73 #if defined(HAVE_SYSCTL) && defined(CTL_HW) && defined(HW_NCPU)
82 EINTR_LOOP(ir
, sysctl(mib
, 2, &n_cpus
, &sz
, NULL
, 0));
84 if (likely(n_cpus
> 0))
85 return (unsigned)n_cpus
;
86 warning("sysctl(CTL_HW,HW_NCPU) returned invalid value %d", n_cpus
);
90 warning("sysctl(CTL_HW,HW_NCPU) returned error: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
101 static inline unsigned thread_concurrency_sysconf(void)
103 #if !(defined(UNUSUAL) && defined(__linux__))
105 const char attr_unused
*str
;
106 #if defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN)
108 str
= "sysconf(_SC_NPROCESSORS_ONLN)";
109 EINTR_LOOP(ir
, sysconf(_SC_NPROCESSORS_ONLN
));
110 #elif defined(HAVE_SYSCONF) && defined(_SC_NPROC_ONLN)
112 str
= "sysconf(_SC_NPROC_ONLN)";
113 EINTR_LOOP(ir
, sysconf(_SC_NPROC_ONLN
));
122 warning("%s returned error: %d, %s", str, er, error_decode(error_from_errno(EC_SYSCALL, er)));
125 warning("%s returned invalid value %d", str
, ir
);
131 static inline unsigned thread_concurrency_linux(void)
133 #if defined(__linux__)
139 if (unlikely(!os_read_file("/proc/cpuinfo", &str
, &len
, &sink
)))
141 array_add(char, &str
, &len
, 0);
145 if (!strncmp(p
, "processor", 9)) {
159 unsigned thread_concurrency(void)
162 #ifdef thread_concurrency_win32_
163 thread_concurrency_win32_
;
165 if ((ret
= thread_concurrency_getdynamic()))
167 if ((ret
= thread_concurrency_sysctl()))
169 if ((ret
= thread_concurrency_sysconf()))
171 if ((ret
= thread_concurrency_linux()))
176 static pthread_mutexattr_t mutex_attr
;
177 static pthread_rwlockattr_t rwmutex_attr
;
178 #ifdef USE_PTHREAD_CONDATTR
179 static pthread_condattr_t cond_attr
;
180 #define cond_attr_p (&cond_attr)
182 #define cond_attr_p NULL
184 static pthread_attr_t thread_attr
;
186 #define do_pthread_mutex_init(m) \
189 r = pthread_mutex_init(m, &mutex_attr); \
191 fatal("pthread_mutex_init failed at %s: %d, %s", position_string(position_arg), r, error_decode(error_from_errno(EC_SYSCALL, r)));\
194 #define do_pthread_mutex_done(m) \
197 r = pthread_mutex_destroy(m); \
199 internal(caller_file_line, "mutex_done: pthread_mutex_destroy failed: %d, %s", r, error_decode(error_from_errno(EC_SYSCALL, r)));\
202 #define do_pthread_mutex_lock(m) \
205 r = pthread_mutex_lock(m); \
207 internal(caller_file_line, "mutex_lock: pthread_mutex_lock failed: %d, %s", r, error_decode(error_from_errno(EC_SYSCALL, r)));\
210 #define do_pthread_mutex_trylock(m) \
213 r = pthread_mutex_trylock(m); \
215 if (unlikely(r != EBUSY) && unlikely(r != EDEADLK)) \
216 internal(caller_file_line, "mutex_trylock: pthread_mutex_trylock failed: %d, %s", r, error_decode(error_from_errno(EC_SYSCALL, r)));\
222 #define do_pthread_mutex_unlock(m) \
225 r = pthread_mutex_unlock(m); \
227 internal(caller_file_line, "mutex_unlock: pthread_mutex_unlock failed: %d, %s", r, error_decode(error_from_errno(EC_SYSCALL, r)));\
230 #ifndef UNUSUAL_SPINLOCK
232 #define do_mutex_init do_pthread_mutex_init
233 #define do_mutex_done do_pthread_mutex_done
234 #define do_mutex_lock do_pthread_mutex_lock
235 #define do_mutex_trylock do_pthread_mutex_trylock
236 #define do_mutex_unlock do_pthread_mutex_unlock
240 #define do_mutex_init(m) \
243 r = pthread_spin_init(m, PTHREAD_PROCESS_PRIVATE); \
245 fatal("pthread_spin_init failed at %s: %d, %s", position_string(position_arg), r, error_decode(error_from_errno(EC_SYSCALL, r)));\
248 #define do_mutex_done(m) \
251 r = pthread_spin_destroy(m); \
253 internal(caller_file_line, "mutex_done: pthread_spin_destroy failed: %d, %s", r, error_decode(error_from_errno(EC_SYSCALL, r)));\
256 #define do_mutex_lock(m) \
259 r = pthread_spin_lock(m); \
261 internal(caller_file_line, "mutex_lock: pthread_spin_lock failed: %d, %s", r, error_decode(error_from_errno(EC_SYSCALL, r)));\
264 #define do_mutex_trylock(m) \
267 r = pthread_spin_trylock(m); \
269 if (unlikely(r != EBUSY) && unlikely(r != EDEADLK)) \
270 internal(caller_file_line, "mutex_trylock: pthread_spin_trylock failed: %d, %s", r, error_decode(error_from_errno(EC_SYSCALL, r)));\
276 #define do_mutex_unlock(m) \
279 r = pthread_spin_unlock(m); \
281 internal(caller_file_line, "mutex_unlock: pthread_spin_unlock failed: %d, %s", r, error_decode(error_from_errno(EC_SYSCALL, r)));\
286 #define do_rwmutex_init(m) \
289 r = pthread_rwlock_init(m, &rwmutex_attr); \
291 fatal("pthread_rwlock_init failed at %s: %d, %s", position_string(position_arg), r, error_decode(error_from_errno(EC_SYSCALL, r)));\
294 #define do_rwmutex_done(m) \
297 r = pthread_rwlock_destroy(m); \
299 internal(caller_file_line, "rwmutex_done: pthread_rwlock_destroy failed: %d, %s", r, error_decode(error_from_errno(EC_SYSCALL, r)));\
302 #define do_rwmutex_lock_read(m) \
305 r = pthread_rwlock_rdlock(m); \
307 internal(caller_file_line, "rwmutex_lock_read: pthread_rwlock_rdlock failed: %d, %s", r, error_decode(error_from_errno(EC_SYSCALL, r)));\
310 #define do_rwmutex_unlock_read(m) \
313 r = pthread_rwlock_unlock(m); \
315 internal(caller_file_line, "rwmutex_unlock_read: pthread_rwlock_unlock failed: %d, %s", r, error_decode(error_from_errno(EC_SYSCALL, r)));\
318 #define do_rwmutex_lock_write(m) \
321 r = pthread_rwlock_wrlock(m); \
323 internal(caller_file_line, "rwmutex_lock_write: pthread_rwlock_wrlock failed: %d, %s", r, error_decode(error_from_errno(EC_SYSCALL, r)));\
326 #define do_rwmutex_unlock_write(m) \
329 r = pthread_rwlock_unlock(m); \
331 internal(caller_file_line, "rwmutex_unlock_write: pthread_rwlock_unlock failed: %d, %s", r, error_decode(error_from_errno(EC_SYSCALL, r)));\
334 #define do_cond_init(c) \
337 do_pthread_mutex_init(&(c)->mutex); \
338 r = pthread_cond_init(&(c)->cond, cond_attr_p); \
340 fatal("pthread_cond_init failed at %s: %d, %s", position_string(position_arg), r, error_decode(error_from_errno(EC_SYSCALL, r)));\
343 #define do_cond_done(c) \
346 do_pthread_mutex_done(&(c)->mutex); \
347 r = pthread_cond_destroy(&(c)->cond); \
349 internal(caller_file_line, "cond_done: pthread_cond_destroy failed: %d, %s", r, error_decode(error_from_errno(EC_SYSCALL, r)));\
352 #define do_cond_lock(c) \
354 do_pthread_mutex_lock(&(c)->mutex); \
357 #define do_cond_unlock(c) \
359 do_pthread_mutex_unlock(&(c)->mutex); \
362 #define do_cond_unlock_signal(c) \
365 do_pthread_mutex_unlock(&(c)->mutex); \
366 r = pthread_cond_signal(&(c)->cond); \
368 internal(caller_file_line, "cond_unlock_signal: pthread_cond_signal failed: %d, %s", r, error_decode(error_from_errno(EC_SYSCALL, r)));\
371 #define do_cond_unlock_broadcast(c) \
374 do_pthread_mutex_unlock(&(c)->mutex); \
375 r = pthread_cond_broadcast(&(c)->cond); \
377 internal(caller_file_line, "cond_unlock_broadcast: pthread_cond_broadcast failed: %d, %s", r, error_decode(error_from_errno(EC_SYSCALL, r)));\
380 #define do_cond_wait(c) \
384 r = pthread_cond_wait(&(c)->cond, &(c)->mutex); \
386 if (r == EINTR) goto again; \
387 internal(caller_file_line, "cond_wait: pthread_cond_wait failed: %d, %s", r, error_decode(error_from_errno(EC_SYSCALL, r)));\
391 #if !defined(USE_PTHREAD_CONDATTR)
393 static struct timespec
cond_getts(uint32_t us
)
399 EINTR_LOOP(r
, gettimeofday(&tv
, NULL
));
400 if (unlikely(r
== -1)) {
402 fatal("gettimeofday failed: %d, %s", e
, error_decode(error_from_errno(EC_SYSCALL
, e
)));
404 if (unlikely(us
>= 1000000)) {
405 tv
.tv_sec
+= us
/ 1000000;
409 if (unlikely(tv
.tv_usec
>= 1000000)) {
410 tv
.tv_usec
-= 1000000;
413 ts
.tv_sec
= tv
.tv_sec
;
414 ts
.tv_nsec
= (uint32_t)tv
.tv_usec
* 1000;
421 static struct timespec
cond_getts(uint32_t us
)
426 EINTR_LOOP(r
, clock_gettime(clock_id
, &ts
));
427 if (unlikely(r
== -1)) {
429 fatal("clock_gettime(%d) failed: %d, %s", (int)clock_id
, e
, error_decode(error_from_errno(EC_SYSCALL
, e
)));
431 if (unlikely(us
>= 1000000)) {
432 ts
.tv_sec
+= us
/ 1000000;
435 ts
.tv_nsec
+= us
* 1000;
436 if (unlikely(ts
.tv_nsec
>= 1000000000)) {
437 ts
.tv_nsec
-= 1000000000;
446 #define do_cond_wait_us(c, us) \
449 struct timespec ts; \
451 ts = cond_getts(us); \
453 r = pthread_cond_timedwait(&(c)->cond, &(c)->mutex, &ts); \
455 if (likely(r == ETIMEDOUT)) return false; \
456 if (r == EINTR) goto again; \
457 internal(caller_file_line, "cond_wait_us: pthread_cond_timedwait failed: %d, %s", r, error_decode(error_from_errno(EC_SYSCALL, r)));\
463 #define do_thread_spawn(t, function, arg, priority, err) \
466 r = pthread_create(t, &thread_attr, function, arg); \
468 ajla_error_t e = error_from_errno(EC_SYSCALL, r); \
469 fatal_mayfail(e, err, "pthread_create failed at %s: %d, %s", position_string(position_arg), r, error_decode(e));\
474 #define do_thread_join(t) \
477 r = pthread_join(*t, NULL); \
479 internal(caller_file_line, "thread_join: pthread_join failed: %d, %s", r, error_decode(error_from_errno(EC_SYSCALL, r)));\
483 #if !defined(HAVE___THREAD)
485 #define do_tls_init(tl) \
488 r = pthread_key_create(tl, NULL); \
490 fatal("pthread_key_create failed at %s: %d, %s", position_string(position_arg), r, error_decode(error_from_errno(EC_SYSCALL, r)));\
493 #define do_tls_done(tl) \
496 r = pthread_key_delete(*(tl)); \
498 internal(caller_file_line, "tls_done: pthread_key_delete failed: %d, %s", r, error_decode(error_from_errno(EC_SYSCALL, r)));\
501 #define do_tls_get(tl, ret) \
503 *(ret) = ptr_to_num(pthread_getspecific(*(tl))); \
506 #define do_tls_set(tl, val) \
509 r = pthread_setspecific(*(tl), (void *)(val)); \
511 internal(caller_file_line, "tls_set: pthread_setspecific failed: %d, %s", r, error_decode(error_from_errno(EC_SYSCALL, r)));\
516 #include "th_com.inc"
519 #ifndef barrier_write_before_lock
521 #if defined(HAVE_PTHREAD_SPIN_INIT) && !defined(UNUSUAL_ARITHMETICS)
523 void barrier_write_before_lock(void)
525 pthread_spinlock_t lock
;
527 #ifdef barrier_write_before_unlock_lock
528 barrier_write_before_unlock_lock();
530 r
= pthread_spin_init(&lock
, PTHREAD_PROCESS_PRIVATE
);
532 fatal("pthread_spin_init failed: %d, %s", r
, error_decode(error_from_errno(EC_SYSCALL
, r
)));
533 r
= pthread_spin_lock(&lock
);
535 internal(file_line
, "pthread_spin_lock failed: %d, %s", r
, error_decode(error_from_errno(EC_SYSCALL
, r
)));
536 r
= pthread_spin_unlock(&lock
);
538 internal(file_line
, "pthread_spin_unlock failed: %d, %s", r
, error_decode(error_from_errno(EC_SYSCALL
, r
)));
539 r
= pthread_spin_destroy(&lock
);
541 internal(file_line
, "pthread_spin_destroy failed: %d, %s", r
, error_decode(error_from_errno(EC_SYSCALL
, r
)));
546 #define barrier_lock_need_tls
547 struct barrier_lock
{
549 tls_destructor_t destructor
;
551 static tls_decl(struct barrier_lock
*, barrier_lock
);
553 static void barrier_lock_destructor(tls_destructor_t
*destr
)
555 struct barrier_lock
*lock
= get_struct(destr
, struct barrier_lock
, destructor
);
556 mutex_done(&lock
->mutex
);
560 void barrier_write_before_lock(void)
562 struct barrier_lock
*lock
= tls_get(struct barrier_lock
*, barrier_lock
);
563 if (unlikely(!lock
)) {
564 lock
= mem_alloc(struct barrier_lock
*, sizeof(struct barrier_lock
));
565 mutex_init(&lock
->mutex
);
566 tls_set(struct barrier_lock
*, barrier_lock
, lock
);
567 tls_destructor(&lock
->destructor
, barrier_lock_destructor
);
569 #ifdef barrier_write_before_unlock_lock
570 barrier_write_before_unlock_lock();
572 mutex_lock(&lock
->mutex
);
573 mutex_unlock(&lock
->mutex
);
581 #include "th_sig.inc"
584 #ifdef USE_PTHREAD_CONDATTR
585 static bool pthread_condattr_try_clock(clockid_t c
, bool mayfail
)
587 int r
= pthread_condattr_setclock(cond_attr_p
, c
);
589 if (unlikely(!mayfail
))
590 fatal("pthread_condattr_setclock(%d) failed: %d, %s", (int)c
, r
, error_decode(error_from_errno(EC_SYSCALL
, r
)));
594 r
= pthread_cond_init(&cond
, cond_attr_p
);
596 (!mayfail
? fatal
: warning
)("pthread_cond_init (clock %d) failed: %d, %s", (int)c
, r
, error_decode(error_from_errno(EC_SYSCALL
, r
)));
599 r
= pthread_cond_destroy(&cond
);
601 internal(file_line
, "pthread_condattr_try_clock: pthread_cond_destroy (clock %d) failed: %d, %s", (int)c
, r
, error_decode(error_from_errno(EC_SYSCALL
, r
)));
608 void thread_init(void)
613 #if defined(__linux__)
617 if (likely(os_read_file("/sys/devices/system/cpu/possible", &str
, &len
, &sink
))) {
618 array_add(char, &str
, &len
, 0);
619 if (!strcmp(str
, "0\n"))
620 thread_needs_barriers
= false;
625 r
= pthread_mutexattr_init(&mutex_attr
);
627 fatal("pthread_mutexattr_init failed: %d, %s", r
, error_decode(error_from_errno(EC_SYSCALL
, r
)));
628 #ifdef DEBUG_OBJECT_POSSIBLE
630 #if defined(HAVE_PTHREAD_MUTEX_ERRORCHECK)
631 r
= pthread_mutexattr_settype(&mutex_attr
, PTHREAD_MUTEX_ERRORCHECK
);
632 #elif defined(HAVE_PTHREAD_MUTEX_ERRORCHECK_NP)
633 r
= pthread_mutexattr_settype(&mutex_attr
, PTHREAD_MUTEX_ERRORCHECK_NP
);
638 fatal("pthread_mutexattr_settype failed: %d, %s", r
, error_decode(error_from_errno(EC_SYSCALL
, r
)));
642 r
= pthread_rwlockattr_init(&rwmutex_attr
);
644 fatal("pthread_rwlockattr_init failed: %d, %s", r
, error_decode(error_from_errno(EC_SYSCALL
, r
)));
646 #ifdef USE_PTHREAD_CONDATTR
647 r
= pthread_condattr_init(&cond_attr
);
649 fatal("pthread_condattr_init failed: %d, %s", r
, error_decode(error_from_errno(EC_SYSCALL
, r
)));
650 #ifdef HAVE_CLOCK_MONOTONIC_RAW
651 if (likely(!pthread_condattr_try_clock(CLOCK_MONOTONIC_RAW
, true)))
654 if (unlikely(!pthread_condattr_try_clock(CLOCK_MONOTONIC
, true))) {
655 pthread_condattr_try_clock(CLOCK_REALTIME
, false);
660 r
= pthread_attr_init(&thread_attr
);
662 fatal("pthread_attr_init failed: %d, %s", r
, error_decode(error_from_errno(EC_SYSCALL
, r
)));
664 r
= pthread_attr_getstacksize(&thread_attr
, &stack
);
666 fatal("pthread_attr_getstacksize failed: %d, %s", r
, error_decode(error_from_errno(EC_SYSCALL
, r
)));
667 if (stack
< MINIMUM_STACK_SIZE
) {
668 r
= pthread_attr_setstacksize(&thread_attr
, MINIMUM_STACK_SIZE
);
670 fatal("pthread_attr_setstacksize failed: %d, %s", r
, error_decode(error_from_errno(EC_SYSCALL
, r
)));
673 thread_signal_init();
674 thread_common_init();
675 #ifdef barrier_lock_need_tls
676 tls_init(struct barrier_lock
*, barrier_lock
);
680 void thread_done(void)
683 thread_common_done();
684 thread_signal_done();
685 #ifdef barrier_lock_need_tls
686 tls_done(struct barrier_lock
*, barrier_lock
);
688 r
= pthread_attr_destroy(&thread_attr
);
690 fatal("pthread_attr_destroy failed: %d, %s", r
, error_decode(error_from_errno(EC_SYSCALL
, r
)));
691 r
= pthread_mutexattr_destroy(&mutex_attr
);
693 fatal("pthread_mutexattr_destroy failed: %d, %s", r
, error_decode(error_from_errno(EC_SYSCALL
, r
)));
694 r
= pthread_rwlockattr_destroy(&rwmutex_attr
);
696 fatal("pthread_rwlockattr_destroy failed: %d, %s", r
, error_decode(error_from_errno(EC_SYSCALL
, r
)));
697 #ifdef USE_PTHREAD_CONDATTR
698 r
= pthread_condattr_destroy(&cond_attr
);
700 fatal("pthread_condattr_destroy failed: %d, %s", r
, error_decode(error_from_errno(EC_SYSCALL
, r
)));