Initial import from FreeBSD RELENG_4:
[dragonfly.git] / lib / libc_r / uthread / uthread_join.c
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: src/lib/libc_r/uthread/uthread_join.c,v 1.12.2.8 2002/10/22 14:44:03 fjoe Exp $
33  */
34 #include <errno.h>
35 #include <pthread.h>
36 #include "pthread_private.h"
37
38 __weak_reference(_pthread_join, pthread_join);
39
40 int
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 lock 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                 /* Undefer and handle pending signals, yielding if necessary: */
107                 _thread_kern_sig_undefer();
108
109                 /* Return an error: */
110                 ret = ESRCH;
111
112         } else if (pthread->joiner != NULL) {
113                 /* Undefer and handle pending signals, yielding if necessary: */
114                 _thread_kern_sig_undefer();
115
116                 /* Multiple joiners are not supported. */
117                 ret = ENOTSUP;
118
119         /* Check if the thread is not dead: */
120         } else if (pthread->state != PS_DEAD) {
121                 /* Set the running thread to be the joiner: */
122                 pthread->joiner = curthread;
123
124                 /* Keep track of which thread we're joining to: */
125                 curthread->join_status.thread = pthread;
126
127                 while (curthread->join_status.thread == pthread) {
128                         /* Schedule the next thread: */
129                         _thread_kern_sched_state(PS_JOIN, __FILE__, __LINE__);
130                 }
131
132                 /*
133                  * The thread return value and error are set by the thread we're
134                  * joining to when it exits or detaches:
135                  */
136                 ret = curthread->join_status.error;
137                 if ((ret == 0) && (thread_return != NULL))
138                         *thread_return = curthread->join_status.ret;
139         } else {
140                 /*
141                  * The thread exited (is dead) without being detached, and no
142                  * thread has joined it.
143                  */
144
145                 /* Check if the return value is required: */
146                 if (thread_return != NULL) {
147                         /* Return the thread's return value: */
148                         *thread_return = pthread->ret;
149                 }
150
151                 /* Make the thread collectable by the garbage collector. */
152                 pthread->attr.flags |= PTHREAD_DETACHED;
153
154                 /* Undefer and handle pending signals, yielding if necessary: */
155                 _thread_kern_sig_undefer();
156         }
157
158         _thread_leave_cancellation_point();
159
160         /* Return the completion status: */
161         return (ret);
162 }