summaryrefslogtreecommitdiff
path: root/pthread
diff options
context:
space:
mode:
Diffstat (limited to 'pthread')
-rw-r--r--pthread/pt-alloc.c22
-rw-r--r--pthread/pt-create.c69
-rw-r--r--pthread/pt-dealloc.c5
-rw-r--r--pthread/pt-detach.c24
-rw-r--r--pthread/pt-exit.c35
-rw-r--r--pthread/pt-internal.h36
-rw-r--r--pthread/pt-join.c18
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;