X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;f=src%2Fthread%2Fpthread_key_create.c;h=e26f199c3c9cce5b7050bf576501533060d9e06b;hb=11ce1b133d594b6a454d3e8d5941e7a6a432c42b;hp=8d7f81625bbddce6dbcd2f7d97081de34bc02b37;hpb=6c16d3e98ab2e466bb12ded12dfad1f8f272b1f8;p=musl diff --git a/src/thread/pthread_key_create.c b/src/thread/pthread_key_create.c index 8d7f8162..e26f199c 100644 --- a/src/thread/pthread_key_create.c +++ b/src/thread/pthread_key_create.c @@ -1,26 +1,125 @@ #include "pthread_impl.h" -const size_t __pthread_tsd_size = sizeof(void *) * PTHREAD_KEYS_MAX; +volatile size_t __pthread_tsd_size = sizeof(void *) * PTHREAD_KEYS_MAX; void *__pthread_tsd_main[PTHREAD_KEYS_MAX] = { 0 }; +static void (*keys[PTHREAD_KEYS_MAX])(void *); + +static pthread_rwlock_t key_lock = PTHREAD_RWLOCK_INITIALIZER; + +static pthread_key_t next_key; + static void nodtor(void *dummy) { } -int pthread_key_create(pthread_key_t *k, void (*dtor)(void *)) +static void dirty(void *dummy) +{ +} + +struct cleanup_args { + pthread_t caller; + int ret; +}; + +static void clean_dirty_tsd_callback(void *p) +{ + struct cleanup_args *args = p; + pthread_t self = __pthread_self(); + pthread_key_t i; + for (i=0; itsd[i]) + self->tsd[i] = 0; + } + /* Arbitrary choice to avoid data race. */ + if (args->caller == self) args->ret = 0; +} + +static int clean_dirty_tsd(void) { - static void (*keys[PTHREAD_KEYS_MAX])(void *); - unsigned i = (uintptr_t)&k / 16 % PTHREAD_KEYS_MAX; - unsigned j = i; + struct cleanup_args args = { + .caller = __pthread_self(), + .ret = EAGAIN + }; + __pthread_key_delete_synccall(clean_dirty_tsd_callback, &args); + return args.ret; +} + +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. */ + if (!self->tsd) self->tsd = __pthread_tsd_main; - pthread_self(); - libc.tsd_keys = keys; + /* Purely a sentinel value since null means slot is free. */ if (!dtor) dtor = nodtor; + + pthread_rwlock_wrlock(&key_lock); do { - if (!a_cas_p(keys+j, 0, dtor)) { - *k = j; + if (!keys[j]) { + keys[next_key = *k = j] = dtor; + 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; jtsd_used && jtsd_used = 0; + for (i=0; itsd[i]; + void (*dtor)(void *) = keys[i]; + self->tsd[i] = 0; + if (val && dtor && dtor != nodtor && dtor != dirty) { + pthread_rwlock_unlock(&key_lock); + dtor(val); + pthread_rwlock_rdlock(&key_lock); + } + } + pthread_rwlock_unlock(&key_lock); + } +} + +weak_alias(__pthread_key_create, pthread_key_create);