diff options
Diffstat (limited to 'pthread')
-rw-r--r-- | pthread/pt-alloc.c | 22 | ||||
-rw-r--r-- | pthread/pt-create.c | 69 | ||||
-rw-r--r-- | pthread/pt-dealloc.c | 5 | ||||
-rw-r--r-- | pthread/pt-detach.c | 24 | ||||
-rw-r--r-- | pthread/pt-exit.c | 35 | ||||
-rw-r--r-- | pthread/pt-internal.h | 36 | ||||
-rw-r--r-- | pthread/pt-join.c | 18 |
7 files changed, 85 insertions, 124 deletions
diff --git a/pthread/pt-alloc.c b/pthread/pt-alloc.c index 604d376..af544c5 100644 --- a/pthread/pt-alloc.c +++ b/pthread/pt-alloc.c @@ -47,7 +47,7 @@ struct __pthread *__pthread_free_threads; pthread_mutex_t __pthread_free_threads_lock; static inline error_t -initialize_pthread (struct __pthread *new, int recycling) +initialize_pthread (struct __pthread *new) { error_t err; @@ -55,6 +55,7 @@ initialize_pthread (struct __pthread *new, int recycling) if (err) return err; + new->nr_refs = 1; new->cancel_lock = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER; new->cancel_hook = NULL; new->cancel_hook_arg = NULL; @@ -62,14 +63,6 @@ initialize_pthread (struct __pthread *new, int recycling) new->cancel_type = PTHREAD_CANCEL_DEFERRED; new->cancel_pending = 0; - if (recycling) - /* Since we are recycling PTHREAD, we can assume certains things - about PTHREAD's current state and save some cycles by not - rewriting the memory. */ - return 0; - - new->stack = 0; - new->state_lock = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER; new->state_cond = (pthread_cond_t) PTHREAD_COND_INITIALIZER; @@ -117,22 +110,15 @@ __pthread_alloc (struct __pthread **pthread) if (new) { - /* The thread may still be running. Make sure it is stopped. - If this is the case, then the thread is either at the end of - __pthread_dealloc or in __pthread_thread_halt. In both - cases, we are interrupt it. */ - __pthread_thread_halt (new); - #ifdef ENABLE_TLS if (new->tcb) { /* Drop old values */ _dl_deallocate_tls (new->tcb, 1); - new->tcb = NULL; } #endif /* ENABLE_TLS */ - err = initialize_pthread (new, 1); + err = initialize_pthread (new); if (! err) *pthread = new; return err; @@ -143,7 +129,7 @@ __pthread_alloc (struct __pthread **pthread) if (new == NULL) return ENOMEM; - err = initialize_pthread (new, 0); + err = initialize_pthread (new); if (err) { free (new); diff --git a/pthread/pt-create.c b/pthread/pt-create.c index fd6800f..99d1b47 100644 --- a/pthread/pt-create.c +++ b/pthread/pt-create.c @@ -103,47 +103,30 @@ __pthread_create_internal (struct __pthread **thread, pthread->state = (setup->detachstate == PTHREAD_CREATE_DETACHED ? PTHREAD_DETACHED : PTHREAD_JOINABLE); - /* If the user supplied a stack, it is not our responsibility to - setup a stack guard. */ if (setup->stackaddr) - pthread->guardsize = 0; - else - pthread->guardsize = (setup->guardsize <= setup->stacksize - ? setup->guardsize : setup->stacksize); - - /* Find a stack. There are several scenarios: if a detached thread - kills itself, it has no way to deallocate its stack, thus it - leaves PTHREAD->stack set to true. We try to reuse it here, - however, if the user supplied a stack or changes the size, - we cannot use the old one. Right now, we simply deallocate it. */ - if (pthread->stack) { - if ((setup->stackaddr && setup->stackaddr != pthread->stackaddr) - || (setup->stacksize != pthread->stacksize)) - { - __pthread_stack_dealloc (pthread->stackaddr, - pthread->stacksize); - pthread->stackaddr = setup->stackaddr; - pthread->stacksize = setup->stacksize; - } + pthread->stackaddr = setup->stackaddr; + + /* If the user supplied a stack, it is not our responsibility to + setup a stack guard. */ + pthread->guardsize = 0; + pthread->stack = 0; } else { - pthread->stacksize = setup->stacksize; - - if (setup->stackaddr) - pthread->stackaddr = setup->stackaddr; - else - { - err = __pthread_stack_alloc (&pthread->stackaddr, - setup->stacksize); - if (err) - goto failed_stack_alloc; - - pthread->stack = 1; - } + /* Allocate a stack. */ + err = __pthread_stack_alloc (&pthread->stackaddr, + setup->stacksize); + if (err) + goto failed_stack_alloc; + + pthread->guardsize = (setup->guardsize <= setup->stacksize + ? setup->guardsize : setup->stacksize); + pthread->stack = 1; } + pthread->stacksize = setup->stacksize; + /* Allocate the kernel thread and other required resources. */ err = __pthread_thread_alloc (pthread); if (err) @@ -169,6 +152,10 @@ __pthread_create_internal (struct __pthread **thread, if (err) goto failed_sigstate; + /* If the new thread is joinable, add a reference for the caller. */ + if (pthread->state == PTHREAD_JOINABLE) + pthread->nr_refs++; + /* Set the new thread's signal mask and set the pending signals to empty. POSIX says: "The signal mask shall be inherited from the creating thread. The set of signals pending for the new thread @@ -216,6 +203,10 @@ __pthread_create_internal (struct __pthread **thread, return 0; failed_starting: + /* If joinable, a reference was added for the caller. */ + if (pthread->state == PTHREAD_JOINABLE) + __pthread_dealloc (pthread); + __pthread_setid (pthread->thread, NULL); __atomic_dec (&__pthread_total); failed_sigstate: @@ -227,10 +218,14 @@ __pthread_create_internal (struct __pthread **thread, failed_thread_tls_alloc: #endif /* ENABLE_TLS */ __pthread_thread_dealloc (pthread); - __pthread_thread_halt (pthread); + __pthread_thread_terminate (pthread); + + /* __pthread_thread_terminate has taken care of deallocating the stack and + the thread structure. */ + goto failed; failed_thread_alloc: - __pthread_stack_dealloc (pthread->stackaddr, pthread->stacksize); - pthread->stack = 0; + if (pthread->stack) + __pthread_stack_dealloc (pthread->stackaddr, pthread->stacksize); failed_stack_alloc: __pthread_dealloc (pthread); failed: diff --git a/pthread/pt-dealloc.c b/pthread/pt-dealloc.c index 92fe1fd..e324800 100644 --- a/pthread/pt-dealloc.c +++ b/pthread/pt-dealloc.c @@ -23,6 +23,8 @@ #include <pt-internal.h> +#include <bits/pt-atomic.h> + /* List of thread structures corresponding to free thread IDs. */ extern struct __pthread *__pthread_free_threads; extern pthread_mutex_t __pthread_free_threads_lock; @@ -34,6 +36,9 @@ __pthread_dealloc (struct __pthread *pthread) { assert (pthread->state != PTHREAD_TERMINATED); + if (! __atomic_dec_and_test (&pthread->nr_refs)) + return; + /* Withdraw this thread from the thread ID lookup table. */ __pthread_setid (pthread->thread, NULL); diff --git a/pthread/pt-detach.c b/pthread/pt-detach.c index 4ed8d2c..3431f1b 100644 --- a/pthread/pt-detach.c +++ b/pthread/pt-detach.c @@ -50,30 +50,16 @@ pthread_detach (pthread_t thread) consequences instead of blocking indefinitely. */ pthread_cond_broadcast (&pthread->state_cond); __pthread_mutex_unlock (&pthread->state_lock); + + __pthread_dealloc (pthread); break; case PTHREAD_EXITED: - /* THREAD has already exited. Make sure that nobody can - reference it anymore, and mark it as terminated. */ - __pthread_mutex_unlock (&pthread->state_lock); - /* Make sure the thread is not running before we remove its - stack. (The only possibility is that it is in a call to - __pthread_thread_halt itself, but that is enough to cause a - sigsegv.) */ - __pthread_thread_halt (pthread); - - /* Destroy the stack, the kernel resources and the control - block. */ - if (pthread->stack) - { - __pthread_stack_dealloc (pthread->stackaddr, pthread->stacksize); - pthread->stack = 0; - } - - __pthread_thread_dealloc (pthread); - + /* THREAD has already exited. PTHREAD remained after the thread + exited in order to provide the exit status, but it turns out + it won't be needed. */ __pthread_dealloc (pthread); break; diff --git a/pthread/pt-exit.c b/pthread/pt-exit.c index 8468b80..ea61732 100644 --- a/pthread/pt-exit.c +++ b/pthread/pt-exit.c @@ -48,12 +48,6 @@ __pthread_exit (void *status) pthread_setcancelstate (oldstate, &oldstate); - /* Destory any thread specific data. */ - __pthread_destroy_specific (self); - - /* Destroy any signal state. */ - __pthread_sigstate_destroy (self); - /* Decrease the number of threads. We use an atomic operation to make sure that only the last thread calls `exit'. */ if (__atomic_dec_and_test (&__pthread_total)) @@ -77,15 +71,8 @@ __pthread_exit (void *status) break; case PTHREAD_DETACHED: - /* Make sure that nobody can reference this thread anymore, and - mark it as terminated. Our thread ID will immediately become - available for re-use. For obvious reasons, we cannot - deallocate our own stack and TLS. However, it will eventually be - reused when this thread structure is recycled. */ __pthread_mutex_unlock (&self->state_lock); - __pthread_dealloc (self); - break; case PTHREAD_JOINABLE: @@ -105,12 +92,22 @@ __pthread_exit (void *status) break; } - /* Note that after this point the resources used by this thread can - be freed at any moment if another thread joins or detaches us. - This means that before freeing any resources, such a thread - should make sure that this thread is really halted. */ - - __pthread_thread_halt (self); + /* Destroy any thread specific data. */ + __pthread_destroy_specific (self); + + /* Destroy any signal state. */ + __pthread_sigstate_destroy (self); + + /* Kernel resources may be used to implement synchronization objects, + release them late. */ + __pthread_thread_dealloc (self); + + /* Self terminating requires TLS, so defer the release of the TCB until + the thread structure is reused. */ + + /* Terminate the kernel thread, release the stack and drop the + self reference. */ + __pthread_thread_terminate (self); /* NOTREACHED */ abort (); diff --git a/pthread/pt-internal.h b/pthread/pt-internal.h index aeac009..9e0bbe9 100644 --- a/pthread/pt-internal.h +++ b/pthread/pt-internal.h @@ -72,6 +72,12 @@ struct __pthread /* Thread ID. */ pthread_t thread; + __atomic_t nr_refs; /* Detached threads have a self reference only, + while joinable threads have two references. + These are used to keep the structure valid at + thread destruction. Detaching/joining a thread + drops a reference. */ + /* Cancellation. */ pthread_mutex_t cancel_lock; /* Protect cancel_xxx members. */ void (*cancel_hook)(void *); /* Called to unblock a thread blocking @@ -208,12 +214,13 @@ extern int __pthread_create_internal (struct __pthread **__restrict pthread, void *__restrict arg); /* Allocate a new thread structure and a pthread thread ID (but not a - kernel thread or a stack). */ + kernel thread or a stack). THREAD has one reference. */ extern int __pthread_alloc (struct __pthread **thread); /* Deallocate the thread structure. This is the dual of - __pthread_alloc (N.B. it does not call __pthread_stack_alloc nor - __pthread_thread_halt). */ + __pthread_alloc (N.B. it does not call __pthread_stack_dealloc nor + __pthread_thread_terminate). THREAD loses one reference and is + released if the reference counter drops to 0. */ extern void __pthread_dealloc (struct __pthread *thread); @@ -238,22 +245,23 @@ extern int __pthread_setup (struct __pthread *__restrict thread, resources) for THREAD; it must not be placed on the run queue. */ extern int __pthread_thread_alloc (struct __pthread *thread); -/* Deallocate any kernel resources associated with THREAD. The thread - must not be running (that is, if __pthread_thread_start was called, - __pthread_thread_halt must first be called). This function will - never be called by a thread on itself. In the case that a thread - exits, its thread structure will be cached and cleaned up - later. */ +/* Deallocate any kernel resources associated with THREAD. */ extern void __pthread_thread_dealloc (struct __pthread *thread); /* Start THREAD making it eligible to run. */ extern int __pthread_thread_start (struct __pthread *thread); -/* Stop the kernel thread associated with THREAD. This function may - be called by two threads in parallel. In particular, by the thread - itself and another thread trying to join it. This function must be - implemented such that this is safe. */ -extern void __pthread_thread_halt (struct __pthread *thread); +/* Terminate the kernel thread associated with THREAD, and deallocate its + stack. In addition, THREAD loses one reference. + + This function can be called by any thread, including the target thread. + Since some resources that are destroyed along the kernel thread are + stored in thread-local variables, the conditions required for this + function to behave correctly are a bit unusual : as long as the target + thread hasn't been started, any thread can terminate it, but once it + has started, no other thread can terminate it, so that thread-local + variables created by that thread are correctly released. */ +extern void __pthread_thread_terminate (struct __pthread *thread); /* Called by a thread just before it calls the provided start diff --git a/pthread/pt-join.c b/pthread/pt-join.c index 417f433..122d130 100644 --- a/pthread/pt-join.c +++ b/pthread/pt-join.c @@ -50,27 +50,11 @@ pthread_join (pthread_t thread, void **status) switch (pthread->state) { case PTHREAD_EXITED: - __pthread_mutex_unlock (&pthread->state_lock); - /* THREAD has already exited. Salvage its exit status. */ if (status) *status = pthread->status; - /* Make sure the thread is not running before we remove its - stack. (The only possibility is that it is in a call to - __pthread_thread_halt itself, but that is enough to cause a - sigsegv.) */ - __pthread_thread_halt (pthread); - - /* Destroy the stack, the kernel resources and the control - block. */ - if (pthread->stack) - { - __pthread_stack_dealloc (pthread->stackaddr, pthread->stacksize); - pthread->stack = 0; - } - - __pthread_thread_dealloc (pthread); + __pthread_mutex_unlock (&pthread->state_lock); __pthread_dealloc (pthread); break; |