thr_join.c revision 71581
1/*
2 * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by John Birrell.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * $FreeBSD: head/lib/libkse/thread/thr_join.c 71581 2001-01-24 13:03:38Z deischen $
33 */
34#include <errno.h>
35#include <pthread.h>
36#include "pthread_private.h"
37
38#pragma weak	pthread_join=_pthread_join
39
40int
41_pthread_join(pthread_t pthread, void **thread_return)
42{
43	struct pthread	*curthread = _get_curthread();
44	int ret = 0;
45
46	_thread_enter_cancellation_point();
47
48	/* Check if the caller has specified an invalid thread: */
49	if (pthread == NULL || pthread->magic != PTHREAD_MAGIC) {
50		/* Invalid thread: */
51		_thread_leave_cancellation_point();
52		return(EINVAL);
53	}
54
55	/* Check if the caller has specified itself: */
56	if (pthread == curthread) {
57		/* Avoid a deadlock condition: */
58		_thread_leave_cancellation_point();
59		return(EDEADLK);
60	}
61
62	/*
63	 * Find the thread in the list of active threads or in the
64	 * list of dead threads:
65	 */
66	if ((_find_thread(pthread) != 0) && (_find_dead_thread(pthread) != 0))
67		/* Return an error: */
68		ret = ESRCH;
69
70	/* Check if this thread has been detached: */
71	else if ((pthread->attr.flags & PTHREAD_DETACHED) != 0)
72		/* Return an error: */
73		ret = ESRCH;
74
75	/* Check if the thread is not dead: */
76	else if (pthread->state != PS_DEAD) {
77		PTHREAD_ASSERT_NOT_IN_SYNCQ(curthread);
78
79		/*
80		 * Enter a loop in case this thread is woken prematurely
81		 * in order to invoke a signal handler:
82		 */
83		for (;;) {
84			/* Clear the interrupted flag: */
85			curthread->interrupted = 0;
86
87			/*
88			 * Protect against being context switched out while
89			 * adding this thread to the join queue.
90			 */
91			_thread_kern_sig_defer();
92
93			/* Add the running thread to the join queue: */
94			TAILQ_INSERT_TAIL(&(pthread->join_queue),
95			    curthread, sqe);
96			curthread->flags |= PTHREAD_FLAGS_IN_JOINQ;
97			curthread->data.thread = pthread;
98
99			/* Schedule the next thread: */
100			_thread_kern_sched_state(PS_JOIN, __FILE__, __LINE__);
101
102			if ((curthread->flags & PTHREAD_FLAGS_IN_JOINQ) != 0) {
103				TAILQ_REMOVE(&(pthread->join_queue),
104				    curthread, sqe);
105				curthread->flags &= ~PTHREAD_FLAGS_IN_JOINQ;
106			}
107			curthread->data.thread = NULL;
108
109			_thread_kern_sig_undefer();
110
111			if (curthread->interrupted != 0) {
112				if (curthread->continuation != NULL)
113					curthread->continuation(curthread);
114				/*
115				 * This thread was interrupted, probably to
116				 * invoke a signal handler.  Make sure the
117				 * target thread is still joinable.
118				 */
119				if (((_find_thread(pthread) != 0) &&
120				    (_find_dead_thread(pthread) != 0)) ||
121				    ((pthread->attr.flags &
122				    PTHREAD_DETACHED) != 0)) {
123					/* Return an error: */
124					ret = ESRCH;
125
126					/* We're done; break out of the loop. */
127					break;
128				}
129				else if (pthread->state == PS_DEAD) {
130					/* We're done; break out of the loop. */
131					break;
132				}
133			} else {
134				/*
135				 * The thread return value and error are set
136				 * by the thread we're joining to when it
137				 * exits or detaches:
138				 */
139				ret = curthread->error;
140				if ((ret == 0) && (thread_return != NULL))
141					*thread_return = curthread->ret;
142
143				/* We're done; break out of the loop. */
144				break;
145			}
146		}
147	/* Check if the return value is required: */
148	} else if (thread_return != NULL)
149		/* Return the thread's return value: */
150		*thread_return = pthread->ret;
151
152	_thread_leave_cancellation_point();
153
154	/* Return the completion status: */
155	return (ret);
156}
157
158void
159_join_backout(pthread_t pthread)
160{
161	struct pthread	*curthread = _get_curthread();
162
163	_thread_kern_sig_defer();
164	if ((pthread->flags & PTHREAD_FLAGS_IN_JOINQ) != 0) {
165		TAILQ_REMOVE(&pthread->data.thread->join_queue, pthread, sqe);
166		curthread->flags &= ~PTHREAD_FLAGS_IN_JOINQ;
167	}
168	_thread_kern_sig_undefer();
169}
170