From dcd60371500a74d489372cac7240674c992c2484 Mon Sep 17 00:00:00 2001 From: Rich Felker Date: Fri, 5 Oct 2012 11:51:50 -0400 Subject: support for TLS in dynamic-loaded (dlopen) modules unlike other implementations, this one reserves memory for new TLS in all pre-existing threads at dlopen-time, and dlopen will fail with no resources consumed and no new libraries loaded if memory is not available. memory is not immediately distributed to running threads; that would be too complex and too costly. instead, assurances are made that threads needing the new TLS can obtain it in an async-signal-safe way from a buffer belonging to the dynamic linker/new module (via atomic fetch-and-add based allocator). I've re-appropriated the lock that was previously used for __synccall (synchronizing set*id() syscalls between threads) as a general pthread_create lock. it's a "backwards" rwlock where the "read" operation is safe atomic modification of the live thread count, which multiple threads can perform at the same time, and the "write" operation is making sure the count does not increase during an operation that depends on it remaining bounded (__synccall or dlopen). in static-linked programs that don't use __synccall, this lock is a no-op and has no cost. --- src/thread/lock_ptc.c | 18 ++++++++++++++++++ src/thread/pthread_create.c | 18 ++++++++---------- src/thread/synccall.c | 15 ++------------- 3 files changed, 28 insertions(+), 23 deletions(-) create mode 100644 src/thread/lock_ptc.c (limited to 'src/thread') diff --git a/src/thread/lock_ptc.c b/src/thread/lock_ptc.c new file mode 100644 index 00000000..7adedab7 --- /dev/null +++ b/src/thread/lock_ptc.c @@ -0,0 +1,18 @@ +#include + +static pthread_rwlock_t lock = PTHREAD_RWLOCK_INITIALIZER; + +void __inhibit_ptc() +{ + pthread_rwlock_wrlock(&lock); +} + +void __acquire_ptc() +{ + pthread_rwlock_rdlock(&lock); +} + +void __release_ptc() +{ + pthread_rwlock_unlock(&lock); +} diff --git a/src/thread/pthread_create.c b/src/thread/pthread_create.c index f53fc1ba..92ce9ffb 100644 --- a/src/thread/pthread_create.c +++ b/src/thread/pthread_create.c @@ -4,8 +4,8 @@ static void dummy_0() { } -weak_alias(dummy_0, __synccall_lock); -weak_alias(dummy_0, __synccall_unlock); +weak_alias(dummy_0, __acquire_ptc); +weak_alias(dummy_0, __release_ptc); weak_alias(dummy_0, __pthread_tsd_run_dtors); _Noreturn void pthread_exit(void *result) @@ -84,7 +84,7 @@ static void init_file_lock(FILE *f) if (f && f->lock<0) f->lock = 0; } -void *__copy_tls(unsigned char *, size_t); +void *__copy_tls(unsigned char *); int pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict attr, void *(*entry)(void *), void *restrict arg) { @@ -94,8 +94,6 @@ int pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict attr, struct pthread *self = pthread_self(), *new; unsigned char *map, *stack, *tsd; unsigned flags = 0x7d8f00; - size_t tls_cnt = libc.tls_cnt; - size_t tls_size = libc.tls_size; if (!self) return ENOSYS; if (!libc.threaded) { @@ -107,6 +105,8 @@ int pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict attr, libc.threaded = 1; } + __acquire_ptc(); + if (attr && attr->_a_stackaddr) { map = 0; tsd = (void *)(attr->_a_stackaddr-__pthread_tsd_size & -16); @@ -114,7 +114,7 @@ int pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict attr, if (attr) { guard = ROUND(attr->_a_guardsize + DEFAULT_GUARD_SIZE); size = guard + ROUND(attr->_a_stacksize - + DEFAULT_STACK_SIZE + tls_size); + + DEFAULT_STACK_SIZE + libc.tls_size); } size += __pthread_tsd_size; if (guard) { @@ -130,7 +130,7 @@ int pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict attr, } tsd = map + size - __pthread_tsd_size; } - new = __copy_tls(tsd - tls_size, tls_cnt); + new = __copy_tls(tsd - libc.tls_size); new->map_base = map; new->map_size = size; new->pid = self->pid; @@ -147,12 +147,10 @@ int pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict attr, new->canary = self->canary ^ (uintptr_t)&new; stack = (void *)new; - __synccall_lock(); - a_inc(&libc.threads_minus_1); ret = __clone(start, stack, flags, new, &new->tid, new, &new->tid); - __synccall_unlock(); + __release_ptc(); if (ret < 0) { a_dec(&libc.threads_minus_1); diff --git a/src/thread/synccall.c b/src/thread/synccall.c index fd377cb3..2b7eac25 100644 --- a/src/thread/synccall.c +++ b/src/thread/synccall.c @@ -9,7 +9,6 @@ static struct chain { static void (*callback)(void *), *context; static int chainlen; static sem_t chainlock, chaindone; -static pthread_rwlock_t lock = PTHREAD_RWLOCK_INITIALIZER; static void handler(int sig, siginfo_t *si, void *ctx) { @@ -59,7 +58,7 @@ void __synccall(void (*func)(void *), void *ctx) return; } - pthread_rwlock_wrlock(&lock); + __inhibit_ptc(); __syscall(SYS_rt_sigprocmask, SIG_BLOCK, SIGALL_SET, &oldmask, __SYSCALL_SSLEN); @@ -97,15 +96,5 @@ void __synccall(void (*func)(void *), void *ctx) __syscall(SYS_rt_sigprocmask, SIG_SETMASK, &oldmask, 0, __SYSCALL_SSLEN); - pthread_rwlock_unlock(&lock); -} - -void __synccall_lock() -{ - pthread_rwlock_rdlock(&lock); -} - -void __synccall_unlock() -{ - pthread_rwlock_unlock(&lock); + __release_ptc(); } -- cgit v1.2.1