From a635399b0744555fe9f3fc01d6f58ff29d3bc075 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Mon, 31 Mar 2008 15:10:00 +0000 Subject: [PATCH] * Rearranged pthread thread support a bit: there is now a pthread_thread structure that is attached (via TLS) to each pthread. * Implemented support for pthread_cleanup_{push|pop}(). * I haven't really been able to test these changes, yet, though. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@24704 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/posix/pthread.h | 49 ++++++-- src/system/libroot/posix/pthread/Jamfile | 1 + src/system/libroot/posix/pthread/pthread.c | 114 ++++++++++++------ .../libroot/posix/pthread/pthread_cleanup.cpp | 34 ++++++ .../libroot/posix/pthread/pthread_key.c | 31 ++--- .../libroot/posix/pthread/pthread_private.h | 31 ++++- 6 files changed, 193 insertions(+), 67 deletions(-) create mode 100644 src/system/libroot/posix/pthread/pthread_cleanup.cpp diff --git a/headers/posix/pthread.h b/headers/posix/pthread.h index 50a3ca5eb2..79753dd982 100644 --- a/headers/posix/pthread.h +++ b/headers/posix/pthread.h @@ -1,6 +1,7 @@ -/* -** Distributed under the terms of the Haiku License. -*/ +/* + * Copyright 2001-2008, Haiku Inc. All Rights Reserved. + * Distributed under the terms of the Haiku License. + */ #ifndef _PTHREAD_H_ #define _PTHREAD_H_ @@ -16,8 +17,8 @@ typedef int pthread_key_t; typedef struct _pthread_once pthread_once_t; typedef struct _pthread_rwlock *pthread_rwlock_t; typedef struct _pthread_rwlockattr *pthread_rwlockattr_t; -typedef struct _pthread_barrier *pthread_barrier_t; -typedef struct _pthread_barrierattr *pthread_barrierattr_t; +typedef struct _pthread_barrier *pthread_barrier_t; +typedef struct _pthread_barrierattr *pthread_barrierattr_t; typedef struct _pthread_spinlock *pthread_spinlock_t; struct _pthread_once { @@ -58,19 +59,40 @@ enum pthread_process_shared { #define PTHREAD_CANCEL_DISABLE 1 #define PTHREAD_CANCEL_DEFERRED 0 #define PTHREAD_CANCEL_ASYNCHRONOUS 2 -#define PTHREAD_CANCELED ((void *) 1) +#define PTHREAD_CANCELED ((void *) 1) -#define PTHREAD_NEEDS_INIT 0 -#define PTHREAD_DONE_INIT 1 -#define PTHREAD_ONCE_INIT { PTHREAD_NEEDS_INIT, NULL } +#define PTHREAD_NEEDS_INIT 0 +#define PTHREAD_DONE_INIT 1 +#define PTHREAD_ONCE_INIT { PTHREAD_NEEDS_INIT, NULL } -#define PTHREAD_BARRIER_SERIAL_THREAD -1 +#define PTHREAD_BARRIER_SERIAL_THREAD -1 #define PTHREAD_PRIO_NONE 0 #define PTHREAD_PRIO_INHERIT 1 #define PTHREAD_PRIO_PROTECT 2 //extern pthread_mutexattr_t pthread_mutexattr_default; +/* private structure */ +struct __pthread_cleanup_handler { + struct __pthread_cleanup_handler *previous; + void (*function)(void *argument); + void *argument; +}; + +#define pthread_cleanup_push(func, arg) \ + do { \ + struct __pthread_cleanup_handler __handler; \ + __handler.function = (func); \ + __handler.argument = (arg); \ + __pthread_cleanup_push_handler(&__handler); + +#define pthread_cleanup_pop(execute) \ + if (execute) \ + __handler.function(__handler.argument); \ + __pthread_cleanup_pop_handler(); \ + } while (0) + + #ifdef __cplusplus extern "C" { #endif @@ -132,7 +154,7 @@ extern int pthread_attr_init(pthread_attr_t *attr); extern int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate); extern int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); -extern int pthread_create(pthread_t *thread, const pthread_attr_t *attr, +extern int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg); extern int pthread_detach(pthread_t thread); extern int pthread_equal(pthread_t t1, pthread_t t2); @@ -148,6 +170,11 @@ extern int pthread_key_delete(pthread_key_t key); extern void *pthread_getspecific(pthread_key_t key); extern int pthread_setspecific(pthread_key_t key, const void *value); +/* private functions */ +extern void __pthread_cleanup_push_handler( + struct __pthread_cleanup_handler *handler); +extern struct __pthread_cleanup_handler *__pthread_cleanup_pop_handler(void); + #ifdef __cplusplus } #endif diff --git a/src/system/libroot/posix/pthread/Jamfile b/src/system/libroot/posix/pthread/Jamfile index fd8261fc27..23d9f97fc7 100644 --- a/src/system/libroot/posix/pthread/Jamfile +++ b/src/system/libroot/posix/pthread/Jamfile @@ -6,6 +6,7 @@ MergeObject posix_pthread.o : pthread.c pthread_atfork.c pthread_attr.c + pthread_cleanup.cpp pthread_cond.c pthread_condattr.c pthread_key.c diff --git a/src/system/libroot/posix/pthread/pthread.c b/src/system/libroot/posix/pthread/pthread.c index 52611b4a63..7494d7c998 100644 --- a/src/system/libroot/posix/pthread/pthread.c +++ b/src/system/libroot/posix/pthread/pthread.c @@ -1,45 +1,77 @@ /* + * Copyright 2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved. * Copyright 2006, Jérôme Duval. All rights reserved. * Distributed under the terms of the MIT License. */ -#include +#include "pthread_private.h" + #include #include -#include "pthread_private.h" + +#include + static const pthread_attr pthread_attr_default = { PTHREAD_CREATE_JOINABLE, B_NORMAL_PRIORITY }; -struct pthread_data { - thread_entry entry; - void *data; -}; + +static int32 sPthreadSlot = -1; + + +struct pthread_thread * +__get_pthread(void) +{ + return (struct pthread_thread *)tls_get(sPthreadSlot); +} + + +static void +pthread_destroy_thread(void *data) +{ + struct pthread_thread *thread = __get_pthread(); + + // call cleanup handlers + while (true) { + struct __pthread_cleanup_handler *handler = __pthread_cleanup_pop_handler(); + if (handler == NULL) + break; + + handler->function(handler->argument); + } + + __pthread_key_call_destructors(); + free(thread); +} + static int32 -_pthread_thread_entry(void *entry_data) +pthread_thread_entry(void *_thread) { - struct pthread_data *pdata = (struct pthread_data *)entry_data; - void *(*entry)(void*) = (void *(*)(void*))pdata->entry; - void *data = pdata->data; + struct pthread_thread *thread = (struct pthread_thread *)_thread; - free(pdata); - on_exit_thread(_pthread_key_call_destructors, NULL); - - pthread_exit(entry(data)); + // store thread data in TLS + *tls_address(sPthreadSlot) = thread; + + on_exit_thread(pthread_destroy_thread, NULL); + + pthread_exit(thread->entry(thread->entry_argument)); return B_OK; } -int +// #pragma mark - public API + + +int pthread_create(pthread_t *_thread, const pthread_attr_t *_attr, - void *(*start_routine)(void*), void *arg) + void *(*startRoutine)(void*), void *arg) { - thread_id thread; const pthread_attr *attr = NULL; - struct pthread_data *pdata; + struct pthread_thread *thread; + thread_id threadID; if (_thread == NULL) return B_BAD_VALUE; @@ -48,39 +80,47 @@ pthread_create(pthread_t *_thread, const pthread_attr_t *_attr, attr = &pthread_attr_default; else attr = *_attr; - - pdata = malloc(sizeof(struct pthread_data)); - if (!pdata) - return B_WOULD_BLOCK; - pdata->entry = (thread_entry)start_routine; - pdata->data = arg; - thread = spawn_thread(_pthread_thread_entry, "pthread func", attr->sched_priority, pdata); - if (thread < B_OK) + thread = (struct pthread_thread *)malloc(sizeof(struct pthread_thread)); + if (thread == NULL) + return B_WOULD_BLOCK; + + thread->entry = startRoutine; + thread->entry_argument = arg; + thread->cleanup_handlers = NULL; + + if (sPthreadSlot == -1) { + // In a clean pthread environment, this is even thread-safe! + sPthreadSlot = tls_allocate(); + } + + threadID = spawn_thread(pthread_thread_entry, "pthread func", + attr->sched_priority, thread); + if (threadID < B_OK) return B_WOULD_BLOCK; // stupid error code (EAGAIN) but demanded by POSIX - resume_thread(thread); - *_thread = thread; + resume_thread(threadID); + *_thread = threadID; return B_OK; } pthread_t -pthread_self() +pthread_self(void) { return find_thread(NULL); } -int +int pthread_equal(pthread_t t1, pthread_t t2) { - return (t1 > 0 && t2 > 0 && t1 == t2); + return t1 > 0 && t2 > 0 && t1 == t2; } -int +int pthread_join(pthread_t thread, void **value_ptr) { status_t error = wait_for_thread(thread, (status_t *)value_ptr); @@ -90,14 +130,14 @@ pthread_join(pthread_t thread, void **value_ptr) } -void -pthread_exit(void *value_ptr) +void +pthread_exit(void *value) { - exit_thread((status_t)value_ptr); + exit_thread((status_t)value); } -int +int pthread_kill(pthread_t thread, int sig) { status_t err = kill(thread, sig); @@ -107,7 +147,7 @@ pthread_kill(pthread_t thread, int sig) } -int +int pthread_detach(pthread_t thread) { return B_NOT_ALLOWED; diff --git a/src/system/libroot/posix/pthread/pthread_cleanup.cpp b/src/system/libroot/posix/pthread/pthread_cleanup.cpp new file mode 100644 index 0000000000..eae1f4d9d7 --- /dev/null +++ b/src/system/libroot/posix/pthread/pthread_cleanup.cpp @@ -0,0 +1,34 @@ +/* + * Copyright 2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include "pthread_private.h" + + +void +__pthread_cleanup_push_handler(__pthread_cleanup_handler* handler) +{ + pthread_thread* thread = __get_pthread(); + if (thread == NULL) + return; + + handler->previous = thread->cleanup_handlers; + thread->cleanup_handlers = handler; +} + + +__pthread_cleanup_handler* +__pthread_cleanup_pop_handler(void) +{ + pthread_thread* thread = __get_pthread(); + if (thread == NULL) + return NULL; + + __pthread_cleanup_handler* handler = thread->cleanup_handlers; + thread->cleanup_handlers = handler->previous; + + return handler; +} + diff --git a/src/system/libroot/posix/pthread/pthread_key.c b/src/system/libroot/posix/pthread/pthread_key.c index 7cacc4fb53..d30a35cc38 100644 --- a/src/system/libroot/posix/pthread/pthread_key.c +++ b/src/system/libroot/posix/pthread/pthread_key.c @@ -1,14 +1,17 @@ -/* -** Copyright 2006, Jérôme Duval. All rights reserved. -** Distributed under the terms of the MIT License. -*/ +/* + * Copyright 2006, Jérôme Duval. All rights reserved. + * Distributed under the terms of the MIT License. + */ -#include -#include -#include #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; @@ -20,7 +23,7 @@ static struct key_link *sKeyList = NULL; void -_pthread_key_call_destructors(void *param) +__pthread_key_call_destructors() { struct key_link *link = NULL; @@ -36,7 +39,7 @@ _pthread_key_call_destructors(void *param) } -int +int pthread_key_create(pthread_key_t *key, void (*destructor)(void*)) { struct key_link *link; @@ -63,18 +66,18 @@ pthread_key_create(pthread_key_t *key, void (*destructor)(void*)) } -int +int pthread_key_delete(pthread_key_t key) { struct key_link *link, *last = NULL; pthread_mutex_lock(&sKeyLock); - - //remove key/destructor from the list + + // remove key/destructor from the list for (link = sKeyList; link; link = link->next) { if (link->key == key) { if (last) last->next = link->next; - else + else sKeyList = link->next; free(link); pthread_mutex_unlock(&sKeyLock); @@ -95,7 +98,7 @@ pthread_getspecific(pthread_key_t key) } -int +int pthread_setspecific(pthread_key_t key, const void *value) { // we don't check if the key is valid diff --git a/src/system/libroot/posix/pthread/pthread_private.h b/src/system/libroot/posix/pthread/pthread_private.h index 311bdadc4e..92f25c132f 100644 --- a/src/system/libroot/posix/pthread/pthread_private.h +++ b/src/system/libroot/posix/pthread/pthread_private.h @@ -1,10 +1,13 @@ -#ifndef _PTHREAD_PRIVATE_H_ -#define _PTHREAD_PRIVATE_H_ -/* - * Copyright 2003, Axel Dörfler, axeld@pinc-software.de. +/* + * Copyright 2003-2008, Axel Dörfler, axeld@pinc-software.de. * Copyright 2007, Ryan Leavengood, leavengood@gmail.com. * All rights reserved. Distributed under the terms of the MIT License. */ +#ifndef _PTHREAD_PRIVATE_H_ +#define _PTHREAD_PRIVATE_H_ + + +#include #include @@ -44,6 +47,24 @@ typedef struct _pthread_attr { } pthread_attr; -void _pthread_key_call_destructors(void *); +// This structure is used internally only, it has no public equivalent +struct pthread_thread { + void *(*entry)(void*); + void *entry_argument; + struct __pthread_cleanup_handler *cleanup_handlers; + // TODO: move pthread keys in here, too +}; + + +#ifdef __cplusplus +extern "C" { +#endif + +void __pthread_key_call_destructors(void); +struct pthread_thread *__get_pthread(void); + +#ifdef __cplusplus +} +#endif #endif /* _PTHREAD_PRIVATE_H_ */