xref: /freebsd/lib/libthr/thread/thr_join.c (revision 6b3455a7665208c366849f0b2b3bc916fb97516e)
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$
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 
41 int
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 
190 out:
191 	_thread_leave_cancellation_point();
192 
193 	/* Return the completion status: */
194 	return (ret);
195 }
196