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