thr_join.c revision 75369
133965Sjdp/*
2104834Sobrien * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>.
377298Sobrien * All rights reserved.
433965Sjdp *
533965Sjdp * Redistribution and use in source and binary forms, with or without
633965Sjdp * modification, are permitted provided that the following conditions
733965Sjdp * are met:
833965Sjdp * 1. Redistributions of source code must retain the above copyright
933965Sjdp *    notice, this list of conditions and the following disclaimer.
1033965Sjdp * 2. Redistributions in binary form must reproduce the above copyright
1133965Sjdp *    notice, this list of conditions and the following disclaimer in the
1233965Sjdp *    documentation and/or other materials provided with the distribution.
1333965Sjdp * 3. All advertising materials mentioning features or use of this software
1433965Sjdp *    must display the following acknowledgement:
1533965Sjdp *	This product includes software developed by John Birrell.
1633965Sjdp * 4. Neither the name of the author nor the names of any co-contributors
1733965Sjdp *    may be used to endorse or promote products derived from this software
1833965Sjdp *    without specific prior written permission.
1933965Sjdp *
2033965Sjdp * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
2133965Sjdp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22104834Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2333965Sjdp * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2433965Sjdp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2533965Sjdp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2677298Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2733965Sjdp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2833965Sjdp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2933965Sjdp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3033965Sjdp * SUCH DAMAGE.
3177298Sobrien *
3233965Sjdp * $FreeBSD: head/lib/libkse/thread/thr_join.c 75369 2001-04-10 04:19:21Z deischen $
3333965Sjdp */
3433965Sjdp#include <errno.h>
3533965Sjdp#include <pthread.h>
3677298Sobrien#include "pthread_private.h"
3733965Sjdp
3833965Sjdp__weak_reference(_pthread_join, pthread_join);
3933965Sjdp
4033965Sjdpint
4177298Sobrien_pthread_join(pthread_t pthread, void **thread_return)
4233965Sjdp{
4333965Sjdp	struct pthread	*curthread = _get_curthread();
4433965Sjdp	int ret = 0;
4533965Sjdp
4633965Sjdp	_thread_enter_cancellation_point();
4733965Sjdp
4833965Sjdp	/* Check if the caller has specified an invalid thread: */
4933965Sjdp	if (pthread == NULL || pthread->magic != PTHREAD_MAGIC) {
5033965Sjdp		/* Invalid thread: */
5133965Sjdp		_thread_leave_cancellation_point();
5233965Sjdp		return(EINVAL);
5333965Sjdp	}
5433965Sjdp
5533965Sjdp	/* Check if the caller has specified itself: */
5633965Sjdp	if (pthread == curthread) {
5777298Sobrien		/* Avoid a deadlock condition: */
5833965Sjdp		_thread_leave_cancellation_point();
5977298Sobrien		return(EDEADLK);
6033965Sjdp	}
6133965Sjdp
6277298Sobrien	/*
6333965Sjdp	 * Find the thread in the list of active threads or in the
6433965Sjdp	 * list of dead threads:
6533965Sjdp	 */
6633965Sjdp	if ((_find_thread(pthread) != 0) && (_find_dead_thread(pthread) != 0))
6777298Sobrien		/* Return an error: */
6833965Sjdp		ret = ESRCH;
6933965Sjdp
7033965Sjdp	/* Check if this thread has been detached: */
7177298Sobrien	else if ((pthread->attr.flags & PTHREAD_DETACHED) != 0)
7233965Sjdp		/* Return an error: */
7377298Sobrien		ret = ESRCH;
7433965Sjdp
7533965Sjdp	/* Check if the thread is not dead: */
7633965Sjdp	else if (pthread->state != PS_DEAD) {
7733965Sjdp		PTHREAD_ASSERT_NOT_IN_SYNCQ(curthread);
7833965Sjdp
7977298Sobrien		/*
8077298Sobrien		 * Enter a loop in case this thread is woken prematurely
8177298Sobrien		 * in order to invoke a signal handler:
8277298Sobrien		 */
8377298Sobrien		for (;;) {
8433965Sjdp			/* Clear the interrupted flag: */
8533965Sjdp			curthread->interrupted = 0;
8633965Sjdp
8733965Sjdp			/*
8833965Sjdp			 * Protect against being context switched out while
8977298Sobrien			 * adding this thread to the join queue.
9077298Sobrien			 */
9133965Sjdp			_thread_kern_sig_defer();
9277298Sobrien
9377298Sobrien			/* Add the running thread to the join queue: */
9433965Sjdp			TAILQ_INSERT_TAIL(&(pthread->join_queue),
9577298Sobrien			    curthread, sqe);
9677298Sobrien			curthread->flags |= PTHREAD_FLAGS_IN_JOINQ;
9733965Sjdp			curthread->data.thread = pthread;
9877298Sobrien
9933965Sjdp			/* Schedule the next thread: */
10033965Sjdp			_thread_kern_sched_state(PS_JOIN, __FILE__, __LINE__);
10133965Sjdp
10233965Sjdp			if ((curthread->flags & PTHREAD_FLAGS_IN_JOINQ) != 0) {
10333965Sjdp				TAILQ_REMOVE(&(pthread->join_queue),
10433965Sjdp				    curthread, sqe);
10533965Sjdp				curthread->flags &= ~PTHREAD_FLAGS_IN_JOINQ;
10633965Sjdp			}
10733965Sjdp			curthread->data.thread = NULL;
10833965Sjdp
10933965Sjdp			_thread_kern_sig_undefer();
11033965Sjdp
11177298Sobrien			if (curthread->interrupted != 0) {
11277298Sobrien				if (curthread->continuation != NULL)
11333965Sjdp					curthread->continuation(curthread);
11433965Sjdp				/*
11533965Sjdp				 * This thread was interrupted, probably to
11633965Sjdp				 * invoke a signal handler.  Make sure the
11733965Sjdp				 * target thread is still joinable.
11833965Sjdp				 */
11933965Sjdp				if (((_find_thread(pthread) != 0) &&
12033965Sjdp				    (_find_dead_thread(pthread) != 0)) ||
12133965Sjdp				    ((pthread->attr.flags &
12233965Sjdp				    PTHREAD_DETACHED) != 0)) {
12333965Sjdp					/* Return an error: */
12433965Sjdp					ret = ESRCH;
12533965Sjdp
12633965Sjdp					/* We're done; break out of the loop. */
12733965Sjdp					break;
12833965Sjdp				}
12933965Sjdp				else if (pthread->state == PS_DEAD) {
13077298Sobrien					/* We're done; break out of the loop. */
13177298Sobrien					break;
13233965Sjdp				}
13333965Sjdp			} else {
13433965Sjdp				/*
13533965Sjdp				 * The thread return value and error are set
13633965Sjdp				 * by the thread we're joining to when it
13733965Sjdp				 * exits or detaches:
13833965Sjdp				 */
13933965Sjdp				ret = curthread->error;
14033965Sjdp				if ((ret == 0) && (thread_return != NULL))
14133965Sjdp					*thread_return = curthread->ret;
14233965Sjdp
14333965Sjdp				/* We're done; break out of the loop. */
14433965Sjdp				break;
14533965Sjdp			}
14633965Sjdp		}
14733965Sjdp	/* Check if the return value is required: */
14833965Sjdp	} else if (thread_return != NULL)
14933965Sjdp		/* Return the thread's return value: */
15033965Sjdp		*thread_return = pthread->ret;
15133965Sjdp
15233965Sjdp	_thread_leave_cancellation_point();
15333965Sjdp
15433965Sjdp	/* Return the completion status: */
15533965Sjdp	return (ret);
15633965Sjdp}
15733965Sjdp
15833965Sjdpvoid
15933965Sjdp_join_backout(pthread_t pthread)
16033965Sjdp{
16133965Sjdp	struct pthread	*curthread = _get_curthread();
16233965Sjdp
16377298Sobrien	_thread_kern_sig_defer();
16477298Sobrien	if ((pthread->flags & PTHREAD_FLAGS_IN_JOINQ) != 0) {
16533965Sjdp		TAILQ_REMOVE(&pthread->data.thread->join_queue, pthread, sqe);
16633965Sjdp		curthread->flags &= ~PTHREAD_FLAGS_IN_JOINQ;
16733965Sjdp	}
16833965Sjdp	_thread_kern_sig_undefer();
16933965Sjdp}
17033965Sjdp