1 /*	$OpenBSD: uthread_join.c,v 1.11 2003/12/31 21:11:45 marc Exp $	*/
2 /*
3  * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgement:
16  *	This product includes software developed by John Birrell.
17  * 4. Neither the name of the author nor the names of any co-contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * $FreeBSD: uthread_join.c,v 1.9 1999/08/28 00:03:37 peter Exp $
34  */
35 #include <errno.h>
36 #ifdef _THREAD_SAFE
37 #include <pthread.h>
38 #include "pthread_private.h"
39 
40 int
pthread_join(pthread_t pthread,void ** thread_return)41 pthread_join(pthread_t pthread, void **thread_return)
42 {
43 	struct pthread	*curthread = _get_curthread();
44 	int ret = 0;
45 	pthread_t thread;
46 
47 	_thread_enter_cancellation_point();
48 
49 	/* Check if the caller has specified an invalid thread: */
50 	if (pthread == NULL || pthread->magic != PTHREAD_MAGIC) {
51 		/* Invalid thread: */
52 		_thread_leave_cancellation_point();
53 		return (EINVAL);
54 	}
55 
56 	/* Check if the caller has specified itself: */
57 	if (pthread == curthread) {
58 		/* Avoid a deadlock condition: */
59 		_thread_leave_cancellation_point();
60 		return (EDEADLK);
61 	}
62 
63 	/*
64 	 * Lock the garbage collector mutex to ensure that the garbage
65 	 * collector is not using the dead thread list.
66 	 */
67 	if (pthread_mutex_lock(&_gc_mutex) != 0)
68 		PANIC("Cannot lock gc mutex");
69 
70 	/*
71 	 * Defer signals to protect the thread list from access
72 	 * by the signal handler:
73 	 */
74 	_thread_kern_sig_defer();
75 
76 	/*
77 	 * Unlock the garbage collector mutex, now that the garbage collector
78 	 * can't be run:
79 	 */
80 	if (pthread_mutex_unlock(&_gc_mutex) != 0)
81 		PANIC("Cannot unlock gc mutex");
82 
83 	/*
84 	 * Search for the specified thread in the list of active threads.  This
85 	 * is done manually here rather than calling _find_thread() because
86 	 * the searches in _thread_list and _dead_list (as well as setting up
87 	 * join/detach state) have to be done atomically.
88 	 */
89 	TAILQ_FOREACH(thread, &_thread_list, tle) {
90 		if (thread == pthread)
91 			break;
92 	}
93 	if (thread == NULL) {
94 		/*
95 		 * Search for the specified thread in the list of dead threads:
96 		 */
97 		TAILQ_FOREACH(thread, &_dead_list, dle) {
98 			if (thread == pthread)
99 				break;
100 		}
101 	}
102 
103 	/* Check if the thread was not found or has been detached: */
104 	if (thread == NULL ||
105 	    ((pthread->attr.flags & PTHREAD_DETACHED) != 0))
106 		/* Return an error: */
107 		ret = ESRCH;
108 
109 	else if (pthread->joiner != NULL)
110 		/* Multiple joiners are not supported. */
111 		ret = ENOTSUP;
112 
113 	/* Check if the thread is not dead: */
114 	else if (pthread->state != PS_DEAD) {
115 		/* Set the running thread to be the joiner: */
116 		pthread->joiner = curthread;
117 
118 		/* Keep track of which thread we're joining to: */
119 		curthread->join_status.thread = pthread;
120 
121 		while (curthread->join_status.thread == pthread) {
122 			/* Schedule the next thread: */
123 			_thread_kern_sched_state(PS_JOIN, __FILE__, __LINE__);
124 		}
125 
126 		/*
127 		 * The thread return value and error are set by the thread we're
128 		 * joining to when it exits or detaches:
129 		 */
130 		ret = curthread->join_status.error;
131 		if ((ret == 0) && (thread_return != NULL))
132 			*thread_return = curthread->join_status.ret;
133 	} else {
134 		/*
135 		 * The thread exited (is dead) without being detached, and no
136 		 * thread has joined it.
137 		 */
138 
139 		/* Check if the return value is required: */
140 		if (thread_return != NULL) {
141 			/* Return the thread's return value: */
142 			*thread_return = pthread->ret;
143 		}
144 
145 		/* Make the thread collectable by the garbage collector. */
146 		pthread->attr.flags |= PTHREAD_DETACHED;
147 
148 	}
149 	/* Undefer and handle pending signals, yielding if necessary: */
150 	_thread_kern_sig_undefer();
151 
152 	_thread_leave_cancellation_point();
153 
154 	/* Return the completion status: */
155 	return (ret);
156 }
157 #endif
158