summaryrefslogtreecommitdiff
path: root/lib/libc/thread
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libc/thread')
-rw-r--r--lib/libc/thread/Kbuild3
-rw-r--r--lib/libc/thread/thrd_create.c113
-rw-r--r--lib/libc/thread/thrd_current.c6
-rw-r--r--lib/libc/thread/thrd_detach.c23
-rw-r--r--lib/libc/thread/thrd_equal.c6
-rw-r--r--lib/libc/thread/thrd_exit.c25
-rw-r--r--lib/libc/thread/thrd_join.c37
-rw-r--r--lib/libc/thread/thrd_sleep.c17
-rw-r--r--lib/libc/thread/thrd_yield.c7
-rw-r--r--lib/libc/thread/tss_create.c0
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