xref: /freebsd/lib/libthr/thread/thr_join.c (revision f51e58036ebe3a3e75527325e659d7ba02b129ed)
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 "thr_private.h"
37 
38 __weak_reference(_pthread_join, pthread_join);
39 
40 int
41 _pthread_join(pthread_t pthread, void **thread_return)
42 {
43 	int ret = 0;
44 	pthread_t thread;
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 	 * Search for the specified thread in the list of active threads.  This
64 	 * is done manually here rather than calling _find_thread() because
65 	 * the searches in _thread_list and _dead_list (as well as setting up
66 	 * join/detach state) have to be done atomically.
67 	 */
68 	DEAD_LIST_LOCK;
69 	THREAD_LIST_LOCK;
70 	TAILQ_FOREACH(thread, &_thread_list, tle)
71 		if (thread == pthread) {
72 			_SPINLOCK(&pthread->lock);
73 			break;
74 		}
75 
76 	if (thread == NULL)
77 		/*
78 		 * Search for the specified thread in the list of dead threads:
79 		 */
80 		TAILQ_FOREACH(thread, &_dead_list, dle)
81 			if (thread == pthread) {
82 				_SPINLOCK(&pthread->lock);
83 				break;
84 			}
85 
86 	/* Check if the thread was not found or has been detached: */
87 	if (thread == NULL ||
88 	    ((pthread->attr.flags & PTHREAD_DETACHED) != 0)) {
89 		if (thread != NULL)
90 			_SPINUNLOCK(&pthread->lock);
91 		THREAD_LIST_UNLOCK;
92 		DEAD_LIST_UNLOCK;
93 		ret = ESRCH;
94 		goto out;
95 
96 	}
97 	if (pthread->joiner != NULL) {
98 		/* Multiple joiners are not supported. */
99 		/* XXXTHR - support multiple joiners. */
100 		_SPINUNLOCK(&pthread->lock);
101 		THREAD_LIST_UNLOCK;
102 		DEAD_LIST_UNLOCK;
103 		ret = ENOTSUP;
104 		goto out;
105 
106 	}
107 
108 	/* Check if the thread is not dead: */
109 	if (pthread->state != PS_DEAD) {
110 		/* Set the running thread to be the joiner: */
111 		pthread->joiner = curthread;
112 		_SPINUNLOCK(&pthread->lock);
113 		_thread_critical_enter(curthread);
114 
115 		/* Keep track of which thread we're joining to: */
116 		curthread->join_status.thread = pthread;
117 
118 		while (curthread->join_status.thread == pthread) {
119 			PTHREAD_SET_STATE(curthread, PS_JOIN);
120 			/* Wait for our signal to wake up. */
121 			_thread_critical_exit(curthread);
122 			THREAD_LIST_UNLOCK;
123 			DEAD_LIST_UNLOCK;
124 			_thread_suspend(curthread, NULL);
125 
126 			/*
127 			 * XXX - For correctness reasons.
128 			 * We must aquire these in the same order and also
129 			 * importantly, release in the same order, order because
130 			 * otherwise we might deadlock with the joined thread
131 			 * when we attempt to release one of these locks.
132 			 */
133 			DEAD_LIST_LOCK;
134 			THREAD_LIST_LOCK;
135 			_thread_critical_enter(curthread);
136 		}
137 
138 		/*
139 		 * The thread return value and error are set by the thread we're
140 		 * joining to when it exits or detaches:
141 		 */
142 		ret = curthread->join_status.error;
143 		if ((ret == 0) && (thread_return != NULL))
144 			*thread_return = curthread->join_status.ret;
145 		_thread_critical_exit(curthread);
146 		THREAD_LIST_UNLOCK;
147 		DEAD_LIST_UNLOCK;
148 	} else {
149 		/*
150 		 * The thread exited (is dead) without being detached, and no
151 		 * thread has joined it.
152 		 */
153 
154 		/* Check if the return value is required: */
155 		if (thread_return != NULL) {
156 			/* Return the thread's return value: */
157 			*thread_return = pthread->ret;
158 		}
159 
160 		/* Make the thread collectable by the garbage collector. */
161 		pthread->attr.flags |= PTHREAD_DETACHED;
162 		_SPINUNLOCK(&pthread->lock);
163 		THREAD_LIST_UNLOCK;
164 		if (pthread_cond_signal(&_gc_cond) != 0)
165 			PANIC("Cannot signal gc cond");
166 		DEAD_LIST_UNLOCK;
167 	}
168 
169 out:
170 	_thread_leave_cancellation_point();
171 
172 	/* Return the completion status: */
173 	return (ret);
174 }
175