summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFederico G. Schwindt <fgsch@cvs.openbsd.org>2001-12-11 00:19:48 +0000
committerFederico G. Schwindt <fgsch@cvs.openbsd.org>2001-12-11 00:19:48 +0000
commitdf4b52670da644e7bd952242b6d1e8f6fcd0c5b2 (patch)
tree362f2780fa4da1e285ed537b8a08e2eccf119a0b
parentd9bad47758857d3344bedc12dff936f73958442f (diff)
More sync with freebsd code; join related code this time.
-rw-r--r--lib/libc_r/uthread/pthread_private.h22
-rw-r--r--lib/libc_r/uthread/uthread_cancel.c22
-rw-r--r--lib/libc_r/uthread/uthread_create.c6
-rw-r--r--lib/libc_r/uthread/uthread_detach.c22
-rw-r--r--lib/libc_r/uthread/uthread_exit.c91
-rw-r--r--lib/libc_r/uthread/uthread_find_thread.c34
-rw-r--r--lib/libc_r/uthread/uthread_gc.c23
-rw-r--r--lib/libc_r/uthread/uthread_info_openbsd.c5
-rw-r--r--lib/libc_r/uthread/uthread_init.c6
-rw-r--r--lib/libc_r/uthread/uthread_join.c131
-rw-r--r--lib/libpthread/uthread/pthread_private.h22
-rw-r--r--lib/libpthread/uthread/uthread_cancel.c22
-rw-r--r--lib/libpthread/uthread/uthread_create.c6
-rw-r--r--lib/libpthread/uthread/uthread_detach.c22
-rw-r--r--lib/libpthread/uthread/uthread_exit.c91
-rw-r--r--lib/libpthread/uthread/uthread_find_thread.c34
-rw-r--r--lib/libpthread/uthread/uthread_gc.c23
-rw-r--r--lib/libpthread/uthread/uthread_info_openbsd.c5
-rw-r--r--lib/libpthread/uthread/uthread_init.c6
-rw-r--r--lib/libpthread/uthread/uthread_join.c131
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: */