1 /*
2  * Copyright (c) 2005, David Xu <davidxu@freebsd.org>
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  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include "namespace.h"
27 #include <machine/tls.h>
28 #include <errno.h>
29 #include <pthread.h>
30 #include "un-namespace.h"
31 
32 #include "thr_private.h"
33 
34 int _pthread_timedjoin_np(pthread_t pthread, void **thread_return,
35 	const struct timespec *abstime);
36 
37 static int join_common(pthread_t, void **, const struct timespec *);
38 
39 static void backout_join(void *arg)
40 {
41 	struct pthread *curthread = tls_get_curthread();
42 	struct pthread *pthread = (struct pthread *)arg;
43 
44 	THREAD_LIST_LOCK(curthread);
45 	pthread->joiner = NULL;
46 	THREAD_LIST_UNLOCK(curthread);
47 }
48 
49 int
50 _pthread_join(pthread_t pthread, void **thread_return)
51 {
52 	return (join_common(pthread, thread_return, NULL));
53 }
54 
55 int
56 _pthread_timedjoin_np(pthread_t pthread, void **thread_return,
57 	const struct timespec *abstime)
58 {
59 	if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
60 	    abstime->tv_nsec >= 1000000000)
61 		return (EINVAL);
62 
63 	return (join_common(pthread, thread_return, abstime));
64 }
65 
66 static int
67 join_common(pthread_t pthread, void **thread_return,
68 	const struct timespec *abstime)
69 {
70 	struct pthread *curthread = tls_get_curthread();
71 	struct timespec ts, ts2, *tsp;
72 	void *tmp;
73 	long state;
74 	int oldcancel;
75 	int ret = 0;
76 
77 	if (pthread == NULL)
78 		return (EINVAL);
79 
80 	if (pthread == curthread)
81 		return (EDEADLK);
82 
83 	THREAD_LIST_LOCK(curthread);
84 	if ((ret = _thr_find_thread(curthread, pthread, 1)) != 0) {
85 		ret = ESRCH;
86 	} else if ((pthread->tlflags & TLFLAGS_DETACHED) != 0) {
87 		ret = EINVAL;
88 	} else if (pthread->joiner != NULL) {
89 		/* Multiple joiners are not supported. */
90 		ret = ENOTSUP;
91 	}
92 	if (ret) {
93 		THREAD_LIST_UNLOCK(curthread);
94 		return (ret);
95 	}
96 	/* Set the running thread to be the joiner: */
97 	pthread->joiner = curthread;
98 
99 	THREAD_LIST_UNLOCK(curthread);
100 
101 	THR_CLEANUP_PUSH(curthread, backout_join, pthread);
102 	oldcancel = _thr_cancel_enter(curthread);
103 
104 	while ((state = pthread->state) != PS_DEAD) {
105 		if (abstime != NULL) {
106 			clock_gettime(CLOCK_REALTIME, &ts);
107 			TIMESPEC_SUB(&ts2, abstime, &ts);
108 			if (ts2.tv_sec < 0) {
109 				ret = ETIMEDOUT;
110 				break;
111 			}
112 			tsp = &ts2;
113 		} else
114 			tsp = NULL;
115 		ret = _thr_umtx_wait(&pthread->state, state, tsp,
116 			 CLOCK_REALTIME);
117 		if (ret == ETIMEDOUT)
118 			break;
119 	}
120 
121 	_thr_cancel_leave(curthread, oldcancel);
122 	THR_CLEANUP_POP(curthread, 0);
123 
124 	if (ret == ETIMEDOUT) {
125 		THREAD_LIST_LOCK(curthread);
126 		pthread->joiner = NULL;
127 		THREAD_LIST_UNLOCK(curthread);
128 	} else {
129 		ret = 0;
130 		tmp = pthread->ret;
131 		THREAD_LIST_LOCK(curthread);
132 		pthread->tlflags |= TLFLAGS_DETACHED;
133 		pthread->joiner = NULL;
134 		THR_GCLIST_ADD(pthread);
135 		THREAD_LIST_UNLOCK(curthread);
136 
137 		if (thread_return != NULL)
138 			*thread_return = tmp;
139 	}
140 	return (ret);
141 }
142 
143 __strong_reference(_pthread_join, pthread_join);
144 __strong_reference(_pthread_timedjoin_np, pthread_timedjoin_np);
145