15e53a4f9SPedro F. Giffuni /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
35e53a4f9SPedro F. Giffuni *
4df2cf821SDavid Xu * Copyright (c) 2005, David Xu <davidxu@freebsd.org>
5bb535300SJeff Roberson * All rights reserved.
6bb535300SJeff Roberson *
7bb535300SJeff Roberson * Redistribution and use in source and binary forms, with or without
8bb535300SJeff Roberson * modification, are permitted provided that the following conditions
9bb535300SJeff Roberson * are met:
10bb535300SJeff Roberson * 1. Redistributions of source code must retain the above copyright
11df2cf821SDavid Xu * notice unmodified, this list of conditions, and the following
12df2cf821SDavid Xu * disclaimer.
13bb535300SJeff Roberson * 2. Redistributions in binary form must reproduce the above copyright
14bb535300SJeff Roberson * notice, this list of conditions and the following disclaimer in the
15bb535300SJeff Roberson * documentation and/or other materials provided with the distribution.
16bb535300SJeff Roberson *
17df2cf821SDavid Xu * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18df2cf821SDavid Xu * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19df2cf821SDavid Xu * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20df2cf821SDavid Xu * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21df2cf821SDavid Xu * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22df2cf821SDavid Xu * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23df2cf821SDavid Xu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24df2cf821SDavid Xu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25df2cf821SDavid Xu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26df2cf821SDavid Xu * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27bb535300SJeff Roberson */
28a091d823SDavid Xu
2937a6356bSDavid Xu #include "namespace.h"
30bb535300SJeff Roberson #include <errno.h>
31bb535300SJeff Roberson #include <pthread.h>
3237a6356bSDavid Xu #include "un-namespace.h"
33a091d823SDavid Xu
34bb535300SJeff Roberson #include "thr_private.h"
35bb535300SJeff Roberson
36132fb3dcSKonstantin Belousov int _pthread_peekjoin_np(pthread_t pthread, void **thread_return);
3737a6356bSDavid Xu int _pthread_timedjoin_np(pthread_t pthread, void **thread_return,
3837a6356bSDavid Xu const struct timespec *abstime);
39132fb3dcSKonstantin Belousov static int join_common(pthread_t, void **, const struct timespec *, bool peek);
409e49a237SDavid Xu
410ab1bfc7SKonstantin Belousov __weak_reference(_thr_join, pthread_join);
420ab1bfc7SKonstantin Belousov __weak_reference(_thr_join, _pthread_join);
439e49a237SDavid Xu __weak_reference(_pthread_timedjoin_np, pthread_timedjoin_np);
44132fb3dcSKonstantin Belousov __weak_reference(_pthread_peekjoin_np, pthread_peekjoin_np);
45bb535300SJeff Roberson
backout_join(void * arg)46a091d823SDavid Xu static void backout_join(void *arg)
47a091d823SDavid Xu {
48a091d823SDavid Xu struct pthread *pthread = (struct pthread *)arg;
49a9b764e2SDavid Xu struct pthread *curthread = _get_curthread();
50a091d823SDavid Xu
51a9b764e2SDavid Xu THR_THREAD_LOCK(curthread, pthread);
52a091d823SDavid Xu pthread->joiner = NULL;
537c243121SDavid Xu THR_THREAD_UNLOCK(curthread, pthread);
54a091d823SDavid Xu }
55a091d823SDavid Xu
56bb535300SJeff Roberson int
_thr_join(pthread_t pthread,void ** thread_return)570ab1bfc7SKonstantin Belousov _thr_join(pthread_t pthread, void **thread_return)
58bb535300SJeff Roberson {
59132fb3dcSKonstantin Belousov return (join_common(pthread, thread_return, NULL, false));
609e49a237SDavid Xu }
619e49a237SDavid Xu
629e49a237SDavid Xu int
_pthread_timedjoin_np(pthread_t pthread,void ** thread_return,const struct timespec * abstime)639e49a237SDavid Xu _pthread_timedjoin_np(pthread_t pthread, void **thread_return,
649e49a237SDavid Xu const struct timespec *abstime)
659e49a237SDavid Xu {
669e49a237SDavid Xu if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
679e49a237SDavid Xu abstime->tv_nsec >= 1000000000)
689e49a237SDavid Xu return (EINVAL);
699e49a237SDavid Xu
70132fb3dcSKonstantin Belousov return (join_common(pthread, thread_return, abstime, false));
71132fb3dcSKonstantin Belousov }
72132fb3dcSKonstantin Belousov
73132fb3dcSKonstantin Belousov int
_pthread_peekjoin_np(pthread_t pthread,void ** thread_return)74132fb3dcSKonstantin Belousov _pthread_peekjoin_np(pthread_t pthread, void **thread_return)
75132fb3dcSKonstantin Belousov {
76132fb3dcSKonstantin Belousov return (join_common(pthread, thread_return, NULL, true));
779e49a237SDavid Xu }
789e49a237SDavid Xu
79635f917aSDavid Xu /*
80635f917aSDavid Xu * Cancellation behavior:
81635f917aSDavid Xu * if the thread is canceled, joinee is not recycled.
82635f917aSDavid Xu */
839e49a237SDavid Xu static int
join_common(pthread_t pthread,void ** thread_return,const struct timespec * abstime,bool peek)849e49a237SDavid Xu join_common(pthread_t pthread, void **thread_return,
85132fb3dcSKonstantin Belousov const struct timespec *abstime, bool peek)
869e49a237SDavid Xu {
87a091d823SDavid Xu struct pthread *curthread = _get_curthread();
889e49a237SDavid Xu struct timespec ts, ts2, *tsp;
89a091d823SDavid Xu void *tmp;
90d7f119abSDavid Xu long tid;
91132fb3dcSKonstantin Belousov int ret;
92bb535300SJeff Roberson
93a091d823SDavid Xu if (pthread == NULL)
94bb535300SJeff Roberson return (EINVAL);
95bb535300SJeff Roberson
964cd18a22SMike Makonnen if (pthread == curthread)
97bb535300SJeff Roberson return (EDEADLK);
98bb535300SJeff Roberson
99a9b764e2SDavid Xu if ((ret = _thr_find_thread(curthread, pthread, 1)) != 0)
100a9b764e2SDavid Xu return (ESRCH);
101a9b764e2SDavid Xu
102a9b764e2SDavid Xu if ((pthread->flags & THR_FLAGS_DETACHED) != 0) {
1036f54e829SDavid Xu ret = EINVAL;
104a091d823SDavid Xu } else if (pthread->joiner != NULL) {
105bb535300SJeff Roberson /* Multiple joiners are not supported. */
106bb535300SJeff Roberson ret = ENOTSUP;
107bb535300SJeff Roberson }
108132fb3dcSKonstantin Belousov if (ret != 0) {
109a9b764e2SDavid Xu THR_THREAD_UNLOCK(curthread, pthread);
110a091d823SDavid Xu return (ret);
111a091d823SDavid Xu }
112132fb3dcSKonstantin Belousov
113132fb3dcSKonstantin Belousov /* Only peek into status, do not gc the thread. */
114132fb3dcSKonstantin Belousov if (peek) {
115132fb3dcSKonstantin Belousov if (pthread->tid != TID_TERMINATED)
116132fb3dcSKonstantin Belousov ret = EBUSY;
117132fb3dcSKonstantin Belousov else if (thread_return != NULL)
118132fb3dcSKonstantin Belousov *thread_return = pthread->ret;
119132fb3dcSKonstantin Belousov THR_THREAD_UNLOCK(curthread, pthread);
120132fb3dcSKonstantin Belousov return (ret);
121132fb3dcSKonstantin Belousov }
122132fb3dcSKonstantin Belousov
123bb535300SJeff Roberson /* Set the running thread to be the joiner: */
124bb535300SJeff Roberson pthread->joiner = curthread;
125bb535300SJeff Roberson
126a9b764e2SDavid Xu THR_THREAD_UNLOCK(curthread, pthread);
127bb535300SJeff Roberson
128a091d823SDavid Xu THR_CLEANUP_PUSH(curthread, backout_join, pthread);
12902c3c858SDavid Xu _thr_cancel_enter(curthread);
1304cd18a22SMike Makonnen
131d7f119abSDavid Xu tid = pthread->tid;
132d7f119abSDavid Xu while (pthread->tid != TID_TERMINATED) {
133635f917aSDavid Xu _thr_testcancel(curthread);
1349e49a237SDavid Xu if (abstime != NULL) {
1359e49a237SDavid Xu clock_gettime(CLOCK_REALTIME, &ts);
1369e49a237SDavid Xu TIMESPEC_SUB(&ts2, abstime, &ts);
1379e49a237SDavid Xu if (ts2.tv_sec < 0) {
1389e49a237SDavid Xu ret = ETIMEDOUT;
1399e49a237SDavid Xu break;
1409e49a237SDavid Xu }
1419e49a237SDavid Xu tsp = &ts2;
1429e49a237SDavid Xu } else
1439e49a237SDavid Xu tsp = NULL;
144d7f119abSDavid Xu ret = _thr_umtx_wait(&pthread->tid, tid, tsp);
1459e49a237SDavid Xu if (ret == ETIMEDOUT)
1469e49a237SDavid Xu break;
147bb535300SJeff Roberson }
148bb535300SJeff Roberson
14902c3c858SDavid Xu _thr_cancel_leave(curthread, 0);
150a091d823SDavid Xu THR_CLEANUP_POP(curthread, 0);
151bb535300SJeff Roberson
1529e49a237SDavid Xu if (ret == ETIMEDOUT) {
153a9b764e2SDavid Xu THR_THREAD_LOCK(curthread, pthread);
1549e49a237SDavid Xu pthread->joiner = NULL;
155a9b764e2SDavid Xu THR_THREAD_UNLOCK(curthread, pthread);
1569e49a237SDavid Xu } else {
157597dc824SDavid Xu ret = 0;
158a091d823SDavid Xu tmp = pthread->ret;
159a9b764e2SDavid Xu THR_THREAD_LOCK(curthread, pthread);
160a9b764e2SDavid Xu pthread->flags |= THR_FLAGS_DETACHED;
1619e49a237SDavid Xu pthread->joiner = NULL;
162a9b764e2SDavid Xu _thr_try_gc(curthread, pthread); /* thread lock released */
163bb535300SJeff Roberson
164a091d823SDavid Xu if (thread_return != NULL)
165a091d823SDavid Xu *thread_return = tmp;
1669e49a237SDavid Xu }
167bb535300SJeff Roberson return (ret);
168bb535300SJeff Roberson }
169