diff --git a/src/system/libroot/posix/pthread/Jamfile b/src/system/libroot/posix/pthread/Jamfile index 7a89bd68f2..b8e9fb7103 100644 --- a/src/system/libroot/posix/pthread/Jamfile +++ b/src/system/libroot/posix/pthread/Jamfile @@ -10,7 +10,7 @@ MergeObject posix_pthread.o : pthread_cleanup.cpp pthread_cond.c pthread_condattr.c - pthread_key.c + pthread_key.cpp pthread_mutex.c pthread_mutexattr.c pthread_once.c diff --git a/src/system/libroot/posix/pthread/pthread.c b/src/system/libroot/posix/pthread/pthread.c index f4822c26a3..31e6000f18 100644 --- a/src/system/libroot/posix/pthread/pthread.c +++ b/src/system/libroot/posix/pthread/pthread.c @@ -18,6 +18,7 @@ static const pthread_attr pthread_attr_default = { }; +static struct pthread_thread sMainThread; static int32 sPthreadSlot = -1; static int sConcurrencyLevel; @@ -25,7 +26,16 @@ static int sConcurrencyLevel; struct pthread_thread * __get_pthread(void) { - return (struct pthread_thread *)tls_get(sPthreadSlot); + struct pthread_thread *thread; + + if (sPthreadSlot == -1) + return &sMainThread; + + thread = (struct pthread_thread *)tls_get(sPthreadSlot); + if (thread == NULL) + return &sMainThread; + + return thread; } @@ -36,14 +46,15 @@ pthread_destroy_thread(void *data) // call cleanup handlers while (true) { - struct __pthread_cleanup_handler *handler = __pthread_cleanup_pop_handler(); + struct __pthread_cleanup_handler *handler + = __pthread_cleanup_pop_handler(); if (handler == NULL) break; handler->function(handler->argument); } - __pthread_key_call_destructors(); + __pthread_key_call_destructors(thread); free(thread); } @@ -100,9 +111,10 @@ pthread_create(pthread_t *_thread, const pthread_attr_t *_attr, threadID = spawn_thread(pthread_thread_entry, "pthread func", attr->sched_priority, thread); - if (threadID < B_OK) - return B_WOULD_BLOCK; + if (threadID < B_OK) { // stupid error code (EAGAIN) but demanded by POSIX + return B_WOULD_BLOCK; + } resume_thread(threadID); *_thread = threadID; diff --git a/src/system/libroot/posix/pthread/pthread_key.c b/src/system/libroot/posix/pthread/pthread_key.c deleted file mode 100644 index d30a35cc38..0000000000 --- a/src/system/libroot/posix/pthread/pthread_key.c +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2006, Jérôme Duval. All rights reserved. - * Distributed under the terms of the MIT License. - */ - - -#include "pthread_private.h" - -#include - -#include - - -// TODO: use a better algorithm than a linked list! -struct key_link { - struct key_link *next; - pthread_key_t key; - void (*destructor)(void*); -}; - -static pthread_mutex_t sKeyLock = PTHREAD_MUTEX_INITIALIZER; -static struct key_link *sKeyList = NULL; - - -void -__pthread_key_call_destructors() -{ - struct key_link *link = NULL; - - pthread_mutex_lock(&sKeyLock); - - for (link = sKeyList; link; link = link->next) { - void *data = pthread_getspecific(link->key); - if (link->destructor && data) - (*link->destructor)(data); - } - - pthread_mutex_unlock(&sKeyLock); -} - - -int -pthread_key_create(pthread_key_t *key, void (*destructor)(void*)) -{ - struct key_link *link; - - if (key == NULL) - return B_BAD_VALUE; - pthread_mutex_lock(&sKeyLock); - *key = tls_allocate(); - if (*key < 0) { - pthread_mutex_unlock(&sKeyLock); - return *key; - } - - // add key/destructor to the list - link = malloc(sizeof(struct key_link)); - if (!link) - return B_WOULD_BLOCK; - link->next = sKeyList; - link->key = *key; - link->destructor= destructor; - sKeyList = link; - pthread_mutex_unlock(&sKeyLock); - return B_OK; -} - - -int -pthread_key_delete(pthread_key_t key) -{ - struct key_link *link, *last = NULL; - pthread_mutex_lock(&sKeyLock); - - // remove key/destructor from the list - for (link = sKeyList; link; link = link->next) { - if (link->key == key) { - if (last) - last->next = link->next; - else - sKeyList = link->next; - free(link); - pthread_mutex_unlock(&sKeyLock); - return B_OK; - } - last = link; - } - - pthread_mutex_unlock(&sKeyLock); - return B_BAD_VALUE; -} - - -void * -pthread_getspecific(pthread_key_t key) -{ - return tls_get(key); -} - - -int -pthread_setspecific(pthread_key_t key, const void *value) -{ - // we don't check if the key is valid - tls_set(key, (void *)value); - return B_OK; -} - diff --git a/src/system/libroot/posix/pthread/pthread_key.cpp b/src/system/libroot/posix/pthread/pthread_key.cpp new file mode 100644 index 0000000000..e607bd39ec --- /dev/null +++ b/src/system/libroot/posix/pthread/pthread_key.cpp @@ -0,0 +1,150 @@ +/* + * Copyright 2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include "pthread_private.h" + +#include + + +static pthread_key sKeyTable[PTHREAD_KEYS_MAX]; +static int32 sNextSequence = 1; + + +/*! Retrieves the destructor of a key locklessly. + Returns the destructor's sequence in \a sequence. +*/ +static pthread_key_destructor +get_key_destructor(uint32 key, int32& sequence) +{ + pthread_key_destructor destructor = NULL; + + do { + sequence = sKeyTable[key].sequence; + if (sequence == PTHREAD_UNUSED_SEQUENCE) + return NULL; + + destructor = sKeyTable[key].destructor; + } while (sKeyTable[key].sequence != sequence); + + return destructor; +} + + +/*! Function to get the thread specific value of a key in a lockless + way. + \a sequence must be the sequence of the key table that this value + has to fit to. +*/ +static void* +get_key_value(pthread_thread* thread, uint32 key, int32 sequence) +{ + pthread_key_data& keyData = thread->specific[key]; + int32 specificSequence; + void* value; + + do { + specificSequence = keyData.sequence; + if (specificSequence != sequence) + return NULL; + + value = keyData.value; + } while (specificSequence != sequence); + + return value; +} + + +void +__pthread_key_call_destructors(pthread_thread* thread) +{ + for (uint32 key = 0; key < PTHREAD_KEYS_MAX; key++) { + int32 sequence; + pthread_key_destructor destructor = get_key_destructor(key, sequence); + void* value = get_key_value(thread, key, sequence); + + if (value != NULL && destructor != NULL) + destructor(value); + } +} + + +// #pragma mark - public API + + +int +pthread_key_create(pthread_key_t* _key, void (*destructor)(void*)) +{ + int32 nextSequence = atomic_add(&sNextSequence, 1); + + for (uint32 key = 0; key < PTHREAD_KEYS_MAX; key++) { + int32 sequence = sKeyTable[key].sequence; + if (sequence != PTHREAD_UNUSED_SEQUENCE) + continue; + + // try to acquire this slot + + if (atomic_test_and_set(&sKeyTable[key].sequence, nextSequence, + sequence) != sequence) + continue; + + sKeyTable[key].destructor = destructor; + *_key = key; + return 0; + } + + return EAGAIN; +} + + +int +pthread_key_delete(pthread_key_t key) +{ + if (key < 0 || key >= PTHREAD_KEYS_MAX) + return EINVAL; + + int32 sequence = atomic_set(&sKeyTable[key].sequence, + PTHREAD_UNUSED_SEQUENCE); + if (sequence == PTHREAD_UNUSED_SEQUENCE) + return EINVAL; + + return 0; +} + + +void* +pthread_getspecific(pthread_key_t key) +{ + pthread_thread* thread = __get_pthread(); + + if (key < 0 || key >= PTHREAD_KEYS_MAX) + return NULL; + + // check if this key is used, and our value belongs to its current meaning + int32 sequence = atomic_get(&sKeyTable[key].sequence); + if (sequence == PTHREAD_UNUSED_SEQUENCE + || thread->specific[key].sequence != sequence) + return NULL; + + return thread->specific[key].value; +} + + +int +pthread_setspecific(pthread_key_t key, const void* value) +{ + if (key < 0 || key >= PTHREAD_KEYS_MAX) + return EINVAL; + + int32 sequence = atomic_get(&sKeyTable[key].sequence); + if (sequence == PTHREAD_UNUSED_SEQUENCE) + return EINVAL; + + pthread_key_data& keyData = __get_pthread()->specific[key]; + keyData.sequence = sequence; + keyData.value = const_cast(value); + return 0; +} + diff --git a/src/system/libroot/posix/pthread/pthread_private.h b/src/system/libroot/posix/pthread/pthread_private.h index d57efecdaa..d8b8bee323 100644 --- a/src/system/libroot/posix/pthread/pthread_private.h +++ b/src/system/libroot/posix/pthread/pthread_private.h @@ -46,6 +46,20 @@ typedef struct _pthread_attr { int32 sched_priority; } pthread_attr; +typedef void (*pthread_key_destructor)(void *data); + +struct pthread_key { + vint32 sequence; + pthread_key_destructor destructor; +}; + +struct pthread_key_data { + vint32 sequence; + void *value; +}; + +#define PTHREAD_KEYS_MAX 256 +#define PTHREAD_UNUSED_SEQUENCE 0 // This structure is used internally only, it has no public equivalent struct pthread_thread { @@ -54,6 +68,7 @@ struct pthread_thread { int cancel_state; int cancel_type; bool cancelled; + struct pthread_key_data specific[PTHREAD_KEYS_MAX]; struct __pthread_cleanup_handler *cleanup_handlers; // TODO: move pthread keys in here, too }; @@ -63,7 +78,7 @@ struct pthread_thread { extern "C" { #endif -void __pthread_key_call_destructors(void); +void __pthread_key_call_destructors(struct pthread_thread *thread); struct pthread_thread *__get_pthread(void); #ifdef __cplusplus diff --git a/src/tests/system/libroot/posix/posixtestsuite/run_posix_tests.sh b/src/tests/system/libroot/posix/posixtestsuite/run_posix_tests.sh index d61b95c8a8..e6f875147b 100644 --- a/src/tests/system/libroot/posix/posixtestsuite/run_posix_tests.sh +++ b/src/tests/system/libroot/posix/posixtestsuite/run_posix_tests.sh @@ -2,7 +2,7 @@ usage() { - cat <