diff options
author | Federico G. Schwindt <fgsch@cvs.openbsd.org> | 2001-12-11 00:19:48 +0000 |
---|---|---|
committer | Federico G. Schwindt <fgsch@cvs.openbsd.org> | 2001-12-11 00:19:48 +0000 |
commit | df4b52670da644e7bd952242b6d1e8f6fcd0c5b2 (patch) | |
tree | 362f2780fa4da1e285ed537b8a08e2eccf119a0b | |
parent | d9bad47758857d3344bedc12dff936f73958442f (diff) |
More sync with freebsd code; join related code this time.
-rw-r--r-- | lib/libc_r/uthread/pthread_private.h | 22 | ||||
-rw-r--r-- | lib/libc_r/uthread/uthread_cancel.c | 22 | ||||
-rw-r--r-- | lib/libc_r/uthread/uthread_create.c | 6 | ||||
-rw-r--r-- | lib/libc_r/uthread/uthread_detach.c | 22 | ||||
-rw-r--r-- | lib/libc_r/uthread/uthread_exit.c | 91 | ||||
-rw-r--r-- | lib/libc_r/uthread/uthread_find_thread.c | 34 | ||||
-rw-r--r-- | lib/libc_r/uthread/uthread_gc.c | 23 | ||||
-rw-r--r-- | lib/libc_r/uthread/uthread_info_openbsd.c | 5 | ||||
-rw-r--r-- | lib/libc_r/uthread/uthread_init.c | 6 | ||||
-rw-r--r-- | lib/libc_r/uthread/uthread_join.c | 131 | ||||
-rw-r--r-- | lib/libpthread/uthread/pthread_private.h | 22 | ||||
-rw-r--r-- | lib/libpthread/uthread/uthread_cancel.c | 22 | ||||
-rw-r--r-- | lib/libpthread/uthread/uthread_create.c | 6 | ||||
-rw-r--r-- | lib/libpthread/uthread/uthread_detach.c | 22 | ||||
-rw-r--r-- | lib/libpthread/uthread/uthread_exit.c | 91 | ||||
-rw-r--r-- | lib/libpthread/uthread/uthread_find_thread.c | 34 | ||||
-rw-r--r-- | lib/libpthread/uthread/uthread_gc.c | 23 | ||||
-rw-r--r-- | lib/libpthread/uthread/uthread_info_openbsd.c | 5 | ||||
-rw-r--r-- | lib/libpthread/uthread/uthread_init.c | 6 | ||||
-rw-r--r-- | lib/libpthread/uthread/uthread_join.c | 131 |
20 files changed, 432 insertions, 292 deletions
diff --git a/lib/libc_r/uthread/pthread_private.h b/lib/libc_r/uthread/pthread_private.h index a07ebaf431d..b5556c44ffe 100644 --- a/lib/libc_r/uthread/pthread_private.h +++ b/lib/libc_r/uthread/pthread_private.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pthread_private.h,v 1.30 2001/12/08 14:51:36 fgsch Exp $ */ +/* $OpenBSD: pthread_private.h,v 1.31 2001/12/11 00:19:47 fgsch Exp $ */ /* * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au>. * All rights reserved. @@ -538,6 +538,12 @@ typedef void (*thread_continuation_t) (void *); typedef V_TAILQ_ENTRY(pthread) pthread_entry_t; +struct join_status { + struct pthread *thread; + void *ret; + int error; +}; + /* * Thread structure. */ @@ -638,8 +644,12 @@ struct pthread { */ int error; - /* Join queue head and link for waiting threads: */ - V_TAILQ_HEAD(join_head, pthread) join_queue; + /* + * The joiner is the thread that is joining this thraed. The + * join status keeps track of a join operation to another thread. + */ + struct pthread *joiner; + struct join_status join_status; /* * The current thread can belong to only one scheduling queue at @@ -648,8 +658,6 @@ struct pthread { * * o A queue of threads waiting for a mutex * o A queue of threads waiting for a condition variable - * o A queue of threads waiting for another thread to terminate - * (the join queue above) * o A queue of threads waiting for a file descriptor lock * o A queue of threads needing work done by the kernel thread * (waiting for a spinlock or file I/O) @@ -661,6 +669,9 @@ struct pthread { /* Priority queue entry for this thread: */ pthread_entry_t pqe; + /* Priority queue entry for this thread: */ + pthread_entry_t sqe; + /* Queue entry for this thread: */ pthread_entry_t qe; @@ -1018,7 +1029,6 @@ SCLASS int _thread_kern_new_state * Function prototype definitions. */ __BEGIN_DECLS -int _find_dead_thread(pthread_t); int _find_thread(pthread_t); struct pthread *_get_curthread(void); void _set_curthread(struct pthread *); diff --git a/lib/libc_r/uthread/uthread_cancel.c b/lib/libc_r/uthread/uthread_cancel.c index b536acc4b8e..8c8515dbbc7 100644 --- a/lib/libc_r/uthread/uthread_cancel.c +++ b/lib/libc_r/uthread/uthread_cancel.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uthread_cancel.c,v 1.7 2001/12/08 14:51:36 fgsch Exp $ */ +/* $OpenBSD: uthread_cancel.c,v 1.8 2001/12/11 00:19:47 fgsch Exp $ */ /* * David Leonard <d@openbsd.org>, 1999. Public domain. */ @@ -6,7 +6,6 @@ #include <pthread.h> #include "pthread_private.h" - static void finish_cancellation(void *arg); int @@ -44,6 +43,7 @@ pthread_cancel(pthread) case PS_FDW_WAIT: case PS_POLL_WAIT: case PS_SELECT_WAIT: + /* Remove these threads from the work queue: */ if ((pthread->flags & PTHREAD_FLAGS_IN_WORKQ) != 0) PTHREAD_WORKQ_REMOVE(pthread); @@ -71,6 +71,19 @@ pthread_cancel(pthread) break; case PS_JOIN: + /* + * Disconnect the thread from the joinee and + * detach: + */ + if (pthread->data.thread != NULL) { + pthread->data.thread->joiner = NULL; + pthread_detach((pthread_t) + pthread->data.thread); + } + pthread->cancelflags |= PTHREAD_CANCELLING; + PTHREAD_NEW_STATE(pthread, PS_RUNNING); + break; + case PS_SUSPENDED: /* Simply wake: */ /* XXX may be incorrect */ @@ -165,6 +178,11 @@ pthread_testcancel() if (((curthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) && ((curthread->cancelflags & PTHREAD_CANCELLING) != 0)) { + /* + * It is possible for this thread to be swapped out + * while performing cancellation; do not allow it + * to be cancelled again. + */ curthread->cancelflags &= ~PTHREAD_CANCELLING; #ifdef notyet _thread_exit_cleanup(); diff --git a/lib/libc_r/uthread/uthread_create.c b/lib/libc_r/uthread/uthread_create.c index 5b1897b7ef4..de96d4440de 100644 --- a/lib/libc_r/uthread/uthread_create.c +++ b/lib/libc_r/uthread/uthread_create.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uthread_create.c,v 1.16 2001/12/08 14:51:36 fgsch Exp $ */ +/* $OpenBSD: uthread_create.c,v 1.17 2001/12/11 00:19:47 fgsch Exp $ */ /* * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> * All rights reserved. @@ -140,8 +140,8 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr, new_thread->active_priority = new_thread->base_priority; new_thread->inherited_priority = 0; - /* Initialise the join queue for the new thread: */ - TAILQ_INIT(&(new_thread->join_queue)); + /* Initialize joiner to NULL (no joiner): */ + new_thread->joiner = NULL; /* Initialize the mutex queue: */ TAILQ_INIT(&new_thread->mutexq); diff --git a/lib/libc_r/uthread/uthread_detach.c b/lib/libc_r/uthread/uthread_detach.c index d6accdc13cf..786df9d165c 100644 --- a/lib/libc_r/uthread/uthread_detach.c +++ b/lib/libc_r/uthread/uthread_detach.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uthread_detach.c,v 1.6 2000/01/06 07:15:05 d Exp $ */ +/* $OpenBSD: uthread_detach.c,v 1.7 2001/12/11 00:19:47 fgsch Exp $ */ /* * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. * All rights reserved. @@ -41,7 +41,6 @@ int pthread_detach(pthread_t pthread) { int rval = 0; - pthread_t next_thread; /* Check for invalid calling parameters: */ if (pthread == NULL || pthread->magic != PTHREAD_MAGIC) @@ -59,13 +58,20 @@ pthread_detach(pthread_t pthread) */ _thread_kern_sig_defer(); - /* Enter a loop to bring all threads off the join queue: */ - while ((next_thread = TAILQ_FIRST(&pthread->join_queue)) != NULL) { - /* Remove the thread from the queue: */ - TAILQ_REMOVE(&pthread->join_queue, next_thread, qe); + /* Check if there is a joiner: */ + if (pthread->joiner != NULL) { + struct pthread *joiner = pthread->joiner; - /* Make the thread run: */ - PTHREAD_NEW_STATE(next_thread,PS_RUNNING); + /* Make the thread runnable: */ + PTHREAD_NEW_STATE(joiner, PS_RUNNING); + + /* Set the return value for the woken thread: */ + joiner->error = ESRCH; + + /* + * Disconnect the joiner from the thread being detached: + */ + pthread->joiner = NULL; } /* diff --git a/lib/libc_r/uthread/uthread_exit.c b/lib/libc_r/uthread/uthread_exit.c index c6013cbf1ea..232b55e008e 100644 --- a/lib/libc_r/uthread/uthread_exit.c +++ b/lib/libc_r/uthread/uthread_exit.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uthread_exit.c,v 1.13 2001/08/21 19:24:53 fgsch Exp $ */ +/* $OpenBSD: uthread_exit.c,v 1.14 2001/12/11 00:19:47 fgsch Exp $ */ /* * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> * All rights reserved. @@ -146,7 +146,7 @@ void pthread_exit(void *status) { struct pthread *curthread = _get_curthread(); - pthread_t pthread; + pthread_t pthread; /* Check if this thread is already in the process of exiting: */ if ((curthread->flags & PTHREAD_EXITING) != 0) { @@ -162,11 +162,9 @@ pthread_exit(void *status) while (curthread->cleanup != NULL) { pthread_cleanup_pop(1); } - if (curthread->attr.cleanup_attr != NULL) { curthread->attr.cleanup_attr(curthread->attr.arg_attr); } - /* Check if there is thread specific data: */ if (curthread->specific_data != NULL) { /* Run the thread-specific data destructors: */ @@ -174,26 +172,6 @@ pthread_exit(void *status) } /* - * Defer signals to protect the scheduling queues from access - * by the signal handler: - */ - _thread_kern_sig_defer(); - - /* Check if there are any threads joined to this one: */ - while ((pthread = TAILQ_FIRST(&(curthread->join_queue))) != NULL) { - /* Remove the thread from the queue: */ - TAILQ_REMOVE(&curthread->join_queue, pthread, qe); - - /* Wake the joined thread and let it detach this thread: */ - PTHREAD_NEW_STATE(pthread,PS_RUNNING); - } - - /* - * Undefer and handle pending signals, yielding if necessary: - */ - _thread_kern_sig_undefer(); - - /* * Lock the garbage collector mutex to ensure that the garbage * collector is not using the dead thread list. */ @@ -204,20 +182,6 @@ pthread_exit(void *status) TAILQ_INSERT_HEAD(&_dead_list, curthread, dle); /* - * Defer signals to protect the scheduling queues from access - * by the signal handler: - */ - _thread_kern_sig_defer(); - - /* Remove this thread from the thread list: */ - TAILQ_REMOVE(&_thread_list, curthread, tle); - - /* - * Undefer and handle pending signals, yielding if necessary: - */ - _thread_kern_sig_undefer(); - - /* * Signal the garbage collector thread that there is something * to clean up. */ @@ -225,17 +189,56 @@ pthread_exit(void *status) PANIC("Cannot signal gc cond"); /* - * Mark the thread as dead so it will not return if it - * gets context switched out when the mutex is unlocked. + * Avoid a race condition where a scheduling signal can occur + * causing the garbage collector thread to run. If this happens, + * the current thread can be cleaned out from under us. */ - PTHREAD_SET_STATE(curthread, PS_DEAD); + _thread_kern_sig_defer(); /* Unlock the garbage collector mutex: */ if (pthread_mutex_unlock(&_gc_mutex) != 0) - PANIC("Cannot lock gc mutex"); + PANIC("Cannot unlock gc mutex"); + + /* Check if there is a thread joining this one: */ + if (curthread->joiner != NULL) { + pthread = curthread->joiner; + curthread->joiner = NULL; + + switch (pthread->suspended) { + case SUSP_JOIN: + /* + * The joining thread is suspended. Change the + * suspension state to make the thread runnable when it + * is resumed: + */ + pthread->suspended = SUSP_NO; + break; + case SUSP_NO: + /* Make the joining thread runnable: */ + PTHREAD_NEW_STATE(pthread, PS_RUNNING); + break; + default: + PANIC("Unreachable code reached"); + } + + /* Set the return value for the joining thread: */ + pthread->join_status.ret = curthread->ret; + pthread->join_status.error = 0; + pthread->join_status.thread = NULL; + +#ifdef notyet + /* Make this thread collectable by the garbage collector. */ + PTHREAD_ASSERT(((curthread->attr.flags & PTHREAD_DETACHED) == + 0), "Cannot join a detached thread"); +#endif + curthread->attr.flags |= PTHREAD_DETACHED; + } + + /* Remove this thread from the thread list: */ + TAILQ_REMOVE(&_thread_list, curthread, tle); - /* This this thread will never be re-scheduled. */ - _thread_kern_sched(NULL); + /* This thread will never be re-scheduled. */ + _thread_kern_sched_state(PS_DEAD, __FILE__, __LINE__); /* This point should not be reached. */ PANIC("Dead thread has resumed"); diff --git a/lib/libc_r/uthread/uthread_find_thread.c b/lib/libc_r/uthread/uthread_find_thread.c index 42b561524b9..c531643e325 100644 --- a/lib/libc_r/uthread/uthread_find_thread.c +++ b/lib/libc_r/uthread/uthread_find_thread.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uthread_find_thread.c,v 1.4 1999/11/25 07:01:35 d Exp $ */ +/* $OpenBSD: uthread_find_thread.c,v 1.5 2001/12/11 00:19:47 fgsch Exp $ */ /* * Copyright (c) 1998 John Birrell <jb@cimlogic.com.au>. * All rights reserved. @@ -66,36 +66,4 @@ _find_thread(pthread_t pthread) /* Return zero if the thread exists: */ return ((pthread1 != NULL) ? 0:ESRCH); } - -/* Find a thread in the linked list of dead threads: */ -int -_find_dead_thread(pthread_t pthread) -{ - pthread_t pthread1; - - /* Check if the caller has specified an invalid thread: */ - if (pthread == NULL || pthread->magic != PTHREAD_MAGIC) - /* Invalid thread: */ - return(EINVAL); - - /* - * Lock the garbage collector mutex to ensure that the garbage - * collector is not using the dead thread list. - */ - if (pthread_mutex_lock(&_gc_mutex) != 0) - PANIC("Cannot lock gc mutex"); - - /* Search for the specified thread: */ - TAILQ_FOREACH(pthread1, &_dead_list, dle) { - if (pthread1 == pthread) - break; - } - - /* Unlock the garbage collector mutex: */ - if (pthread_mutex_unlock(&_gc_mutex) != 0) - PANIC("Cannot lock gc mutex"); - - /* Return zero if the thread exists: */ - return ((pthread1 != NULL) ? 0:ESRCH); -} #endif diff --git a/lib/libc_r/uthread/uthread_gc.c b/lib/libc_r/uthread/uthread_gc.c index 5f4e520d6aa..956633e7fe4 100644 --- a/lib/libc_r/uthread/uthread_gc.c +++ b/lib/libc_r/uthread/uthread_gc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uthread_gc.c,v 1.10 2001/08/21 19:24:53 fgsch Exp $ */ +/* $OpenBSD: uthread_gc.c,v 1.11 2001/12/11 00:19:47 fgsch Exp $ */ /* * Copyright (c) 1998 John Birrell <jb@cimlogic.com.au> * All rights reserved. @@ -59,8 +59,8 @@ _thread_gc(pthread_addr_t arg) void *p_stack; /* Block all signals */ - sigfillset (&mask); - sigprocmask (SIG_BLOCK, &mask, NULL); + sigfillset(&mask); + pthread_sigmask(SIG_BLOCK, &mask, NULL); /* Mark this thread as a library thread (not a user thread). */ curthread->flags |= PTHREAD_FLAGS_PRIVATE; @@ -98,6 +98,10 @@ _thread_gc(pthread_addr_t arg) */ _thread_kern_sig_undefer(); + /* No stack of thread structure to free yet: */ + p_stack = NULL; + pthread_cln = NULL; + /* * Lock the garbage collector mutex which ensures that * this thread sees another thread exit: @@ -105,10 +109,6 @@ _thread_gc(pthread_addr_t arg) if (pthread_mutex_lock(&_gc_mutex) != 0) PANIC("Cannot lock gc mutex"); - /* No stack of thread structure to free yet: */ - p_stack = NULL; - pthread_cln = NULL; - /* * Enter a loop to search for the first dead thread that * has memory to free. @@ -124,7 +124,7 @@ _thread_gc(pthread_addr_t arg) * Check if this thread has detached: */ else if ((pthread->attr.flags & - PTHREAD_DETACHED) != 0) { + PTHREAD_DETACHED) != 0) { /* Remove this thread from the dead list: */ TAILQ_REMOVE(&_dead_list, pthread, dle); @@ -193,13 +193,14 @@ _thread_gc(pthread_addr_t arg) _thread_stack_free(p_stack); if (pthread_cln != NULL) { + if (pthread_cln->name != NULL) { + /* Free the thread name string. */ + free(pthread_cln->name); + } /* * Free the memory allocated for the thread * structure. */ - if (pthread_cln->name != NULL) - free(pthread_cln->name); - if (pthread_cln->poll_data.fds != NULL) free(pthread_cln->poll_data.fds); diff --git a/lib/libc_r/uthread/uthread_info_openbsd.c b/lib/libc_r/uthread/uthread_info_openbsd.c index 439bf6a7655..a9f133550e9 100644 --- a/lib/libc_r/uthread/uthread_info_openbsd.c +++ b/lib/libc_r/uthread/uthread_info_openbsd.c @@ -1,3 +1,5 @@ +/* $OpenBSD: uthread_info_openbsd.c,v 1.5 2001/12/11 00:19:47 fgsch Exp $ */ + /* * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> * All rights reserved. @@ -29,7 +31,6 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $OpenBSD: uthread_info_openbsd.c,v 1.4 2001/12/08 14:51:36 fgsch Exp $ */ #include <stdio.h> #include <fcntl.h> @@ -220,6 +221,7 @@ _thread_dump_entry(pthread, fd, verbose) pthread->data.cond); _thread_sys_write(fd, s, strlen(s)); break; +#ifdef notyet case PS_JOIN: { struct pthread *t, * volatile *last; @@ -243,6 +245,7 @@ _thread_dump_entry(pthread, fd, verbose) _thread_sys_write(fd, s, strlen(s)); } break; +#endif case PS_SLEEP_WAIT: { struct timeval tv; diff --git a/lib/libc_r/uthread/uthread_init.c b/lib/libc_r/uthread/uthread_init.c index a0aaf8e8964..0c952b4130e 100644 --- a/lib/libc_r/uthread/uthread_init.c +++ b/lib/libc_r/uthread/uthread_init.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uthread_init.c,v 1.17 2001/12/08 14:51:36 fgsch Exp $ */ +/* $OpenBSD: uthread_init.c,v 1.18 2001/12/11 00:19:47 fgsch Exp $ */ /* * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> * All rights reserved. @@ -186,8 +186,8 @@ _thread_init(void) /* Initialise the state of the initial thread: */ _thread_initial->state = PS_RUNNING; - /* Initialise the queue: */ - TAILQ_INIT(&(_thread_initial->join_queue)); + /* Initialize joiner to NULL (no joiner): */ + _thread_initial->joiner = NULL; /* Initialize the owned mutex queue and count: */ TAILQ_INIT(&(_thread_initial->mutexq)); diff --git a/lib/libc_r/uthread/uthread_join.c b/lib/libc_r/uthread/uthread_join.c index 310e5521268..1cf1624a884 100644 --- a/lib/libc_r/uthread/uthread_join.c +++ b/lib/libc_r/uthread/uthread_join.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uthread_join.c,v 1.7 2001/08/21 19:24:53 fgsch Exp $ */ +/* $OpenBSD: uthread_join.c,v 1.8 2001/12/11 00:19:47 fgsch Exp $ */ /* * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. * All rights reserved. @@ -42,59 +42,120 @@ pthread_join(pthread_t pthread, void **thread_return) { struct pthread *curthread = _get_curthread(); int ret = 0; + pthread_t thread; - /* This is a cancellation point: */ _thread_enter_cancellation_point(); /* Check if the caller has specified an invalid thread: */ - if (pthread == NULL || pthread->magic != PTHREAD_MAGIC) + if (pthread == NULL || pthread->magic != PTHREAD_MAGIC) { /* Invalid thread: */ - ret = EINVAL; + _thread_leave_cancellation_point(); + return (EINVAL); + } /* Check if the caller has specified itself: */ - else if (pthread == curthread) + if (pthread == curthread) { /* Avoid a deadlock condition: */ - ret = EDEADLK; + _thread_leave_cancellation_point(); + return (EDEADLK); + } /* - * Find the thread in the list of active threads or in the - * list of dead threads: + * Lock the garbage collector mutex to ensure that the garbage + * collector is not using the dead thread list. */ - else if (_find_thread(pthread) != 0 && - _find_dead_thread(pthread) != 0) - /* Return an error: */ - ret = ESRCH; + if (pthread_mutex_lock(&_gc_mutex) != 0) + PANIC("Cannot lock gc mutex"); + + /* + * Defer signals to protect the thread list from access + * by the signal handler: + */ + _thread_kern_sig_defer(); + + /* + * Unlock the garbage collector mutex, now that the garbage collector + * can't be run: + */ + if (pthread_mutex_unlock(&_gc_mutex) != 0) + PANIC("Cannot unlock gc mutex"); + + /* + * Search for the specified thread in the list of active threads. This + * is done manually here rather than calling _find_thread() because + * the searches in _thread_list and _dead_list (as well as setting up + * join/detach state) have to be done atomically. + */ + TAILQ_FOREACH(thread, &_thread_list, tle) { + if (thread == pthread) + break; + } + if (thread == NULL) { + /* + * Search for the specified thread in the list of dead threads: + */ + TAILQ_FOREACH(thread, &_dead_list, dle) { + if (thread == pthread) + break; + } + } + + /* Check if the thread was not found or has been detached: */ + if (thread == NULL || + ((pthread->attr.flags & PTHREAD_DETACHED) != 0)) { + /* Undefer and handle pending signals, yielding if necessary: */ + _thread_kern_sig_undefer(); - /* Check if this thread has been detached: */ - else if ((pthread->attr.flags & PTHREAD_DETACHED) != 0) /* Return an error: */ ret = ESRCH; + } else if (pthread->joiner != NULL) { + /* Undefer and handle pending signals, yielding if necessary: */ + _thread_kern_sig_undefer(); + + /* Multiple joiners are not supported. */ + ret = EOPNOTSUPP; + /* Check if the thread is not dead: */ - else if (pthread->state != PS_DEAD) { - /* Add the running thread to the join queue: */ - TAILQ_INSERT_TAIL(&(pthread->join_queue), curthread, qe); - - /* Schedule the next thread: */ - _thread_kern_sched_state(PS_JOIN, __FILE__, __LINE__); - - /* Check if the thread is not detached: */ - if ((pthread->attr.flags & PTHREAD_DETACHED) == 0) { - /* Check if the return value is required: */ - if (thread_return) - /* Return the thread's return value: */ - *thread_return = pthread->ret; + } else if (pthread->state != PS_DEAD) { + /* Set the running thread to be the joiner: */ + pthread->joiner = curthread; + + /* Keep track of which thread we're joining to: */ + curthread->join_status.thread = pthread; + + while (curthread->join_status.thread == pthread) { + /* Schedule the next thread: */ + _thread_kern_sched_state(PS_JOIN, __FILE__, __LINE__); } - else - /* Return an error: */ - ret = ESRCH; - /* Check if the return value is required: */ - } else if (thread_return != NULL) - /* Return the thread's return value: */ - *thread_return = pthread->ret; + /* + * The thread return value and error are set by the thread we're + * joining to when it exits or detaches: + */ + ret = curthread->join_status.error; + ret = curthread->join_status.error; + if ((ret == 0) && (thread_return != NULL)) + *thread_return = curthread->join_status.ret; + } else { + /* + * The thread exited (is dead) without being detached, and no + * thread has joined it. + */ + + /* Check if the return value is required: */ + if (thread_return != NULL) { + /* Return the thread's return value: */ + *thread_return = pthread->ret; + } + + /* Make the thread collectable by the garbage collector. */ + pthread->attr.flags |= PTHREAD_DETACHED; + + /* Undefer and handle pending signals, yielding if necessary: */ + _thread_kern_sig_undefer(); + } - /* No longer in a cancellation point: */ _thread_leave_cancellation_point(); /* Return the completion status: */ diff --git a/lib/libpthread/uthread/pthread_private.h b/lib/libpthread/uthread/pthread_private.h index a07ebaf431d..b5556c44ffe 100644 --- a/lib/libpthread/uthread/pthread_private.h +++ b/lib/libpthread/uthread/pthread_private.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pthread_private.h,v 1.30 2001/12/08 14:51:36 fgsch Exp $ */ +/* $OpenBSD: pthread_private.h,v 1.31 2001/12/11 00:19:47 fgsch Exp $ */ /* * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au>. * All rights reserved. @@ -538,6 +538,12 @@ typedef void (*thread_continuation_t) (void *); typedef V_TAILQ_ENTRY(pthread) pthread_entry_t; +struct join_status { + struct pthread *thread; + void *ret; + int error; +}; + /* * Thread structure. */ @@ -638,8 +644,12 @@ struct pthread { */ int error; - /* Join queue head and link for waiting threads: */ - V_TAILQ_HEAD(join_head, pthread) join_queue; + /* + * The joiner is the thread that is joining this thraed. The + * join status keeps track of a join operation to another thread. + */ + struct pthread *joiner; + struct join_status join_status; /* * The current thread can belong to only one scheduling queue at @@ -648,8 +658,6 @@ struct pthread { * * o A queue of threads waiting for a mutex * o A queue of threads waiting for a condition variable - * o A queue of threads waiting for another thread to terminate - * (the join queue above) * o A queue of threads waiting for a file descriptor lock * o A queue of threads needing work done by the kernel thread * (waiting for a spinlock or file I/O) @@ -661,6 +669,9 @@ struct pthread { /* Priority queue entry for this thread: */ pthread_entry_t pqe; + /* Priority queue entry for this thread: */ + pthread_entry_t sqe; + /* Queue entry for this thread: */ pthread_entry_t qe; @@ -1018,7 +1029,6 @@ SCLASS int _thread_kern_new_state * Function prototype definitions. */ __BEGIN_DECLS -int _find_dead_thread(pthread_t); int _find_thread(pthread_t); struct pthread *_get_curthread(void); void _set_curthread(struct pthread *); diff --git a/lib/libpthread/uthread/uthread_cancel.c b/lib/libpthread/uthread/uthread_cancel.c index b536acc4b8e..8c8515dbbc7 100644 --- a/lib/libpthread/uthread/uthread_cancel.c +++ b/lib/libpthread/uthread/uthread_cancel.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uthread_cancel.c,v 1.7 2001/12/08 14:51:36 fgsch Exp $ */ +/* $OpenBSD: uthread_cancel.c,v 1.8 2001/12/11 00:19:47 fgsch Exp $ */ /* * David Leonard <d@openbsd.org>, 1999. Public domain. */ @@ -6,7 +6,6 @@ #include <pthread.h> #include "pthread_private.h" - static void finish_cancellation(void *arg); int @@ -44,6 +43,7 @@ pthread_cancel(pthread) case PS_FDW_WAIT: case PS_POLL_WAIT: case PS_SELECT_WAIT: + /* Remove these threads from the work queue: */ if ((pthread->flags & PTHREAD_FLAGS_IN_WORKQ) != 0) PTHREAD_WORKQ_REMOVE(pthread); @@ -71,6 +71,19 @@ pthread_cancel(pthread) break; case PS_JOIN: + /* + * Disconnect the thread from the joinee and + * detach: + */ + if (pthread->data.thread != NULL) { + pthread->data.thread->joiner = NULL; + pthread_detach((pthread_t) + pthread->data.thread); + } + pthread->cancelflags |= PTHREAD_CANCELLING; + PTHREAD_NEW_STATE(pthread, PS_RUNNING); + break; + case PS_SUSPENDED: /* Simply wake: */ /* XXX may be incorrect */ @@ -165,6 +178,11 @@ pthread_testcancel() if (((curthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) && ((curthread->cancelflags & PTHREAD_CANCELLING) != 0)) { + /* + * It is possible for this thread to be swapped out + * while performing cancellation; do not allow it + * to be cancelled again. + */ curthread->cancelflags &= ~PTHREAD_CANCELLING; #ifdef notyet _thread_exit_cleanup(); diff --git a/lib/libpthread/uthread/uthread_create.c b/lib/libpthread/uthread/uthread_create.c index 5b1897b7ef4..de96d4440de 100644 --- a/lib/libpthread/uthread/uthread_create.c +++ b/lib/libpthread/uthread/uthread_create.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uthread_create.c,v 1.16 2001/12/08 14:51:36 fgsch Exp $ */ +/* $OpenBSD: uthread_create.c,v 1.17 2001/12/11 00:19:47 fgsch Exp $ */ /* * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> * All rights reserved. @@ -140,8 +140,8 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr, new_thread->active_priority = new_thread->base_priority; new_thread->inherited_priority = 0; - /* Initialise the join queue for the new thread: */ - TAILQ_INIT(&(new_thread->join_queue)); + /* Initialize joiner to NULL (no joiner): */ + new_thread->joiner = NULL; /* Initialize the mutex queue: */ TAILQ_INIT(&new_thread->mutexq); diff --git a/lib/libpthread/uthread/uthread_detach.c b/lib/libpthread/uthread/uthread_detach.c index d6accdc13cf..786df9d165c 100644 --- a/lib/libpthread/uthread/uthread_detach.c +++ b/lib/libpthread/uthread/uthread_detach.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uthread_detach.c,v 1.6 2000/01/06 07:15:05 d Exp $ */ +/* $OpenBSD: uthread_detach.c,v 1.7 2001/12/11 00:19:47 fgsch Exp $ */ /* * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. * All rights reserved. @@ -41,7 +41,6 @@ int pthread_detach(pthread_t pthread) { int rval = 0; - pthread_t next_thread; /* Check for invalid calling parameters: */ if (pthread == NULL || pthread->magic != PTHREAD_MAGIC) @@ -59,13 +58,20 @@ pthread_detach(pthread_t pthread) */ _thread_kern_sig_defer(); - /* Enter a loop to bring all threads off the join queue: */ - while ((next_thread = TAILQ_FIRST(&pthread->join_queue)) != NULL) { - /* Remove the thread from the queue: */ - TAILQ_REMOVE(&pthread->join_queue, next_thread, qe); + /* Check if there is a joiner: */ + if (pthread->joiner != NULL) { + struct pthread *joiner = pthread->joiner; - /* Make the thread run: */ - PTHREAD_NEW_STATE(next_thread,PS_RUNNING); + /* Make the thread runnable: */ + PTHREAD_NEW_STATE(joiner, PS_RUNNING); + + /* Set the return value for the woken thread: */ + joiner->error = ESRCH; + + /* + * Disconnect the joiner from the thread being detached: + */ + pthread->joiner = NULL; } /* diff --git a/lib/libpthread/uthread/uthread_exit.c b/lib/libpthread/uthread/uthread_exit.c index c6013cbf1ea..232b55e008e 100644 --- a/lib/libpthread/uthread/uthread_exit.c +++ b/lib/libpthread/uthread/uthread_exit.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uthread_exit.c,v 1.13 2001/08/21 19:24:53 fgsch Exp $ */ +/* $OpenBSD: uthread_exit.c,v 1.14 2001/12/11 00:19:47 fgsch Exp $ */ /* * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> * All rights reserved. @@ -146,7 +146,7 @@ void pthread_exit(void *status) { struct pthread *curthread = _get_curthread(); - pthread_t pthread; + pthread_t pthread; /* Check if this thread is already in the process of exiting: */ if ((curthread->flags & PTHREAD_EXITING) != 0) { @@ -162,11 +162,9 @@ pthread_exit(void *status) while (curthread->cleanup != NULL) { pthread_cleanup_pop(1); } - if (curthread->attr.cleanup_attr != NULL) { curthread->attr.cleanup_attr(curthread->attr.arg_attr); } - /* Check if there is thread specific data: */ if (curthread->specific_data != NULL) { /* Run the thread-specific data destructors: */ @@ -174,26 +172,6 @@ pthread_exit(void *status) } /* - * Defer signals to protect the scheduling queues from access - * by the signal handler: - */ - _thread_kern_sig_defer(); - - /* Check if there are any threads joined to this one: */ - while ((pthread = TAILQ_FIRST(&(curthread->join_queue))) != NULL) { - /* Remove the thread from the queue: */ - TAILQ_REMOVE(&curthread->join_queue, pthread, qe); - - /* Wake the joined thread and let it detach this thread: */ - PTHREAD_NEW_STATE(pthread,PS_RUNNING); - } - - /* - * Undefer and handle pending signals, yielding if necessary: - */ - _thread_kern_sig_undefer(); - - /* * Lock the garbage collector mutex to ensure that the garbage * collector is not using the dead thread list. */ @@ -204,20 +182,6 @@ pthread_exit(void *status) TAILQ_INSERT_HEAD(&_dead_list, curthread, dle); /* - * Defer signals to protect the scheduling queues from access - * by the signal handler: - */ - _thread_kern_sig_defer(); - - /* Remove this thread from the thread list: */ - TAILQ_REMOVE(&_thread_list, curthread, tle); - - /* - * Undefer and handle pending signals, yielding if necessary: - */ - _thread_kern_sig_undefer(); - - /* * Signal the garbage collector thread that there is something * to clean up. */ @@ -225,17 +189,56 @@ pthread_exit(void *status) PANIC("Cannot signal gc cond"); /* - * Mark the thread as dead so it will not return if it - * gets context switched out when the mutex is unlocked. + * Avoid a race condition where a scheduling signal can occur + * causing the garbage collector thread to run. If this happens, + * the current thread can be cleaned out from under us. */ - PTHREAD_SET_STATE(curthread, PS_DEAD); + _thread_kern_sig_defer(); /* Unlock the garbage collector mutex: */ if (pthread_mutex_unlock(&_gc_mutex) != 0) - PANIC("Cannot lock gc mutex"); + PANIC("Cannot unlock gc mutex"); + + /* Check if there is a thread joining this one: */ + if (curthread->joiner != NULL) { + pthread = curthread->joiner; + curthread->joiner = NULL; + + switch (pthread->suspended) { + case SUSP_JOIN: + /* + * The joining thread is suspended. Change the + * suspension state to make the thread runnable when it + * is resumed: + */ + pthread->suspended = SUSP_NO; + break; + case SUSP_NO: + /* Make the joining thread runnable: */ + PTHREAD_NEW_STATE(pthread, PS_RUNNING); + break; + default: + PANIC("Unreachable code reached"); + } + + /* Set the return value for the joining thread: */ + pthread->join_status.ret = curthread->ret; + pthread->join_status.error = 0; + pthread->join_status.thread = NULL; + +#ifdef notyet + /* Make this thread collectable by the garbage collector. */ + PTHREAD_ASSERT(((curthread->attr.flags & PTHREAD_DETACHED) == + 0), "Cannot join a detached thread"); +#endif + curthread->attr.flags |= PTHREAD_DETACHED; + } + + /* Remove this thread from the thread list: */ + TAILQ_REMOVE(&_thread_list, curthread, tle); - /* This this thread will never be re-scheduled. */ - _thread_kern_sched(NULL); + /* This thread will never be re-scheduled. */ + _thread_kern_sched_state(PS_DEAD, __FILE__, __LINE__); /* This point should not be reached. */ PANIC("Dead thread has resumed"); diff --git a/lib/libpthread/uthread/uthread_find_thread.c b/lib/libpthread/uthread/uthread_find_thread.c index 42b561524b9..c531643e325 100644 --- a/lib/libpthread/uthread/uthread_find_thread.c +++ b/lib/libpthread/uthread/uthread_find_thread.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uthread_find_thread.c,v 1.4 1999/11/25 07:01:35 d Exp $ */ +/* $OpenBSD: uthread_find_thread.c,v 1.5 2001/12/11 00:19:47 fgsch Exp $ */ /* * Copyright (c) 1998 John Birrell <jb@cimlogic.com.au>. * All rights reserved. @@ -66,36 +66,4 @@ _find_thread(pthread_t pthread) /* Return zero if the thread exists: */ return ((pthread1 != NULL) ? 0:ESRCH); } - -/* Find a thread in the linked list of dead threads: */ -int -_find_dead_thread(pthread_t pthread) -{ - pthread_t pthread1; - - /* Check if the caller has specified an invalid thread: */ - if (pthread == NULL || pthread->magic != PTHREAD_MAGIC) - /* Invalid thread: */ - return(EINVAL); - - /* - * Lock the garbage collector mutex to ensure that the garbage - * collector is not using the dead thread list. - */ - if (pthread_mutex_lock(&_gc_mutex) != 0) - PANIC("Cannot lock gc mutex"); - - /* Search for the specified thread: */ - TAILQ_FOREACH(pthread1, &_dead_list, dle) { - if (pthread1 == pthread) - break; - } - - /* Unlock the garbage collector mutex: */ - if (pthread_mutex_unlock(&_gc_mutex) != 0) - PANIC("Cannot lock gc mutex"); - - /* Return zero if the thread exists: */ - return ((pthread1 != NULL) ? 0:ESRCH); -} #endif diff --git a/lib/libpthread/uthread/uthread_gc.c b/lib/libpthread/uthread/uthread_gc.c index 5f4e520d6aa..956633e7fe4 100644 --- a/lib/libpthread/uthread/uthread_gc.c +++ b/lib/libpthread/uthread/uthread_gc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uthread_gc.c,v 1.10 2001/08/21 19:24:53 fgsch Exp $ */ +/* $OpenBSD: uthread_gc.c,v 1.11 2001/12/11 00:19:47 fgsch Exp $ */ /* * Copyright (c) 1998 John Birrell <jb@cimlogic.com.au> * All rights reserved. @@ -59,8 +59,8 @@ _thread_gc(pthread_addr_t arg) void *p_stack; /* Block all signals */ - sigfillset (&mask); - sigprocmask (SIG_BLOCK, &mask, NULL); + sigfillset(&mask); + pthread_sigmask(SIG_BLOCK, &mask, NULL); /* Mark this thread as a library thread (not a user thread). */ curthread->flags |= PTHREAD_FLAGS_PRIVATE; @@ -98,6 +98,10 @@ _thread_gc(pthread_addr_t arg) */ _thread_kern_sig_undefer(); + /* No stack of thread structure to free yet: */ + p_stack = NULL; + pthread_cln = NULL; + /* * Lock the garbage collector mutex which ensures that * this thread sees another thread exit: @@ -105,10 +109,6 @@ _thread_gc(pthread_addr_t arg) if (pthread_mutex_lock(&_gc_mutex) != 0) PANIC("Cannot lock gc mutex"); - /* No stack of thread structure to free yet: */ - p_stack = NULL; - pthread_cln = NULL; - /* * Enter a loop to search for the first dead thread that * has memory to free. @@ -124,7 +124,7 @@ _thread_gc(pthread_addr_t arg) * Check if this thread has detached: */ else if ((pthread->attr.flags & - PTHREAD_DETACHED) != 0) { + PTHREAD_DETACHED) != 0) { /* Remove this thread from the dead list: */ TAILQ_REMOVE(&_dead_list, pthread, dle); @@ -193,13 +193,14 @@ _thread_gc(pthread_addr_t arg) _thread_stack_free(p_stack); if (pthread_cln != NULL) { + if (pthread_cln->name != NULL) { + /* Free the thread name string. */ + free(pthread_cln->name); + } /* * Free the memory allocated for the thread * structure. */ - if (pthread_cln->name != NULL) - free(pthread_cln->name); - if (pthread_cln->poll_data.fds != NULL) free(pthread_cln->poll_data.fds); diff --git a/lib/libpthread/uthread/uthread_info_openbsd.c b/lib/libpthread/uthread/uthread_info_openbsd.c index 439bf6a7655..a9f133550e9 100644 --- a/lib/libpthread/uthread/uthread_info_openbsd.c +++ b/lib/libpthread/uthread/uthread_info_openbsd.c @@ -1,3 +1,5 @@ +/* $OpenBSD: uthread_info_openbsd.c,v 1.5 2001/12/11 00:19:47 fgsch Exp $ */ + /* * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> * All rights reserved. @@ -29,7 +31,6 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $OpenBSD: uthread_info_openbsd.c,v 1.4 2001/12/08 14:51:36 fgsch Exp $ */ #include <stdio.h> #include <fcntl.h> @@ -220,6 +221,7 @@ _thread_dump_entry(pthread, fd, verbose) pthread->data.cond); _thread_sys_write(fd, s, strlen(s)); break; +#ifdef notyet case PS_JOIN: { struct pthread *t, * volatile *last; @@ -243,6 +245,7 @@ _thread_dump_entry(pthread, fd, verbose) _thread_sys_write(fd, s, strlen(s)); } break; +#endif case PS_SLEEP_WAIT: { struct timeval tv; diff --git a/lib/libpthread/uthread/uthread_init.c b/lib/libpthread/uthread/uthread_init.c index a0aaf8e8964..0c952b4130e 100644 --- a/lib/libpthread/uthread/uthread_init.c +++ b/lib/libpthread/uthread/uthread_init.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uthread_init.c,v 1.17 2001/12/08 14:51:36 fgsch Exp $ */ +/* $OpenBSD: uthread_init.c,v 1.18 2001/12/11 00:19:47 fgsch Exp $ */ /* * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> * All rights reserved. @@ -186,8 +186,8 @@ _thread_init(void) /* Initialise the state of the initial thread: */ _thread_initial->state = PS_RUNNING; - /* Initialise the queue: */ - TAILQ_INIT(&(_thread_initial->join_queue)); + /* Initialize joiner to NULL (no joiner): */ + _thread_initial->joiner = NULL; /* Initialize the owned mutex queue and count: */ TAILQ_INIT(&(_thread_initial->mutexq)); diff --git a/lib/libpthread/uthread/uthread_join.c b/lib/libpthread/uthread/uthread_join.c index 310e5521268..1cf1624a884 100644 --- a/lib/libpthread/uthread/uthread_join.c +++ b/lib/libpthread/uthread/uthread_join.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uthread_join.c,v 1.7 2001/08/21 19:24:53 fgsch Exp $ */ +/* $OpenBSD: uthread_join.c,v 1.8 2001/12/11 00:19:47 fgsch Exp $ */ /* * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. * All rights reserved. @@ -42,59 +42,120 @@ pthread_join(pthread_t pthread, void **thread_return) { struct pthread *curthread = _get_curthread(); int ret = 0; + pthread_t thread; - /* This is a cancellation point: */ _thread_enter_cancellation_point(); /* Check if the caller has specified an invalid thread: */ - if (pthread == NULL || pthread->magic != PTHREAD_MAGIC) + if (pthread == NULL || pthread->magic != PTHREAD_MAGIC) { /* Invalid thread: */ - ret = EINVAL; + _thread_leave_cancellation_point(); + return (EINVAL); + } /* Check if the caller has specified itself: */ - else if (pthread == curthread) + if (pthread == curthread) { /* Avoid a deadlock condition: */ - ret = EDEADLK; + _thread_leave_cancellation_point(); + return (EDEADLK); + } /* - * Find the thread in the list of active threads or in the - * list of dead threads: + * Lock the garbage collector mutex to ensure that the garbage + * collector is not using the dead thread list. */ - else if (_find_thread(pthread) != 0 && - _find_dead_thread(pthread) != 0) - /* Return an error: */ - ret = ESRCH; + if (pthread_mutex_lock(&_gc_mutex) != 0) + PANIC("Cannot lock gc mutex"); + + /* + * Defer signals to protect the thread list from access + * by the signal handler: + */ + _thread_kern_sig_defer(); + + /* + * Unlock the garbage collector mutex, now that the garbage collector + * can't be run: + */ + if (pthread_mutex_unlock(&_gc_mutex) != 0) + PANIC("Cannot unlock gc mutex"); + + /* + * Search for the specified thread in the list of active threads. This + * is done manually here rather than calling _find_thread() because + * the searches in _thread_list and _dead_list (as well as setting up + * join/detach state) have to be done atomically. + */ + TAILQ_FOREACH(thread, &_thread_list, tle) { + if (thread == pthread) + break; + } + if (thread == NULL) { + /* + * Search for the specified thread in the list of dead threads: + */ + TAILQ_FOREACH(thread, &_dead_list, dle) { + if (thread == pthread) + break; + } + } + + /* Check if the thread was not found or has been detached: */ + if (thread == NULL || + ((pthread->attr.flags & PTHREAD_DETACHED) != 0)) { + /* Undefer and handle pending signals, yielding if necessary: */ + _thread_kern_sig_undefer(); - /* Check if this thread has been detached: */ - else if ((pthread->attr.flags & PTHREAD_DETACHED) != 0) /* Return an error: */ ret = ESRCH; + } else if (pthread->joiner != NULL) { + /* Undefer and handle pending signals, yielding if necessary: */ + _thread_kern_sig_undefer(); + + /* Multiple joiners are not supported. */ + ret = EOPNOTSUPP; + /* Check if the thread is not dead: */ - else if (pthread->state != PS_DEAD) { - /* Add the running thread to the join queue: */ - TAILQ_INSERT_TAIL(&(pthread->join_queue), curthread, qe); - - /* Schedule the next thread: */ - _thread_kern_sched_state(PS_JOIN, __FILE__, __LINE__); - - /* Check if the thread is not detached: */ - if ((pthread->attr.flags & PTHREAD_DETACHED) == 0) { - /* Check if the return value is required: */ - if (thread_return) - /* Return the thread's return value: */ - *thread_return = pthread->ret; + } else if (pthread->state != PS_DEAD) { + /* Set the running thread to be the joiner: */ + pthread->joiner = curthread; + + /* Keep track of which thread we're joining to: */ + curthread->join_status.thread = pthread; + + while (curthread->join_status.thread == pthread) { + /* Schedule the next thread: */ + _thread_kern_sched_state(PS_JOIN, __FILE__, __LINE__); } - else - /* Return an error: */ - ret = ESRCH; - /* Check if the return value is required: */ - } else if (thread_return != NULL) - /* Return the thread's return value: */ - *thread_return = pthread->ret; + /* + * The thread return value and error are set by the thread we're + * joining to when it exits or detaches: + */ + ret = curthread->join_status.error; + ret = curthread->join_status.error; + if ((ret == 0) && (thread_return != NULL)) + *thread_return = curthread->join_status.ret; + } else { + /* + * The thread exited (is dead) without being detached, and no + * thread has joined it. + */ + + /* Check if the return value is required: */ + if (thread_return != NULL) { + /* Return the thread's return value: */ + *thread_return = pthread->ret; + } + + /* Make the thread collectable by the garbage collector. */ + pthread->attr.flags |= PTHREAD_DETACHED; + + /* Undefer and handle pending signals, yielding if necessary: */ + _thread_kern_sig_undefer(); + } - /* No longer in a cancellation point: */ _thread_leave_cancellation_point(); /* Return the completion status: */ |