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