thr_join.c revision 164715
1/*
2 * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>.
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 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by John Birrell.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * $FreeBSD: head/lib/libkse/thread/thr_join.c 164715 2006-11-28 11:05:31Z davidxu $
33 */
34#include <errno.h>
35#include <pthread.h>
36#include "thr_private.h"
37
38LT10_COMPAT_PRIVATE(_pthread_join);
39LT10_COMPAT_DEFAULT(pthread_join);
40
41__weak_reference(_pthread_join, pthread_join);
42
43int
44_pthread_join(pthread_t pthread, void **thread_return)
45{
46	struct pthread *curthread = _get_curthread();
47	void *tmp;
48	kse_critical_t crit;
49	int ret = 0;
50
51	_thr_cancel_enter(curthread);
52
53	/* Check if the caller has specified an invalid thread: */
54	if (pthread == NULL || pthread->magic != THR_MAGIC) {
55		/* Invalid thread: */
56		_thr_cancel_leave(curthread, 1);
57		return (EINVAL);
58	}
59
60	/* Check if the caller has specified itself: */
61	if (pthread == curthread) {
62		/* Avoid a deadlock condition: */
63		_thr_cancel_leave(curthread, 1);
64		return (EDEADLK);
65	}
66
67	/*
68	 * Find the thread in the list of active threads or in the
69	 * list of dead threads:
70	 */
71	if ((ret = _thr_ref_add(curthread, pthread, /*include dead*/1)) != 0) {
72		/* Return an error: */
73		_thr_cancel_leave(curthread, 1);
74		return (ESRCH);
75	}
76
77	THR_SCHED_LOCK(curthread, pthread);
78	/* Check if this thread has been detached: */
79	if ((pthread->attr.flags & PTHREAD_DETACHED) != 0) {
80		THR_SCHED_UNLOCK(curthread, pthread);
81		/* Remove the reference and return an error: */
82		_thr_ref_delete(curthread, pthread);
83		ret = EINVAL;
84	} else {
85		/* Lock the target thread while checking its state. */
86		if (pthread->state == PS_DEAD) {
87			/* Return the thread's return value: */
88			tmp = pthread->ret;
89
90			/* Detach the thread. */
91			pthread->attr.flags |= PTHREAD_DETACHED;
92
93			/* Unlock the thread. */
94			THR_SCHED_UNLOCK(curthread, pthread);
95
96			/*
97			 * Remove the thread from the list of active
98			 * threads and add it to the GC list.
99			 */
100			crit = _kse_critical_enter();
101			KSE_LOCK_ACQUIRE(curthread->kse, &_thread_list_lock);
102			THR_LIST_REMOVE(pthread);
103			THR_GCLIST_ADD(pthread);
104			KSE_LOCK_RELEASE(curthread->kse, &_thread_list_lock);
105			_kse_critical_leave(crit);
106
107			/* Remove the reference. */
108			_thr_ref_delete(curthread, pthread);
109			if (thread_return != NULL)
110				*thread_return = tmp;
111		}
112		else if (pthread->joiner != NULL) {
113			/* Unlock the thread and remove the reference. */
114			THR_SCHED_UNLOCK(curthread, pthread);
115			_thr_ref_delete(curthread, pthread);
116
117			/* Multiple joiners are not supported. */
118			ret = ENOTSUP;
119		}
120		else {
121			/* Set the running thread to be the joiner: */
122			pthread->joiner = curthread;
123
124			/* Keep track of which thread we're joining to: */
125			curthread->join_status.thread = pthread;
126
127			/* Unlock the thread and remove the reference. */
128			THR_SCHED_UNLOCK(curthread, pthread);
129			_thr_ref_delete(curthread, pthread);
130
131			THR_SCHED_LOCK(curthread, curthread);
132			while (curthread->join_status.thread == pthread) {
133				THR_SET_STATE(curthread, PS_JOIN);
134				THR_SCHED_UNLOCK(curthread, curthread);
135				/* Schedule the next thread: */
136				_thr_sched_switch(curthread);
137				THR_SCHED_LOCK(curthread, curthread);
138			}
139			THR_SCHED_UNLOCK(curthread, curthread);
140
141			if ((curthread->cancelflags & THR_CANCELLING) &&
142			   !(curthread->cancelflags & PTHREAD_CANCEL_DISABLE)) {
143				if (_thr_ref_add(curthread, pthread, 1) == 0) {
144					THR_SCHED_LOCK(curthread, pthread);
145					pthread->joiner = NULL;
146					THR_SCHED_UNLOCK(curthread, pthread);
147					_thr_ref_delete(curthread, pthread);
148				}
149				pthread_exit(PTHREAD_CANCELED);
150			}
151
152			/*
153			 * The thread return value and error are set by the
154			 * thread we're joining to when it exits or detaches:
155			 */
156			ret = curthread->join_status.error;
157			if ((ret == 0) && (thread_return != NULL))
158				*thread_return = curthread->join_status.ret;
159		}
160	}
161	_thr_cancel_leave(curthread, 1);
162
163	/* Return the completion status: */
164	return (ret);
165}
166