summaryrefslogtreecommitdiffstats
path: root/lib/libthr/thread/thr_join.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libthr/thread/thr_join.c')
-rw-r--r--lib/libthr/thread/thr_join.c187
1 files changed, 46 insertions, 141 deletions
diff --git a/lib/libthr/thread/thr_join.c b/lib/libthr/thread/thr_join.c
index 29dd421..c44b261 100644
--- a/lib/libthr/thread/thr_join.c
+++ b/lib/libthr/thread/thr_join.c
@@ -31,170 +31,75 @@
*
* $FreeBSD$
*/
+
#include <errno.h>
#include <pthread.h>
-#include <stdlib.h>
+
#include "thr_private.h"
__weak_reference(_pthread_join, pthread_join);
+static void backout_join(void *arg)
+{
+ struct pthread *curthread = _get_curthread();
+ struct pthread *pthread = (struct pthread *)arg;
+
+ THREAD_LIST_LOCK(curthread);
+ pthread->joiner = NULL;
+ THREAD_LIST_UNLOCK(curthread);
+}
+
int
_pthread_join(pthread_t pthread, void **thread_return)
{
- int ret, dead;
- pthread_t thread;
+ struct pthread *curthread = _get_curthread();
+ void *tmp;
+ long state;
+ int oldcancel;
+ int ret = 0;
- /* Check if the caller has specified an invalid thread: */
- if (pthread->magic != PTHREAD_MAGIC)
- /* Invalid thread: */
- return(EINVAL);
+ if (pthread == NULL)
+ return (EINVAL);
- /* Check if the caller has specified itself: */
if (pthread == curthread)
- /* Avoid a deadlock condition: */
- return(EDEADLK);
+ return (EDEADLK);
- /*
- * 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.
- */
- ret = 0;
- dead = 0;
- thread = NULL;
- _thread_sigblock();
- DEAD_LIST_LOCK;
- THREAD_LIST_LOCK;
- if (!pthread->isdead) {
- TAILQ_FOREACH(thread, &_thread_list, tle) {
- if (thread == pthread) {
- PTHREAD_LOCK(pthread);
- break;
- }
- }
- }
- if (thread == NULL) {
- TAILQ_FOREACH(thread, &_dead_list, dle) {
- if (thread == pthread) {
- PTHREAD_LOCK(pthread);
- dead = 1;
- break;
- }
- }
- }
-
- /* Check if the thread was not found or has been detached: */
- if (thread == NULL) {
- THREAD_LIST_UNLOCK;
- DEAD_LIST_UNLOCK;
- _thread_sigunblock();
+ THREAD_LIST_LOCK(curthread);
+ if ((ret = _thr_find_thread(curthread, pthread, 1)) != 0) {
ret = ESRCH;
- goto out;
- }
- if ((pthread->attr.flags & PTHREAD_DETACHED) != 0) {
- PTHREAD_UNLOCK(pthread);
- THREAD_LIST_UNLOCK;
- DEAD_LIST_UNLOCK;
- _thread_sigunblock();
- ret = EINVAL;
- goto out;
- }
-
- if (pthread->joiner != NULL) {
+ } else if ((pthread->tlflags & TLFLAGS_DETACHED) != 0) {
+ ret = ESRCH;
+ } else if (pthread->joiner != NULL) {
/* Multiple joiners are not supported. */
- /* XXXTHR - support multiple joiners. */
- PTHREAD_UNLOCK(pthread);
- THREAD_LIST_UNLOCK;
- DEAD_LIST_UNLOCK;
- _thread_sigunblock();
ret = ENOTSUP;
- goto out;
-
}
+ if (ret) {
+ THREAD_LIST_UNLOCK(curthread);
+ return (ret);
+ }
+ /* Set the running thread to be the joiner: */
+ pthread->joiner = curthread;
- /* Check if the thread is not dead: */
- if (!dead) {
- /* Set the running thread to be the joiner: */
- pthread->joiner = curthread;
- PTHREAD_UNLOCK(pthread);
-
- /* Keep track of which thread we're joining to: */
- curthread->join_status.thread = pthread;
-
- while (curthread->join_status.thread == pthread) {
- /* Wait for our signal to wake up. */
- THREAD_LIST_UNLOCK;
- DEAD_LIST_UNLOCK;
- _thread_sigunblock();
- if (curthread->cancellation != CS_NULL)
- pthread->joiner = NULL;
- _thread_enter_cancellation_point();
-
- /*
- * XXX - Workaround to make a join a cancellation
- * point. Must find a better solution.
- */
- PTHREAD_LOCK(curthread);
- curthread->flags |= PTHREAD_FLAGS_SUSPENDED;
- PTHREAD_UNLOCK(curthread);
- ret = _thread_suspend(curthread, NULL);
- if (ret != 0 && ret != EAGAIN && ret != EINTR)
- PANIC("Unable to suspend in join.");
- PTHREAD_LOCK(curthread);
- curthread->flags &= ~PTHREAD_FLAGS_SUSPENDED;
- PTHREAD_UNLOCK(curthread);
- if (curthread->cancellation != CS_NULL)
- pthread->joiner = NULL;
- _thread_leave_cancellation_point();
+ THREAD_LIST_UNLOCK(curthread);
- /*
- * XXX - For correctness reasons.
- * We must aquire these in the same order and also
- * importantly, release in the same order because
- * otherwise we might deadlock with the joined thread
- * when we attempt to release one of these locks.
- */
- _thread_sigblock();
- DEAD_LIST_LOCK;
- THREAD_LIST_LOCK;
- }
+ THR_CLEANUP_PUSH(curthread, backout_join, pthread);
+ oldcancel = _thr_cancel_enter(curthread);
- /*
- * 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;
- if ((ret == 0) && (thread_return != NULL))
- *thread_return = curthread->join_status.ret;
- THREAD_LIST_UNLOCK;
- DEAD_LIST_UNLOCK;
- _thread_sigunblock();
- } else {
- /*
- * The thread exited (is dead) without being detached, and no
- * thread has joined it.
- */
+ while ((state = pthread->state) != PS_DEAD) {
+ _thr_umtx_wait(&pthread->state, state, NULL);
+ }
- /* Check if the return value is required: */
- if (thread_return != NULL) {
- /* Return the thread's return value: */
- *thread_return = pthread->ret;
- }
+ _thr_cancel_leave(curthread, oldcancel);
+ THR_CLEANUP_POP(curthread, 0);
- /* Free all remaining memory allocated to the thread. */
- pthread->attr.flags |= PTHREAD_DETACHED;
- PTHREAD_UNLOCK(pthread);
- TAILQ_REMOVE(&_dead_list, pthread, dle);
- deadlist_free_onethread(pthread);
- THREAD_LIST_UNLOCK;
- DEAD_LIST_UNLOCK;
- _thread_sigunblock();
- }
+ tmp = pthread->ret;
+ THREAD_LIST_LOCK(curthread);
+ pthread->tlflags |= TLFLAGS_DETACHED;
+ THR_GCLIST_ADD(pthread);
+ THREAD_LIST_UNLOCK(curthread);
-out:
- _thread_leave_cancellation_point();
+ if (thread_return != NULL)
+ *thread_return = tmp;
- /* Return the completion status: */
return (ret);
}
OpenPOWER on IntegriCloud