xref: /freebsd/lib/libthr/thread/thr_join.c (revision f8bbbce458194ff4312c610d32a64ff4a3a71d45)
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