thr_join.c revision 129484
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/libthr/thread/thr_join.c 129484 2004-05-20 12:06:16Z mtm $
33 */
34#include <errno.h>
35#include <pthread.h>
36#include <stdlib.h>
37#include "thr_private.h"
38
39__weak_reference(_pthread_join, pthread_join);
40
41int
42_pthread_join(pthread_t pthread, void **thread_return)
43{
44	int ret = 0;
45	pthread_t thread;
46
47	/* Check if the caller has specified an invalid thread: */
48	if (pthread->magic != PTHREAD_MAGIC)
49		/* Invalid thread: */
50		return(EINVAL);
51
52	/* Check if the caller has specified itself: */
53	if (pthread == curthread)
54		/* Avoid a deadlock condition: */
55		return(EDEADLK);
56
57	/*
58	 * Search for the specified thread in the list of active threads.  This
59	 * is done manually here rather than calling _find_thread() because
60	 * the searches in _thread_list and _dead_list (as well as setting up
61	 * join/detach state) have to be done atomically.
62	 */
63	_thread_sigblock();
64	DEAD_LIST_LOCK;
65	THREAD_LIST_LOCK;
66	if (!pthread->isdead) {
67		TAILQ_FOREACH(thread, &_thread_list, tle) {
68			if (thread == pthread) {
69				PTHREAD_LOCK(pthread);
70				break;
71			}
72		}
73	} else {
74		TAILQ_FOREACH(thread, &_dead_list, dle) {
75			if (thread == pthread) {
76				PTHREAD_LOCK(pthread);
77				break;
78			}
79		}
80	}
81
82	/* Check if the thread was not found or has been detached: */
83	if (thread == NULL) {
84		THREAD_LIST_UNLOCK;
85		DEAD_LIST_UNLOCK;
86		_thread_sigunblock();
87		ret = ESRCH;
88		goto out;
89	}
90	if ((pthread->attr.flags & PTHREAD_DETACHED) != 0) {
91		PTHREAD_UNLOCK(pthread);
92		THREAD_LIST_UNLOCK;
93		DEAD_LIST_UNLOCK;
94		_thread_sigunblock();
95		ret = EINVAL;
96		goto out;
97	}
98
99	if (pthread->joiner != NULL) {
100		/* Multiple joiners are not supported. */
101		/* XXXTHR - support multiple joiners. */
102		PTHREAD_UNLOCK(pthread);
103		THREAD_LIST_UNLOCK;
104		DEAD_LIST_UNLOCK;
105		_thread_sigunblock();
106		ret = ENOTSUP;
107		goto out;
108
109	}
110
111	/* Check if the thread is not dead: */
112	if (!pthread->isdead) {
113		/* Set the running thread to be the joiner: */
114		pthread->joiner = curthread;
115		PTHREAD_UNLOCK(pthread);
116
117		/* Keep track of which thread we're joining to: */
118		curthread->join_status.thread = pthread;
119
120		while (curthread->join_status.thread == pthread) {
121			/* Wait for our signal to wake up. */
122			THREAD_LIST_UNLOCK;
123			DEAD_LIST_UNLOCK;
124			_thread_sigunblock();
125			if (curthread->cancellation != CS_NULL)
126				pthread->joiner = NULL;
127			_thread_enter_cancellation_point();
128
129			/*
130			 * XXX - Workaround to make a join a cancellation
131			 *	 point. Must find a better solution.
132			 */
133			PTHREAD_LOCK(curthread);
134			curthread->flags |= PTHREAD_FLAGS_SUSPENDED;
135			PTHREAD_UNLOCK(curthread);
136			ret = _thread_suspend(curthread, NULL);
137			if (ret != 0 && ret != EAGAIN && ret != EINTR)
138				PANIC("Unable to suspend in join.");
139			PTHREAD_LOCK(curthread);
140			curthread->flags &= ~PTHREAD_FLAGS_SUSPENDED;
141			PTHREAD_UNLOCK(curthread);
142			if (curthread->cancellation != CS_NULL)
143				pthread->joiner = NULL;
144			_thread_leave_cancellation_point();
145
146			/*
147			 * XXX - For correctness reasons.
148			 * We must aquire these in the same order and also
149			 * importantly, release in the same order because
150			 * otherwise we might deadlock with the joined thread
151			 * when we attempt to release one of these locks.
152			 */
153			_thread_sigblock();
154			DEAD_LIST_LOCK;
155			THREAD_LIST_LOCK;
156		}
157
158		/*
159		 * The thread return value and error are set by the thread we're
160		 * joining to when it exits or detaches:
161		 */
162		ret = curthread->join_status.error;
163		if ((ret == 0) && (thread_return != NULL))
164			*thread_return = curthread->join_status.ret;
165		THREAD_LIST_UNLOCK;
166		DEAD_LIST_UNLOCK;
167		_thread_sigunblock();
168	} else {
169		/*
170		 * The thread exited (is dead) without being detached, and no
171		 * thread has joined it.
172		 */
173
174		/* Check if the return value is required: */
175		if (thread_return != NULL) {
176			/* Return the thread's return value: */
177			*thread_return = pthread->ret;
178		}
179
180		/* Free all remaining memory allocated to the thread. */
181		pthread->attr.flags |= PTHREAD_DETACHED;
182		PTHREAD_UNLOCK(pthread);
183		TAILQ_REMOVE(&_dead_list, pthread, dle);
184		deadlist_free_onethread(pthread);
185		THREAD_LIST_UNLOCK;
186		DEAD_LIST_UNLOCK;
187		_thread_sigunblock();
188	}
189
190out:
191	_thread_leave_cancellation_point();
192
193	/* Return the completion status: */
194	return (ret);
195}
196