1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2005, David Xu <davidxu@freebsd.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice unmodified, this list of conditions, and the following 12 * disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include "namespace.h" 33 #include <errno.h> 34 #include <pthread.h> 35 #include "un-namespace.h" 36 37 #include "thr_private.h" 38 39 int _pthread_peekjoin_np(pthread_t pthread, void **thread_return); 40 int _pthread_timedjoin_np(pthread_t pthread, void **thread_return, 41 const struct timespec *abstime); 42 static int join_common(pthread_t, void **, const struct timespec *, bool peek); 43 44 __weak_reference(_thr_join, pthread_join); 45 __weak_reference(_thr_join, _pthread_join); 46 __weak_reference(_pthread_timedjoin_np, pthread_timedjoin_np); 47 __weak_reference(_pthread_peekjoin_np, pthread_peekjoin_np); 48 49 static void backout_join(void *arg) 50 { 51 struct pthread *pthread = (struct pthread *)arg; 52 struct pthread *curthread = _get_curthread(); 53 54 THR_THREAD_LOCK(curthread, pthread); 55 pthread->joiner = NULL; 56 THR_THREAD_UNLOCK(curthread, pthread); 57 } 58 59 int 60 _thr_join(pthread_t pthread, void **thread_return) 61 { 62 return (join_common(pthread, thread_return, NULL, false)); 63 } 64 65 int 66 _pthread_timedjoin_np(pthread_t pthread, void **thread_return, 67 const struct timespec *abstime) 68 { 69 if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || 70 abstime->tv_nsec >= 1000000000) 71 return (EINVAL); 72 73 return (join_common(pthread, thread_return, abstime, false)); 74 } 75 76 int 77 _pthread_peekjoin_np(pthread_t pthread, void **thread_return) 78 { 79 return (join_common(pthread, thread_return, NULL, true)); 80 } 81 82 /* 83 * Cancellation behavior: 84 * if the thread is canceled, joinee is not recycled. 85 */ 86 static int 87 join_common(pthread_t pthread, void **thread_return, 88 const struct timespec *abstime, bool peek) 89 { 90 struct pthread *curthread = _get_curthread(); 91 struct timespec ts, ts2, *tsp; 92 void *tmp; 93 long tid; 94 int ret; 95 96 if (pthread == NULL) 97 return (EINVAL); 98 99 if (pthread == curthread) 100 return (EDEADLK); 101 102 if ((ret = _thr_find_thread(curthread, pthread, 1)) != 0) 103 return (ESRCH); 104 105 if ((pthread->flags & THR_FLAGS_DETACHED) != 0) { 106 ret = EINVAL; 107 } else if (pthread->joiner != NULL) { 108 /* Multiple joiners are not supported. */ 109 ret = ENOTSUP; 110 } 111 if (ret != 0) { 112 THR_THREAD_UNLOCK(curthread, pthread); 113 return (ret); 114 } 115 116 /* Only peek into status, do not gc the thread. */ 117 if (peek) { 118 if (pthread->tid != TID_TERMINATED) 119 ret = EBUSY; 120 else if (thread_return != NULL) 121 *thread_return = pthread->ret; 122 THR_THREAD_UNLOCK(curthread, pthread); 123 return (ret); 124 } 125 126 /* Set the running thread to be the joiner: */ 127 pthread->joiner = curthread; 128 129 THR_THREAD_UNLOCK(curthread, pthread); 130 131 THR_CLEANUP_PUSH(curthread, backout_join, pthread); 132 _thr_cancel_enter(curthread); 133 134 tid = pthread->tid; 135 while (pthread->tid != TID_TERMINATED) { 136 _thr_testcancel(curthread); 137 if (abstime != NULL) { 138 clock_gettime(CLOCK_REALTIME, &ts); 139 TIMESPEC_SUB(&ts2, abstime, &ts); 140 if (ts2.tv_sec < 0) { 141 ret = ETIMEDOUT; 142 break; 143 } 144 tsp = &ts2; 145 } else 146 tsp = NULL; 147 ret = _thr_umtx_wait(&pthread->tid, tid, tsp); 148 if (ret == ETIMEDOUT) 149 break; 150 } 151 152 _thr_cancel_leave(curthread, 0); 153 THR_CLEANUP_POP(curthread, 0); 154 155 if (ret == ETIMEDOUT) { 156 THR_THREAD_LOCK(curthread, pthread); 157 pthread->joiner = NULL; 158 THR_THREAD_UNLOCK(curthread, pthread); 159 } else { 160 ret = 0; 161 tmp = pthread->ret; 162 THR_THREAD_LOCK(curthread, pthread); 163 pthread->flags |= THR_FLAGS_DETACHED; 164 pthread->joiner = NULL; 165 _thr_try_gc(curthread, pthread); /* thread lock released */ 166 167 if (thread_return != NULL) 168 *thread_return = tmp; 169 } 170 return (ret); 171 } 172