1112918Sjeff/*
2153496Sdavidxu * Copyright (c) 2005, David Xu <davidxu@freebsd.org>
3112918Sjeff * All rights reserved.
4112918Sjeff *
5112918Sjeff * Redistribution and use in source and binary forms, with or without
6112918Sjeff * modification, are permitted provided that the following conditions
7112918Sjeff * are met:
8112918Sjeff * 1. Redistributions of source code must retain the above copyright
9153496Sdavidxu *    notice unmodified, this list of conditions, and the following
10153496Sdavidxu *    disclaimer.
11112918Sjeff * 2. Redistributions in binary form must reproduce the above copyright
12112918Sjeff *    notice, this list of conditions and the following disclaimer in the
13112918Sjeff *    documentation and/or other materials provided with the distribution.
14112918Sjeff *
15153496Sdavidxu * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16153496Sdavidxu * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17153496Sdavidxu * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18153496Sdavidxu * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19153496Sdavidxu * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20153496Sdavidxu * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21153496Sdavidxu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22153496Sdavidxu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23153496Sdavidxu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24153496Sdavidxu * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25112918Sjeff *
26112918Sjeff * $FreeBSD$
27153496Sdavidxu *
28112918Sjeff */
29144518Sdavidxu
30157457Sdavidxu#include "namespace.h"
31112918Sjeff#include <errno.h>
32112918Sjeff#include <pthread.h>
33157457Sdavidxu#include "un-namespace.h"
34144518Sdavidxu
35112918Sjeff#include "thr_private.h"
36112918Sjeff
37157457Sdavidxuint	_pthread_timedjoin_np(pthread_t pthread, void **thread_return,
38157457Sdavidxu	const struct timespec *abstime);
39150901Sdavidxustatic int join_common(pthread_t, void **, const struct timespec *);
40150901Sdavidxu
41112918Sjeff__weak_reference(_pthread_join, pthread_join);
42150901Sdavidxu__weak_reference(_pthread_timedjoin_np, pthread_timedjoin_np);
43112918Sjeff
44144518Sdavidxustatic void backout_join(void *arg)
45144518Sdavidxu{
46212536Sdavidxu	struct pthread *pthread = (struct pthread *)arg;
47144518Sdavidxu	struct pthread *curthread = _get_curthread();
48144518Sdavidxu
49212536Sdavidxu	THR_THREAD_LOCK(curthread, pthread);
50144518Sdavidxu	pthread->joiner = NULL;
51212840Sdavidxu	THR_THREAD_UNLOCK(curthread, pthread);
52144518Sdavidxu}
53144518Sdavidxu
54112918Sjeffint
55112918Sjeff_pthread_join(pthread_t pthread, void **thread_return)
56112918Sjeff{
57150901Sdavidxu	return (join_common(pthread, thread_return, NULL));
58150901Sdavidxu}
59150901Sdavidxu
60150901Sdavidxuint
61150901Sdavidxu_pthread_timedjoin_np(pthread_t pthread, void **thread_return,
62150901Sdavidxu	const struct timespec *abstime)
63150901Sdavidxu{
64150901Sdavidxu	if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
65150901Sdavidxu	    abstime->tv_nsec >= 1000000000)
66150901Sdavidxu		return (EINVAL);
67150901Sdavidxu
68150901Sdavidxu	return (join_common(pthread, thread_return, abstime));
69150901Sdavidxu}
70150901Sdavidxu
71211524Sdavidxu/*
72211524Sdavidxu * Cancellation behavior:
73211524Sdavidxu *   if the thread is canceled, joinee is not recycled.
74211524Sdavidxu */
75150901Sdavidxustatic int
76150901Sdavidxujoin_common(pthread_t pthread, void **thread_return,
77150901Sdavidxu	const struct timespec *abstime)
78150901Sdavidxu{
79144518Sdavidxu	struct pthread *curthread = _get_curthread();
80150901Sdavidxu	struct timespec ts, ts2, *tsp;
81144518Sdavidxu	void *tmp;
82151694Sdavidxu	long tid;
83144518Sdavidxu	int ret = 0;
84151694Sdavidxu
85144518Sdavidxu	if (pthread == NULL)
86144518Sdavidxu		return (EINVAL);
87112918Sjeff
88129484Smtm	if (pthread == curthread)
89144518Sdavidxu		return (EDEADLK);
90112918Sjeff
91212536Sdavidxu	if ((ret = _thr_find_thread(curthread, pthread, 1)) != 0)
92212536Sdavidxu		return (ESRCH);
93212536Sdavidxu
94212536Sdavidxu	if ((pthread->flags & THR_FLAGS_DETACHED) != 0) {
95164715Sdavidxu		ret = EINVAL;
96144518Sdavidxu	} else if (pthread->joiner != NULL) {
97112918Sjeff		/* Multiple joiners are not supported. */
98112918Sjeff		ret = ENOTSUP;
99112918Sjeff	}
100144518Sdavidxu	if (ret) {
101212536Sdavidxu		THR_THREAD_UNLOCK(curthread, pthread);
102144518Sdavidxu		return (ret);
103144518Sdavidxu	}
104144518Sdavidxu	/* Set the running thread to be the joiner: */
105144518Sdavidxu	pthread->joiner = curthread;
106112918Sjeff
107212536Sdavidxu	THR_THREAD_UNLOCK(curthread, pthread);
108112918Sjeff
109144518Sdavidxu	THR_CLEANUP_PUSH(curthread, backout_join, pthread);
110212076Sdavidxu	_thr_cancel_enter(curthread);
111112918Sjeff
112151694Sdavidxu	tid = pthread->tid;
113151694Sdavidxu	while (pthread->tid != TID_TERMINATED) {
114211524Sdavidxu		_thr_testcancel(curthread);
115150901Sdavidxu		if (abstime != NULL) {
116150901Sdavidxu			clock_gettime(CLOCK_REALTIME, &ts);
117150901Sdavidxu			TIMESPEC_SUB(&ts2, abstime, &ts);
118150901Sdavidxu			if (ts2.tv_sec < 0) {
119150901Sdavidxu				ret = ETIMEDOUT;
120150901Sdavidxu				break;
121150901Sdavidxu			}
122150901Sdavidxu			tsp = &ts2;
123150901Sdavidxu		} else
124150901Sdavidxu			tsp = NULL;
125151694Sdavidxu		ret = _thr_umtx_wait(&pthread->tid, tid, tsp);
126150901Sdavidxu		if (ret == ETIMEDOUT)
127150901Sdavidxu			break;
128144518Sdavidxu	}
129129484Smtm
130212076Sdavidxu	_thr_cancel_leave(curthread, 0);
131144518Sdavidxu	THR_CLEANUP_POP(curthread, 0);
132115314Smtm
133150901Sdavidxu	if (ret == ETIMEDOUT) {
134212536Sdavidxu		THR_THREAD_LOCK(curthread, pthread);
135150901Sdavidxu		pthread->joiner = NULL;
136212536Sdavidxu		THR_THREAD_UNLOCK(curthread, pthread);
137150901Sdavidxu	} else {
138153526Sdavidxu		ret = 0;
139150901Sdavidxu		tmp = pthread->ret;
140212536Sdavidxu		THR_THREAD_LOCK(curthread, pthread);
141212536Sdavidxu		pthread->flags |= THR_FLAGS_DETACHED;
142150901Sdavidxu		pthread->joiner = NULL;
143212536Sdavidxu		_thr_try_gc(curthread, pthread); /* thread lock released */
144112918Sjeff
145150901Sdavidxu		if (thread_return != NULL)
146150901Sdavidxu			*thread_return = tmp;
147150901Sdavidxu	}
148112918Sjeff	return (ret);
149112918Sjeff}
150