path: root/src/env/__init_tls.c
diff options
authorRich Felker <>2019-02-15 22:29:01 -0500
committerRich Felker <>2019-02-15 22:29:01 -0500
commit8f11e6127fe93093f81a52b15bb1537edc3fc8af (patch)
tree6868c949ea888b3695361bb9fa67af86dc546bfe /src/env/__init_tls.c
parent04335d9260c076cf4d9264bd93dd3b06c237a639 (diff)
track all live threads in an AS-safe, fully-consistent linked list
the hard problem here is unlinking threads from a list when they exit without creating a window of inconsistency where the kernel task for a thread still exists and is still executing instructions in userspace, but is not reflected in the list. the magic solution here is getting rid of per-thread exit futex addresses (set_tid_address), and instead using the exit futex to unlock the global thread list. since pthread_join can no longer see the thread enter a detach_state of EXITED (which depended on the exit futex address pointing to the detach_state), it must now observe the unlocking of the thread list lock before it can unmap the joined thread and return. it doesn't actually have to take the lock. for this, a __tl_sync primitive is offered, with a signature that will allow it to be enhanced for quick return even under contention on the lock, if needed. for now, the exiting thread always performs a futex wake on its detach_state. a future change could optimize this out except when there is already a joiner waiting. initial/dynamic variants of detached state no longer need to be tracked separately, since the futex address is always set to the global list lock, not a thread-local address that could become invalid on detached thread exit. all detached threads, however, must perform a second sigprocmask syscall to block implementation-internal signals, since locking the thread list with them already blocked is not permissible. the arch-independent C version of __unmapself no longer needs to take a lock or setup its own futex address to release the lock, since it must necessarily be called with the thread list lock already held, guaranteeing exclusive access to the temporary stack. changes to libc.threads_minus_1 no longer need to be atomic, since they are guarded by the thread list lock. it is largely vestigial at this point, and can be replaced with a cheaper boolean indicating whether the process is multithreaded at some point in the future.
Diffstat (limited to 'src/env/__init_tls.c')
1 files changed, 4 insertions, 1 deletions
diff --git a/src/env/__init_tls.c b/src/env/__init_tls.c
index 842886f6..f1874f2a 100644
--- a/src/env/__init_tls.c
+++ b/src/env/__init_tls.c
@@ -8,6 +8,8 @@
#include "atomic.h"
#include "syscall.h"
+volatile int __thread_list_lock;
int __init_tp(void *p)
pthread_t td = p;
@@ -16,9 +18,10 @@ int __init_tp(void *p)
if (r < 0) return -1;
if (!r) libc.can_do_threads = 1;
td->detach_state = DT_JOINABLE;
- td->tid = __syscall(SYS_set_tid_address, &td->detach_state);
+ td->tid = __syscall(SYS_set_tid_address, &__thread_list_lock);
td->locale = &libc.global_locale;
td->robust_list.head = &td->robust_list.head;
+ td->next = td->prev = td;
return 0;