Merge branch 'vendor/OPENRESOLV'
[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 int
39 _pthread_join(pthread_t pthread, void **thread_return)
40 {
41         struct pthread  *curthread = _get_curthread();
42         int ret = 0;
43         pthread_t thread;
44  
45         _thread_enter_cancellation_point();
46
47         /* Check if the caller has specified an invalid thread: */
48         if (pthread == NULL || pthread->magic != PTHREAD_MAGIC) {
49                 /* Invalid thread: */
50                 _thread_leave_cancellation_point();
51                 return(EINVAL);
52         }
53
54         /* Check if the caller has specified itself: */
55         if (pthread == curthread) {
56                 /* Avoid a deadlock condition: */
57                 _thread_leave_cancellation_point();
58                 return(EDEADLK);
59         }
60
61         /*
62          * Lock the garbage collector mutex to ensure that the garbage
63          * collector is not using the dead thread list.
64          */
65         if (pthread_mutex_lock(&_gc_mutex) != 0)
66                 PANIC("Cannot lock gc mutex");
67
68         /*
69          * Defer signals to protect the thread list from access
70          * by the signal handler:
71          */
72         _thread_kern_sig_defer();
73
74         /*
75          * Unlock the garbage collector mutex, now that the garbage collector
76          * can't be run:
77          */
78         if (pthread_mutex_unlock(&_gc_mutex) != 0)
79                 PANIC("Cannot lock gc mutex");
80
81         /*
82          * Search for the specified thread in the list of active threads.  This
83          * is done manually here rather than calling _find_thread() because
84          * the searches in _thread_list and _dead_list (as well as setting up
85          * join/detach state) have to be done atomically.
86          */
87         TAILQ_FOREACH(thread, &_thread_list, tle) {
88                 if (thread == pthread)
89                         break;
90         }
91         if (thread == NULL) {
92                 /*
93                  * Search for the specified thread in the list of dead threads:
94                  */
95                 TAILQ_FOREACH(thread, &_dead_list, dle) {
96                         if (thread == pthread)
97                                 break;
98                 }
99         }
100
101         /* Check if the thread was not found or has been detached: */
102         if (thread == NULL) {
103                 /* Undefer and handle pending signals, yielding if necessary: */
104                 _thread_kern_sig_undefer();
105
106                 /* Thread not found. */
107                 ret = ESRCH;
108
109         } else if ((pthread->attr.flags & PTHREAD_DETACHED) != 0) {
110                 /* Undefer and handle pending signals, yielding if necessary: */
111                 _thread_kern_sig_undefer();
112
113                 /* Thread is in detached state. */
114                 ret = EINVAL;
115
116         } else if (pthread->joiner != NULL) {
117                 /* Undefer and handle pending signals, yielding if necessary: */
118                 _thread_kern_sig_undefer();
119
120                 /* Multiple joiners are not supported. */
121                 ret = ENOTSUP;
122
123         /* Check if the thread is not dead: */
124         } else if (pthread->state != PS_DEAD) {
125                 /* Set the running thread to be the joiner: */
126                 pthread->joiner = curthread;
127
128                 /* Keep track of which thread we're joining to: */
129                 curthread->join_status.thread = pthread;
130
131                 while (curthread->join_status.thread == pthread) {
132                         /* Schedule the next thread: */
133                         _thread_kern_sched_state(PS_JOIN, __FILE__, __LINE__);
134                 }
135
136                 /*
137                  * The thread return value and error are set by the thread we're
138                  * joining to when it exits or detaches:
139                  */
140                 ret = curthread->join_status.error;
141                 if ((ret == 0) && (thread_return != NULL))
142                         *thread_return = curthread->join_status.ret;
143         } else {
144                 /*
145                  * The thread exited (is dead) without being detached, and no
146                  * thread has joined it.
147                  */
148
149                 /* Check if the return value is required: */
150                 if (thread_return != NULL) {
151                         /* Return the thread's return value: */
152                         *thread_return = pthread->ret;
153                 }
154
155                 /* Make the thread collectable by the garbage collector. */
156                 pthread->attr.flags |= PTHREAD_DETACHED;
157
158                 /* Undefer and handle pending signals, yielding if necessary: */
159                 _thread_kern_sig_undefer();
160         }
161
162         _thread_leave_cancellation_point();
163
164         /* Return the completion status: */
165         return (ret);
166 }
167
168 __strong_reference(_pthread_join, pthread_join);