xref: /freebsd/lib/libthr/thread/thr_join.c (revision 6af83ee0d2941d18880b6aaa2b4facd1d30c6106)
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, dead;
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 	ret = 0;
64 	dead = 0;
65 	thread = NULL;
66 	_thread_sigblock();
67 	DEAD_LIST_LOCK;
68 	THREAD_LIST_LOCK;
69 	if (!pthread->isdead) {
70 		TAILQ_FOREACH(thread, &_thread_list, tle) {
71 			if (thread == pthread) {
72 				PTHREAD_LOCK(pthread);
73 				break;
74 			}
75 		}
76 	}
77 	if (thread == NULL) {
78 		TAILQ_FOREACH(thread, &_dead_list, dle) {
79 			if (thread == pthread) {
80 				PTHREAD_LOCK(pthread);
81 				dead = 1;
82 				break;
83 			}
84 		}
85 	}
86 
87 	/* Check if the thread was not found or has been detached: */
88 	if (thread == NULL) {
89 		THREAD_LIST_UNLOCK;
90 		DEAD_LIST_UNLOCK;
91 		_thread_sigunblock();
92 		ret = ESRCH;
93 		goto out;
94 	}
95 	if ((pthread->attr.flags & PTHREAD_DETACHED) != 0) {
96 		PTHREAD_UNLOCK(pthread);
97 		THREAD_LIST_UNLOCK;
98 		DEAD_LIST_UNLOCK;
99 		_thread_sigunblock();
100 		ret = EINVAL;
101 		goto out;
102 	}
103 
104 	if (pthread->joiner != NULL) {
105 		/* Multiple joiners are not supported. */
106 		/* XXXTHR - support multiple joiners. */
107 		PTHREAD_UNLOCK(pthread);
108 		THREAD_LIST_UNLOCK;
109 		DEAD_LIST_UNLOCK;
110 		_thread_sigunblock();
111 		ret = ENOTSUP;
112 		goto out;
113 
114 	}
115 
116 	/* Check if the thread is not dead: */
117 	if (!dead) {
118 		/* Set the running thread to be the joiner: */
119 		pthread->joiner = curthread;
120 		PTHREAD_UNLOCK(pthread);
121 
122 		/* Keep track of which thread we're joining to: */
123 		curthread->join_status.thread = pthread;
124 
125 		while (curthread->join_status.thread == pthread) {
126 			/* Wait for our signal to wake up. */
127 			THREAD_LIST_UNLOCK;
128 			DEAD_LIST_UNLOCK;
129 			_thread_sigunblock();
130 			if (curthread->cancellation != CS_NULL)
131 				pthread->joiner = NULL;
132 			_thread_enter_cancellation_point();
133 
134 			/*
135 			 * XXX - Workaround to make a join a cancellation
136 			 *	 point. Must find a better solution.
137 			 */
138 			PTHREAD_LOCK(curthread);
139 			curthread->flags |= PTHREAD_FLAGS_SUSPENDED;
140 			PTHREAD_UNLOCK(curthread);
141 			ret = _thread_suspend(curthread, NULL);
142 			if (ret != 0 && ret != EAGAIN && ret != EINTR)
143 				PANIC("Unable to suspend in join.");
144 			PTHREAD_LOCK(curthread);
145 			curthread->flags &= ~PTHREAD_FLAGS_SUSPENDED;
146 			PTHREAD_UNLOCK(curthread);
147 			if (curthread->cancellation != CS_NULL)
148 				pthread->joiner = NULL;
149 			_thread_leave_cancellation_point();
150 
151 			/*
152 			 * XXX - For correctness reasons.
153 			 * We must aquire these in the same order and also
154 			 * importantly, release in the same order because
155 			 * otherwise we might deadlock with the joined thread
156 			 * when we attempt to release one of these locks.
157 			 */
158 			_thread_sigblock();
159 			DEAD_LIST_LOCK;
160 			THREAD_LIST_LOCK;
161 		}
162 
163 		/*
164 		 * The thread return value and error are set by the thread we're
165 		 * joining to when it exits or detaches:
166 		 */
167 		ret = curthread->join_status.error;
168 		if ((ret == 0) && (thread_return != NULL))
169 			*thread_return = curthread->join_status.ret;
170 		THREAD_LIST_UNLOCK;
171 		DEAD_LIST_UNLOCK;
172 		_thread_sigunblock();
173 	} else {
174 		/*
175 		 * The thread exited (is dead) without being detached, and no
176 		 * thread has joined it.
177 		 */
178 
179 		/* Check if the return value is required: */
180 		if (thread_return != NULL) {
181 			/* Return the thread's return value: */
182 			*thread_return = pthread->ret;
183 		}
184 
185 		/* Free all remaining memory allocated to the thread. */
186 		pthread->attr.flags |= PTHREAD_DETACHED;
187 		PTHREAD_UNLOCK(pthread);
188 		TAILQ_REMOVE(&_dead_list, pthread, dle);
189 		deadlist_free_onethread(pthread);
190 		THREAD_LIST_UNLOCK;
191 		DEAD_LIST_UNLOCK;
192 		_thread_sigunblock();
193 	}
194 
195 out:
196 	_thread_leave_cancellation_point();
197 
198 	/* Return the completion status: */
199 	return (ret);
200 }
201