diff options
| author | Kacper <kacper@mail.openlinux.dev> | 2025-12-22 23:27:56 +0100 |
|---|---|---|
| committer | Kacper <kacper@mail.openlinux.dev> | 2025-12-22 23:30:32 +0100 |
| commit | 0f30d227497418c6d3bef7d52244407e30454504 (patch) | |
| tree | 0e1ac19623d3268380cf74328cdf643648a2f43c /lib/libc/thread | |
| parent | 90dad97fc07f049383903a166631e2c257f9b8c1 (diff) | |
Added c11 threads, fixed some locks and add *_unlocked functions
Diffstat (limited to 'lib/libc/thread')
| -rw-r--r-- | lib/libc/thread/Kbuild | 3 | ||||
| -rw-r--r-- | lib/libc/thread/thrd_create.c | 113 | ||||
| -rw-r--r-- | lib/libc/thread/thrd_current.c | 6 | ||||
| -rw-r--r-- | lib/libc/thread/thrd_detach.c | 23 | ||||
| -rw-r--r-- | lib/libc/thread/thrd_equal.c | 6 | ||||
| -rw-r--r-- | lib/libc/thread/thrd_exit.c | 25 | ||||
| -rw-r--r-- | lib/libc/thread/thrd_join.c | 37 | ||||
| -rw-r--r-- | lib/libc/thread/thrd_sleep.c | 17 | ||||
| -rw-r--r-- | lib/libc/thread/thrd_yield.c | 7 | ||||
| -rw-r--r-- | lib/libc/thread/tss_create.c | 0 |
10 files changed, 233 insertions, 4 deletions
diff --git a/lib/libc/thread/Kbuild b/lib/libc/thread/Kbuild index 3e28ece3..b419b566 100644 --- a/lib/libc/thread/Kbuild +++ b/lib/libc/thread/Kbuild @@ -1 +1,4 @@ +obj-y += thrd_create.o obj-y += thrd_current.o +obj-y += thrd_exit.o +obj-y += thrd_join.o diff --git a/lib/libc/thread/thrd_create.c b/lib/libc/thread/thrd_create.c new file mode 100644 index 00000000..e5b7405c --- /dev/null +++ b/lib/libc/thread/thrd_create.c @@ -0,0 +1,113 @@ +#include <__thread.h> +#include <asm/prctl.h> +#include <errno.h> +#include <libc.h> +#include <linux/sched.h> +#include <sched.h> +#include <signal.h> +#include <stdio.h> +#include <sys/mman.h> +#include <syscall.h> +#include <threads.h> +#include <unistd.h> + +struct thread_start { + struct __thread_self *self; + thrd_start_t func; + void *arg; +}; + +static long __thread_start(void *arg) +{ + struct thread_start *ts = (struct thread_start *)arg; + syscall(set_tid_address, &ts->self->tid); + int r = ts->func(ts->arg); + thrd_exit(r); + __builtin_unreachable(); +} + +int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) +{ + struct thread_start *ts; + + if ((void *)thr == NULL || func == NULL) + return thrd_error; + + /* Ensure callers never observe an uninitialized thread handle on failure. */ + *thr = NULL; + + void *map; + size_t size = THREAD_GUARD_SIZE + THREAD_STACK_SIZE + __libc.tls.size + sizeof(struct __thread_self); + map = mmap(NULL, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + + if (__predict_false(map == MAP_FAILED)) + return thrd_nomem; + + if (__predict_false(mprotect((unsigned char *)map + THREAD_GUARD_SIZE, size - THREAD_GUARD_SIZE, + PROT_READ | PROT_WRITE) < 0)) { + munmap(map, size); + return thrd_error; + } + + unsigned char *base = (unsigned char *)map; + + /* Place TCB at the very end of the mapping. */ + struct __thread_self *tcb = (struct __thread_self *)(base + size - sizeof(struct __thread_self)); + + /* Place TLS immediately below the TCB, aligned down. */ + unsigned char *tls_end = (unsigned char *)tcb; + + size_t tls_align = __libc.tls.align; + if (tls_align == 0 || (tls_align & (tls_align - 1)) != 0) + tls_align = sizeof(void *); + if (tls_align < sizeof(void *)) + tls_align = sizeof(void *); + + unsigned char *tls_start = + (unsigned char *)(((uintptr_t)(tls_end - __libc.tls.size)) & ~((uintptr_t)tls_align - 1)); + + __libc_tls_copy(tls_start); + + /* Build initial stack just below TLS. */ + unsigned char *stack_top = tls_start; + ts = (struct thread_start *)(stack_top - sizeof(struct thread_start)); + stack_top -= sizeof(struct thread_start); + + ts->self = tcb; + ts->func = func; + ts->arg = arg; + + stack_top = (unsigned char *)((uintptr_t)stack_top & ~15UL); + + tcb->self = tcb; + tcb->map_base = map; + tcb->map_size = size; + tcb->errno_v = 0; + tcb->dtv = 0; + tcb->state = THREAD_STATE_JOINABLE; + + unsigned long flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM | + CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID; + +#if defined(__x86_64__) + long tid = __clone(__thread_start, ts, flags, stack_top, &tcb->tid, tcb, &tcb->tid); +#endif + + if (__predict_false(tid < 0)) { + munmap(map, size); + return thrd_error; + } + +#if !defined(__x86_64__) + if (tid == 0) { + __thread_start(ts); + __builtin_unreachable(); + } +#endif + + tcb->tid = tid; + + *thr = tcb; + + return thrd_success; +} diff --git a/lib/libc/thread/thrd_current.c b/lib/libc/thread/thrd_current.c index b588cc27..fd563bb6 100644 --- a/lib/libc/thread/thrd_current.c +++ b/lib/libc/thread/thrd_current.c @@ -1,9 +1,7 @@ -#include <__thread.h> +#include <libc/thread.h> #include <threads.h> -thread_local struct __thread_self __thread_self; - thrd_t thrd_current(void) { - return &__thread_self; + return __libc_thread_self(); } diff --git a/lib/libc/thread/thrd_detach.c b/lib/libc/thread/thrd_detach.c new file mode 100644 index 00000000..b315a20a --- /dev/null +++ b/lib/libc/thread/thrd_detach.c @@ -0,0 +1,23 @@ +#include <__thread.h> +#include <libc/futex.h> +#include <stdatomic.h> +#include <sys/mman.h> +#include <threads.h> +#include <unistd.h> + +int thrd_detach(thrd_t thr) +{ + struct __thread_self *tcb = (struct __thread_self *)thr; + + if (tcb == NULL) + return thrd_error; + + int state = atomic_exchange_explicit(&tcb->state, THREAD_STATE_DETACHED, memory_order_seq_cst); + + if (state == THREAD_STATE_EXITED) { + if (tcb->map_base && tcb->map_size) + munmap(tcb->map_base, tcb->map_size); + } + + return thrd_success; +} diff --git a/lib/libc/thread/thrd_equal.c b/lib/libc/thread/thrd_equal.c new file mode 100644 index 00000000..25cfd963 --- /dev/null +++ b/lib/libc/thread/thrd_equal.c @@ -0,0 +1,6 @@ +#include <threads.h> + +int thrd_equal(thrd_t thr0, thrd_t thr1) +{ + return thr0 == thr1; +} diff --git a/lib/libc/thread/thrd_exit.c b/lib/libc/thread/thrd_exit.c new file mode 100644 index 00000000..9ba1f54a --- /dev/null +++ b/lib/libc/thread/thrd_exit.c @@ -0,0 +1,25 @@ +#include <__thread.h> +#include <libc/futex.h> +#include <stdatomic.h> +#include <sys/mman.h> +#include <threads.h> +#include <unistd.h> + +_Noreturn void thrd_exit(int res) +{ + struct __thread_self *self = thrd_current(); + + self->res = res; + + int state = atomic_load_explicit((int *)&self->state, memory_order_seq_cst); + if (state == THREAD_STATE_DETACHED) { + if (self->map_base && self->map_size) + munmap(self->map_base, self->map_size); + _exit(0); + } + + atomic_store_explicit(&self->state, THREAD_STATE_EXITED, memory_order_seq_cst); + __futex_wake(&self->state, 1); + + _exit(0); +} diff --git a/lib/libc/thread/thrd_join.c b/lib/libc/thread/thrd_join.c new file mode 100644 index 00000000..7ec38c4a --- /dev/null +++ b/lib/libc/thread/thrd_join.c @@ -0,0 +1,37 @@ +#include <__thread.h> +#include <libc/futex.h> +#include <stdatomic.h> +#include <sys/mman.h> +#include <threads.h> + +int thrd_join(thrd_t thr, int *res) +{ + struct __thread_self *tcb = (struct __thread_self *)thr; + if (tcb == NULL) + return thrd_error; + + int state = atomic_load_explicit(&tcb->state, memory_order_seq_cst); + if (state == THREAD_STATE_DETACHED) + return thrd_error; // Cannot join a detached thread + + while (atomic_load_explicit(&tcb->state, memory_order_seq_cst) != THREAD_STATE_EXITED) { + int cur = atomic_load_explicit(&tcb->state, memory_order_seq_cst); + int r = __futex_wait((volatile int *)&tcb->state, cur); + if (r < 0) + return thrd_error; + } + + if (res) { + *res = tcb->res; + } + + atomic_store_explicit(&tcb->state, THREAD_STATE_JOINABLE, memory_order_seq_cst); + __futex_wake((volatile int *)&tcb->state, 1); + + if (tcb->map_base == NULL || tcb->map_size == 0) + return thrd_error; + + munmap(tcb->map_base, tcb->map_size); + + return thrd_success; +} diff --git a/lib/libc/thread/thrd_sleep.c b/lib/libc/thread/thrd_sleep.c new file mode 100644 index 00000000..075431e3 --- /dev/null +++ b/lib/libc/thread/thrd_sleep.c @@ -0,0 +1,17 @@ +#include <errno.h> +#include <syscall.h> +#include <threads.h> +#include <time.h> + +int thrd_sleep(const struct timespec *duration, struct timespec *remaining) +{ + int r = __syscall(nanosleep, CLOCK_REALTIME, 0, duration, remaining); + + if (r == 0) + return 0; + + if (r == EINTR) + return -1; + + return -2; +} diff --git a/lib/libc/thread/thrd_yield.c b/lib/libc/thread/thrd_yield.c new file mode 100644 index 00000000..38cff265 --- /dev/null +++ b/lib/libc/thread/thrd_yield.c @@ -0,0 +1,7 @@ +#include <sched.h> +#include <threads.h> + +void thrd_yield(void) +{ + sched_yield(); +} diff --git a/lib/libc/thread/tss_create.c b/lib/libc/thread/tss_create.c new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/lib/libc/thread/tss_create.c |
