diff options
Diffstat (limited to 'src/thread/pthread_key_create.c')
-rw-r--r-- | src/thread/pthread_key_create.c | 98 |
1 files changed, 36 insertions, 62 deletions
diff --git a/src/thread/pthread_key_create.c b/src/thread/pthread_key_create.c index e26f199c..39770c7a 100644 --- a/src/thread/pthread_key_create.c +++ b/src/thread/pthread_key_create.c @@ -1,4 +1,5 @@ #include "pthread_impl.h" +#include "fork_impl.h" volatile size_t __pthread_tsd_size = sizeof(void *) * PTHREAD_KEYS_MAX; void *__pthread_tsd_main[PTHREAD_KEYS_MAX] = { 0 }; @@ -13,43 +14,23 @@ static void nodtor(void *dummy) { } -static void dirty(void *dummy) +static void dummy_0(void) { } -struct cleanup_args { - pthread_t caller; - int ret; -}; +weak_alias(dummy_0, __tl_lock); +weak_alias(dummy_0, __tl_unlock); -static void clean_dirty_tsd_callback(void *p) +void __pthread_key_atfork(int who) { - struct cleanup_args *args = p; - pthread_t self = __pthread_self(); - pthread_key_t i; - for (i=0; i<PTHREAD_KEYS_MAX; i++) { - if (keys[i] == dirty && self->tsd[i]) - self->tsd[i] = 0; - } - /* Arbitrary choice to avoid data race. */ - if (args->caller == self) args->ret = 0; -} - -static int clean_dirty_tsd(void) -{ - struct cleanup_args args = { - .caller = __pthread_self(), - .ret = EAGAIN - }; - __pthread_key_delete_synccall(clean_dirty_tsd_callback, &args); - return args.ret; + if (who<0) __pthread_rwlock_rdlock(&key_lock); + else if (!who) __pthread_rwlock_unlock(&key_lock); + else key_lock = (pthread_rwlock_t)PTHREAD_RWLOCK_INITIALIZER; } int __pthread_key_create(pthread_key_t *k, void (*dtor)(void *)) { - pthread_key_t j = next_key; pthread_t self = __pthread_self(); - int found_dirty = 0; /* This can only happen in the main thread before * pthread_create has been called. */ @@ -58,46 +39,38 @@ int __pthread_key_create(pthread_key_t *k, void (*dtor)(void *)) /* Purely a sentinel value since null means slot is free. */ if (!dtor) dtor = nodtor; - pthread_rwlock_wrlock(&key_lock); + __pthread_rwlock_wrlock(&key_lock); + pthread_key_t j = next_key; do { if (!keys[j]) { keys[next_key = *k = j] = dtor; - pthread_rwlock_unlock(&key_lock); + __pthread_rwlock_unlock(&key_lock); return 0; - } else if (keys[j] == dirty) { - found_dirty = 1; } } while ((j=(j+1)%PTHREAD_KEYS_MAX) != next_key); - /* It's possible that all slots are in use or __synccall fails. */ - if (!found_dirty || clean_dirty_tsd()) { - pthread_rwlock_unlock(&key_lock); - return EAGAIN; - } - - /* If this point is reached there is necessarily a newly-cleaned - * slot to allocate to satisfy the caller's request. Find it and - * mark any additional previously-dirty slots clean. */ - for (j=0; j<PTHREAD_KEYS_MAX; j++) { - if (keys[j] == dirty) { - if (dtor) { - keys[next_key = *k = j] = dtor; - dtor = 0; - } else { - keys[j] = 0; - } - } - } - - pthread_rwlock_unlock(&key_lock); - return 0; + __pthread_rwlock_unlock(&key_lock); + return EAGAIN; } -int __pthread_key_delete_impl(pthread_key_t k) +int __pthread_key_delete(pthread_key_t k) { - pthread_rwlock_wrlock(&key_lock); - keys[k] = dirty; - pthread_rwlock_unlock(&key_lock); + sigset_t set; + pthread_t self = __pthread_self(), td=self; + + __block_app_sigs(&set); + __pthread_rwlock_wrlock(&key_lock); + + __tl_lock(); + do td->tsd[k] = 0; + while ((td=td->next)!=self); + __tl_unlock(); + + keys[k] = 0; + + __pthread_rwlock_unlock(&key_lock); + __restore_sigs(&set); + return 0; } @@ -106,20 +79,21 @@ void __pthread_tsd_run_dtors() pthread_t self = __pthread_self(); int i, j; for (j=0; self->tsd_used && j<PTHREAD_DESTRUCTOR_ITERATIONS; j++) { - pthread_rwlock_rdlock(&key_lock); + __pthread_rwlock_rdlock(&key_lock); self->tsd_used = 0; for (i=0; i<PTHREAD_KEYS_MAX; i++) { void *val = self->tsd[i]; void (*dtor)(void *) = keys[i]; self->tsd[i] = 0; - if (val && dtor && dtor != nodtor && dtor != dirty) { - pthread_rwlock_unlock(&key_lock); + if (val && dtor && dtor != nodtor) { + __pthread_rwlock_unlock(&key_lock); dtor(val); - pthread_rwlock_rdlock(&key_lock); + __pthread_rwlock_rdlock(&key_lock); } } - pthread_rwlock_unlock(&key_lock); + __pthread_rwlock_unlock(&key_lock); } } weak_alias(__pthread_key_create, pthread_key_create); +weak_alias(__pthread_key_delete, pthread_key_delete); |