xref: /netbsd-src/lib/libpthread/pthread.c (revision f782e99583eb44bde7880871a39a6817f8c942db)
1*f782e995Sdrochner /*	$NetBSD: pthread.c,v 1.48 2006/04/24 18:39:36 drochner Exp $	*/
2c62a74e6Sthorpej 
3c62a74e6Sthorpej /*-
4ec2c1698Snathanw  * Copyright (c) 2001,2002,2003 The NetBSD Foundation, Inc.
5c62a74e6Sthorpej  * All rights reserved.
6c62a74e6Sthorpej  *
7c62a74e6Sthorpej  * This code is derived from software contributed to The NetBSD Foundation
8c62a74e6Sthorpej  * by Nathan J. Williams.
9c62a74e6Sthorpej  *
10c62a74e6Sthorpej  * Redistribution and use in source and binary forms, with or without
11c62a74e6Sthorpej  * modification, are permitted provided that the following conditions
12c62a74e6Sthorpej  * are met:
13c62a74e6Sthorpej  * 1. Redistributions of source code must retain the above copyright
14c62a74e6Sthorpej  *    notice, this list of conditions and the following disclaimer.
15c62a74e6Sthorpej  * 2. Redistributions in binary form must reproduce the above copyright
16c62a74e6Sthorpej  *    notice, this list of conditions and the following disclaimer in the
17c62a74e6Sthorpej  *    documentation and/or other materials provided with the distribution.
18c62a74e6Sthorpej  * 3. All advertising materials mentioning features or use of this software
19c62a74e6Sthorpej  *    must display the following acknowledgement:
20c62a74e6Sthorpej  *        This product includes software developed by the NetBSD
21c62a74e6Sthorpej  *        Foundation, Inc. and its contributors.
22c62a74e6Sthorpej  * 4. Neither the name of The NetBSD Foundation nor the names of its
23c62a74e6Sthorpej  *    contributors may be used to endorse or promote products derived
24c62a74e6Sthorpej  *    from this software without specific prior written permission.
25c62a74e6Sthorpej  *
26c62a74e6Sthorpej  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27c62a74e6Sthorpej  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28c62a74e6Sthorpej  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29c62a74e6Sthorpej  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30c62a74e6Sthorpej  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31c62a74e6Sthorpej  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32c62a74e6Sthorpej  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33c62a74e6Sthorpej  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34c62a74e6Sthorpej  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35c62a74e6Sthorpej  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36c62a74e6Sthorpej  * POSSIBILITY OF SUCH DAMAGE.
37c62a74e6Sthorpej  */
38c62a74e6Sthorpej 
39f043c0fbSlukem #include <sys/cdefs.h>
40*f782e995Sdrochner __RCSID("$NetBSD: pthread.c,v 1.48 2006/04/24 18:39:36 drochner Exp $");
41f043c0fbSlukem 
42c62a74e6Sthorpej #include <err.h>
43c62a74e6Sthorpej #include <errno.h>
44c62a74e6Sthorpej #include <lwp.h>
45c62a74e6Sthorpej #include <signal.h>
468bcff70bSnathanw #include <stdio.h>
47c62a74e6Sthorpej #include <stdlib.h>
48c62a74e6Sthorpej #include <string.h>
490172694eSnathanw #include <syslog.h>
50c62a74e6Sthorpej #include <ucontext.h>
518bcff70bSnathanw #include <unistd.h>
52f2f10664Scl #include <sys/param.h>
53f2f10664Scl #include <sys/sysctl.h>
54cca94056Schristos #ifdef PTHREAD_MLOCK_KLUDGE
55cca94056Schristos #include <sys/mman.h>
56cca94056Schristos #endif
578bcff70bSnathanw 
58c62a74e6Sthorpej #include <sched.h>
59c62a74e6Sthorpej #include "pthread.h"
60c62a74e6Sthorpej #include "pthread_int.h"
61c62a74e6Sthorpej 
62c62a74e6Sthorpej #ifdef PTHREAD_MAIN_DEBUG
63c62a74e6Sthorpej #define SDPRINTF(x) DPRINTF(x)
64c62a74e6Sthorpej #else
65c62a74e6Sthorpej #define SDPRINTF(x)
66c62a74e6Sthorpej #endif
67c62a74e6Sthorpej 
68c62a74e6Sthorpej static void	pthread__create_tramp(void *(*start)(void *), void *arg);
696b2b9c62Syamt static void	pthread__dead(pthread_t, pthread_t);
70c62a74e6Sthorpej 
71c62a74e6Sthorpej int pthread__started;
72c62a74e6Sthorpej 
7394a458ceSchs pthread_spin_t pthread__allqueue_lock = __SIMPLELOCK_UNLOCKED;
74c62a74e6Sthorpej struct pthread_queue_t pthread__allqueue;
75c62a74e6Sthorpej 
7694a458ceSchs pthread_spin_t pthread__deadqueue_lock = __SIMPLELOCK_UNLOCKED;
77c62a74e6Sthorpej struct pthread_queue_t pthread__deadqueue;
78f2f10664Scl struct pthread_queue_t *pthread__reidlequeue;
79c62a74e6Sthorpej 
80c62a74e6Sthorpej static int nthreads;
81c62a74e6Sthorpej static int nextthread;
8294a458ceSchs static pthread_spin_t nextthread_lock = __SIMPLELOCK_UNLOCKED;
83c62a74e6Sthorpej static pthread_attr_t pthread_default_attr;
84c62a74e6Sthorpej 
850172694eSnathanw enum {
860172694eSnathanw 	DIAGASSERT_ABORT =	1<<0,
870172694eSnathanw 	DIAGASSERT_STDERR =	1<<1,
880172694eSnathanw 	DIAGASSERT_SYSLOG =	1<<2
890172694eSnathanw };
90df277271Snathanw 
910172694eSnathanw static int pthread__diagassert = DIAGASSERT_ABORT | DIAGASSERT_STDERR;
92df277271Snathanw 
9394a458ceSchs pthread_spin_t pthread__runqueue_lock = __SIMPLELOCK_UNLOCKED;
94c62a74e6Sthorpej struct pthread_queue_t pthread__runqueue;
95c62a74e6Sthorpej struct pthread_queue_t pthread__idlequeue;
9638b1c6f4Schristos struct pthread_queue_t pthread__suspqueue;
97c62a74e6Sthorpej 
98f2f10664Scl int pthread__concurrency, pthread__maxconcurrency;
99f2f10664Scl 
100*f782e995Sdrochner int _sys___sigprocmask14(int, const sigset_t *, sigset_t *);
101*f782e995Sdrochner 
102c62a74e6Sthorpej __strong_alias(__libc_thr_self,pthread_self)
1037dc01dbfSthorpej __strong_alias(__libc_thr_create,pthread_create)
1047dc01dbfSthorpej __strong_alias(__libc_thr_exit,pthread_exit)
105c62a74e6Sthorpej __strong_alias(__libc_thr_errno,pthread__errno)
1069e5c8705Snathanw __strong_alias(__libc_thr_setcancelstate,pthread_setcancelstate)
107c62a74e6Sthorpej 
108c62a74e6Sthorpej /*
109c62a74e6Sthorpej  * Static library kludge.  Place a reference to a symbol any library
110c62a74e6Sthorpej  * file which does not already have a reference here.
111c62a74e6Sthorpej  */
112c62a74e6Sthorpej extern int pthread__cancel_stub_binder;
113c62a74e6Sthorpej extern int pthread__sched_binder;
1148bcff70bSnathanw extern struct pthread_queue_t pthread__nanosleeping;
115c62a74e6Sthorpej 
116c62a74e6Sthorpej void *pthread__static_lib_binder[] = {
117c62a74e6Sthorpej 	&pthread__cancel_stub_binder,
118c62a74e6Sthorpej 	pthread_cond_init,
119c62a74e6Sthorpej 	pthread_mutex_init,
120c62a74e6Sthorpej 	pthread_rwlock_init,
121c62a74e6Sthorpej 	pthread_barrier_init,
122c62a74e6Sthorpej 	pthread_key_create,
123bd9a18b7Snathanw 	pthread_setspecific,
124c62a74e6Sthorpej 	&pthread__sched_binder,
1258bcff70bSnathanw 	&pthread__nanosleeping
126c62a74e6Sthorpej };
127c62a74e6Sthorpej 
128c62a74e6Sthorpej /*
129c62a74e6Sthorpej  * This needs to be started by the library loading code, before main()
130c62a74e6Sthorpej  * gets to run, for various things that use the state of the initial thread
131c62a74e6Sthorpej  * to work properly (thread-specific data is an application-visible example;
132c62a74e6Sthorpej  * spinlock counts for mutexes is an internal example).
133c62a74e6Sthorpej  */
134c62a74e6Sthorpej void
135c62a74e6Sthorpej pthread_init(void)
136c62a74e6Sthorpej {
137c62a74e6Sthorpej 	pthread_t first;
1380172694eSnathanw 	char *p;
139f2f10664Scl 	int i, mib[2], ncpu;
140f2f10664Scl 	size_t len;
141c62a74e6Sthorpej 	extern int __isthreaded;
142cca94056Schristos #ifdef PTHREAD_MLOCK_KLUDGE
143cca94056Schristos 	int ret;
144cca94056Schristos #endif
145c62a74e6Sthorpej 
146f2f10664Scl 	mib[0] = CTL_HW;
147f2f10664Scl 	mib[1] = HW_NCPU;
148f2f10664Scl 
149f2f10664Scl 	len = sizeof(ncpu);
150f2f10664Scl 	sysctl(mib, 2, &ncpu, &len, NULL, 0);
151f2f10664Scl 
152c62a74e6Sthorpej 	/* Initialize locks first; they're needed elsewhere. */
153f2f10664Scl 	pthread__lockprim_init(ncpu);
154f2f10664Scl 
155f2f10664Scl 	/* Find out requested/possible concurrency */
156f2f10664Scl 	p = getenv("PTHREAD_CONCURRENCY");
157b35aef8dSchristos 	pthread__maxconcurrency = p ? atoi(p) : 1;
1583a610280Schristos 
159f2f10664Scl 	if (pthread__maxconcurrency < 1)
160f2f10664Scl 		pthread__maxconcurrency = 1;
161f2f10664Scl 	if (pthread__maxconcurrency > ncpu)
162f2f10664Scl 		pthread__maxconcurrency = ncpu;
163f2f10664Scl 
164f2f10664Scl 	/* Allocate data structures */
165f2f10664Scl 	pthread__reidlequeue = (struct pthread_queue_t *)malloc
166f2f10664Scl 		(pthread__maxconcurrency * sizeof(struct pthread_queue_t));
167f2f10664Scl 	if (pthread__reidlequeue == NULL)
168f2f10664Scl 		err(1, "Couldn't allocate memory for pthread__reidlequeue");
169c62a74e6Sthorpej 
170c62a74e6Sthorpej 	/* Basic data structure setup */
171c62a74e6Sthorpej 	pthread_attr_init(&pthread_default_attr);
172c62a74e6Sthorpej 	PTQ_INIT(&pthread__allqueue);
173c62a74e6Sthorpej 	PTQ_INIT(&pthread__deadqueue);
174cca94056Schristos #ifdef PTHREAD_MLOCK_KLUDGE
175cca94056Schristos 	ret = mlock(&pthread__deadqueue, sizeof(pthread__deadqueue));
176cca94056Schristos 	pthread__assert(ret == 0);
177cca94056Schristos #endif
178c62a74e6Sthorpej 	PTQ_INIT(&pthread__runqueue);
179c62a74e6Sthorpej 	PTQ_INIT(&pthread__idlequeue);
180f2f10664Scl 	for (i = 0; i < pthread__maxconcurrency; i++)
181f2f10664Scl 		PTQ_INIT(&pthread__reidlequeue[i]);
18268a63a40Snathanw 	nthreads = 1;
183c62a74e6Sthorpej 
184c62a74e6Sthorpej 	/* Create the thread structure corresponding to main() */
185c62a74e6Sthorpej 	pthread__initmain(&first);
186c62a74e6Sthorpej 	pthread__initthread(first, first);
187c62a74e6Sthorpej 	first->pt_state = PT_STATE_RUNNING;
188*f782e995Sdrochner 	_sys___sigprocmask14(0, NULL, &first->pt_sigmask);
189c62a74e6Sthorpej 	PTQ_INSERT_HEAD(&pthread__allqueue, first, pt_allq);
190c62a74e6Sthorpej 
191c62a74e6Sthorpej 	/* Start subsystems */
192c62a74e6Sthorpej 	pthread__signal_init();
193c62a74e6Sthorpej 	PTHREAD_MD_INIT
194c62a74e6Sthorpej #ifdef PTHREAD__DEBUG
195f2f10664Scl 	pthread__debug_init(ncpu);
196c62a74e6Sthorpej #endif
197c62a74e6Sthorpej 
1980172694eSnathanw 	for (p = getenv("PTHREAD_DIAGASSERT"); p && *p; p++) {
1990172694eSnathanw 		switch (*p) {
2000172694eSnathanw 		case 'a':
2010172694eSnathanw 			pthread__diagassert |= DIAGASSERT_ABORT;
2020172694eSnathanw 			break;
2030172694eSnathanw 		case 'A':
2040172694eSnathanw 			pthread__diagassert &= ~DIAGASSERT_ABORT;
2050172694eSnathanw 			break;
2060172694eSnathanw 		case 'e':
2070172694eSnathanw 			pthread__diagassert |= DIAGASSERT_STDERR;
2080172694eSnathanw 			break;
2090172694eSnathanw 		case 'E':
2100172694eSnathanw 			pthread__diagassert &= ~DIAGASSERT_STDERR;
2110172694eSnathanw 			break;
2120172694eSnathanw 		case 'l':
2130172694eSnathanw 			pthread__diagassert |= DIAGASSERT_SYSLOG;
2140172694eSnathanw 			break;
2150172694eSnathanw 		case 'L':
2160172694eSnathanw 			pthread__diagassert &= ~DIAGASSERT_SYSLOG;
2170172694eSnathanw 			break;
218df277271Snathanw 		}
2190172694eSnathanw 	}
2200172694eSnathanw 
221df277271Snathanw 
222c62a74e6Sthorpej 	/* Tell libc that we're here and it should role-play accordingly. */
223c62a74e6Sthorpej 	__isthreaded = 1;
224c62a74e6Sthorpej }
225c62a74e6Sthorpej 
2262a4cef11Snathanw static void
2272a4cef11Snathanw pthread__child_callback(void)
2282a4cef11Snathanw {
2292a4cef11Snathanw 	/*
2302a4cef11Snathanw 	 * Clean up data structures that a forked child process might
2312a4cef11Snathanw 	 * trip over. Note that if threads have been created (causing
2322a4cef11Snathanw 	 * this handler to be registered) the standards say that the
2332a4cef11Snathanw 	 * child will trigger undefined behavior if it makes any
2342a4cef11Snathanw 	 * pthread_* calls (or any other calls that aren't
2352a4cef11Snathanw 	 * async-signal-safe), so we don't really have to clean up
2362a4cef11Snathanw 	 * much. Anything that permits some pthread_* calls to work is
2372a4cef11Snathanw 	 * merely being polite.
2382a4cef11Snathanw 	 */
2392a4cef11Snathanw 	pthread__started = 0;
2402a4cef11Snathanw }
241c62a74e6Sthorpej 
2420e675542Schs static void
243c62a74e6Sthorpej pthread__start(void)
244c62a74e6Sthorpej {
245c62a74e6Sthorpej 	pthread_t self, idle;
246c62a74e6Sthorpej 	int i, ret;
247c62a74e6Sthorpej 
248c62a74e6Sthorpej 	self = pthread__self(); /* should be the "main()" thread */
249c62a74e6Sthorpej 
250ff14fbf2Snathanw 	/*
251ff14fbf2Snathanw 	 * Per-process timers are cleared by fork(); despite the
252ff14fbf2Snathanw 	 * various restrictions on fork() and threads, it's legal to
253ff14fbf2Snathanw 	 * fork() before creating any threads.
254ff14fbf2Snathanw 	 */
255ff14fbf2Snathanw 	pthread__alarm_init();
256ff14fbf2Snathanw 
257916de878Snathanw 	pthread__signal_start();
258916de878Snathanw 
2592a4cef11Snathanw 	pthread_atfork(NULL, NULL, pthread__child_callback);
260c62a74e6Sthorpej 
261f2f10664Scl 	/*
262f2f10664Scl 	 * Create idle threads
263f2f10664Scl 	 * XXX need to create more idle threads if concurrency > 3
264f2f10664Scl 	 */
265c62a74e6Sthorpej 	for (i = 0; i < NIDLETHREADS; i++) {
266c62a74e6Sthorpej 		ret = pthread__stackalloc(&idle);
267c62a74e6Sthorpej 		if (ret != 0)
268c62a74e6Sthorpej 			err(1, "Couldn't allocate stack for idle thread!");
269c62a74e6Sthorpej 		pthread__initthread(self, idle);
270c62a74e6Sthorpej 		sigfillset(&idle->pt_sigmask);
271c62a74e6Sthorpej 		idle->pt_type = PT_THREAD_IDLE;
272c62a74e6Sthorpej 		PTQ_INSERT_HEAD(&pthread__allqueue, idle, pt_allq);
273c62a74e6Sthorpej 		pthread__sched_idle(self, idle);
274c62a74e6Sthorpej 	}
275c62a74e6Sthorpej 
276c62a74e6Sthorpej 	/* Start up the SA subsystem */
277c62a74e6Sthorpej 	pthread__sa_start();
278c62a74e6Sthorpej 	SDPRINTF(("(pthread__start %p) Started.\n", self));
279c62a74e6Sthorpej }
280c62a74e6Sthorpej 
281c62a74e6Sthorpej 
282c62a74e6Sthorpej /* General-purpose thread data structure sanitization. */
283c62a74e6Sthorpej void
284c62a74e6Sthorpej pthread__initthread(pthread_t self, pthread_t t)
285c62a74e6Sthorpej {
286c62a74e6Sthorpej 	int id;
287c62a74e6Sthorpej 
288c62a74e6Sthorpej 	pthread_spinlock(self, &nextthread_lock);
289c62a74e6Sthorpej 	id = nextthread;
290c62a74e6Sthorpej 	nextthread++;
291c62a74e6Sthorpej 	pthread_spinunlock(self, &nextthread_lock);
292c62a74e6Sthorpej 	t->pt_num = id;
293c62a74e6Sthorpej 
294c62a74e6Sthorpej 	t->pt_magic = PT_MAGIC;
295c62a74e6Sthorpej 	t->pt_type = PT_THREAD_NORMAL;
296c62a74e6Sthorpej 	t->pt_state = PT_STATE_RUNNABLE;
297c62a74e6Sthorpej 	pthread_lockinit(&t->pt_statelock);
2980878df5dSnathanw 	pthread_lockinit(&t->pt_flaglock);
299c62a74e6Sthorpej 	t->pt_spinlocks = 0;
300c62a74e6Sthorpej 	t->pt_next = NULL;
301c62a74e6Sthorpej 	t->pt_exitval = NULL;
302c62a74e6Sthorpej 	t->pt_flags = 0;
303c62a74e6Sthorpej 	t->pt_cancel = 0;
304c62a74e6Sthorpej 	t->pt_errno = 0;
305c62a74e6Sthorpej 	t->pt_parent = NULL;
306c62a74e6Sthorpej 	t->pt_heldlock = NULL;
307c62a74e6Sthorpej 	t->pt_switchto = NULL;
308487eb7e1Snathanw 	t->pt_trapuc = NULL;
309c62a74e6Sthorpej 	sigemptyset(&t->pt_siglist);
310c62a74e6Sthorpej 	sigemptyset(&t->pt_sigmask);
311c62a74e6Sthorpej 	pthread_lockinit(&t->pt_siglock);
312c62a74e6Sthorpej 	PTQ_INIT(&t->pt_joiners);
313c62a74e6Sthorpej 	pthread_lockinit(&t->pt_join_lock);
314c62a74e6Sthorpej 	PTQ_INIT(&t->pt_cleanup_stack);
315c62a74e6Sthorpej 	memset(&t->pt_specific, 0, sizeof(int) * PTHREAD_KEYS_MAX);
316b33971b9Sthorpej 	t->pt_name = NULL;
317c62a74e6Sthorpej #ifdef PTHREAD__DEBUG
318c62a74e6Sthorpej 	t->blocks = 0;
319c62a74e6Sthorpej 	t->preempts = 0;
320c62a74e6Sthorpej 	t->rescheds = 0;
321c62a74e6Sthorpej #endif
322c62a74e6Sthorpej }
323c62a74e6Sthorpej 
324c62a74e6Sthorpej 
325c62a74e6Sthorpej int
326c62a74e6Sthorpej pthread_create(pthread_t *thread, const pthread_attr_t *attr,
327c62a74e6Sthorpej 	    void *(*startfunc)(void *), void *arg)
328c62a74e6Sthorpej {
329c62a74e6Sthorpej 	pthread_t self, newthread;
330c62a74e6Sthorpej 	pthread_attr_t nattr;
331b33971b9Sthorpej 	struct pthread_attr_private *p;
332b33971b9Sthorpej 	char *name;
333c62a74e6Sthorpej 	int ret;
334c62a74e6Sthorpej 
335c62a74e6Sthorpej 	PTHREADD_ADD(PTHREADD_CREATE);
336c62a74e6Sthorpej 
337c62a74e6Sthorpej 	/*
338c62a74e6Sthorpej 	 * It's okay to check this without a lock because there can
339c62a74e6Sthorpej 	 * only be one thread before it becomes true.
340c62a74e6Sthorpej 	 */
341c62a74e6Sthorpej 	if (pthread__started == 0) {
342c62a74e6Sthorpej 		pthread__start();
343c62a74e6Sthorpej 		pthread__started = 1;
344c62a74e6Sthorpej 	}
345c62a74e6Sthorpej 
346c62a74e6Sthorpej 	if (attr == NULL)
347c62a74e6Sthorpej 		nattr = pthread_default_attr;
348e81f9f17Sdrochner 	else if (attr->pta_magic == PT_ATTR_MAGIC)
349c62a74e6Sthorpej 		nattr = *attr;
350c62a74e6Sthorpej 	else
351c62a74e6Sthorpej 		return EINVAL;
352c62a74e6Sthorpej 
353b33971b9Sthorpej 	/* Fetch misc. attributes from the attr structure. */
354508a50acSnathanw 	name = NULL;
355508a50acSnathanw 	if ((p = nattr.pta_private) != NULL)
356508a50acSnathanw 		if (p->ptap_name[0] != '\0')
357b33971b9Sthorpej 			if ((name = strdup(p->ptap_name)) == NULL)
358b33971b9Sthorpej 				return ENOMEM;
359c62a74e6Sthorpej 
360c62a74e6Sthorpej 	self = pthread__self();
361c62a74e6Sthorpej 
362c62a74e6Sthorpej 	pthread_spinlock(self, &pthread__deadqueue_lock);
363c62a74e6Sthorpej 	if (!PTQ_EMPTY(&pthread__deadqueue)) {
364c62a74e6Sthorpej 		newthread = PTQ_FIRST(&pthread__deadqueue);
365c62a74e6Sthorpej 		PTQ_REMOVE(&pthread__deadqueue, newthread, pt_allq);
366c62a74e6Sthorpej 		pthread_spinunlock(self, &pthread__deadqueue_lock);
367c62a74e6Sthorpej 	} else {
368c62a74e6Sthorpej 		pthread_spinunlock(self, &pthread__deadqueue_lock);
369c62a74e6Sthorpej 		/* Set up a stack and allocate space for a pthread_st. */
370c62a74e6Sthorpej 		ret = pthread__stackalloc(&newthread);
371b7559f85Schristos 		if (ret != 0) {
372b7559f85Schristos 			if (name)
373b7559f85Schristos 				free(name);
374c62a74e6Sthorpej 			return ret;
375c62a74e6Sthorpej 		}
376b7559f85Schristos 	}
377c62a74e6Sthorpej 
378c62a74e6Sthorpej 	/* 2. Set up state. */
379c62a74e6Sthorpej 	pthread__initthread(self, newthread);
380c62a74e6Sthorpej 	newthread->pt_flags = nattr.pta_flags;
381c62a74e6Sthorpej 	newthread->pt_sigmask = self->pt_sigmask;
382c62a74e6Sthorpej 
383b33971b9Sthorpej 	/* 3. Set up misc. attributes. */
384b33971b9Sthorpej 	newthread->pt_name = name;
385b33971b9Sthorpej 
386c62a74e6Sthorpej 	/*
387b33971b9Sthorpej 	 * 4. Set up context.
388c62a74e6Sthorpej 	 *
389c62a74e6Sthorpej 	 * The pt_uc pointer points to a location safely below the
390c62a74e6Sthorpej 	 * stack start; this is arranged by pthread__stackalloc().
391c62a74e6Sthorpej 	 */
392c62a74e6Sthorpej 	_INITCONTEXT_U(newthread->pt_uc);
393877f8985Snathanw #ifdef PTHREAD_MACHINE_HAS_ID_REGISTER
394877f8985Snathanw 	pthread__uc_id(newthread->pt_uc) = newthread;
395877f8985Snathanw #endif
396c62a74e6Sthorpej 	newthread->pt_uc->uc_stack = newthread->pt_stack;
397c62a74e6Sthorpej 	newthread->pt_uc->uc_link = NULL;
398c62a74e6Sthorpej 	makecontext(newthread->pt_uc, pthread__create_tramp, 2,
399c62a74e6Sthorpej 	    startfunc, arg);
400c62a74e6Sthorpej 
401b33971b9Sthorpej 	/* 5. Add to list of all threads. */
402c62a74e6Sthorpej 	pthread_spinlock(self, &pthread__allqueue_lock);
403c62a74e6Sthorpej 	PTQ_INSERT_HEAD(&pthread__allqueue, newthread, pt_allq);
404c62a74e6Sthorpej 	nthreads++;
405c62a74e6Sthorpej 	pthread_spinunlock(self, &pthread__allqueue_lock);
406c62a74e6Sthorpej 
407ba70e96aSchs 	SDPRINTF(("(pthread_create %p) new thread %p (name pointer %p).\n",
408ba70e96aSchs 		  self, newthread, newthread->pt_name));
40938b1c6f4Schristos 	/* 6. Put on appropriate queue. */
41038b1c6f4Schristos 	if (newthread->pt_flags & PT_FLAG_SUSPENDED) {
41138b1c6f4Schristos 		pthread_spinlock(self, &newthread->pt_statelock);
41238b1c6f4Schristos 		pthread__suspend(self, newthread);
41338b1c6f4Schristos 		pthread_spinunlock(self, &newthread->pt_statelock);
41438b1c6f4Schristos 	} else
415c62a74e6Sthorpej 		pthread__sched(self, newthread);
416c62a74e6Sthorpej 
417c62a74e6Sthorpej 	*thread = newthread;
418c62a74e6Sthorpej 
419c62a74e6Sthorpej 	return 0;
420c62a74e6Sthorpej }
421c62a74e6Sthorpej 
422c62a74e6Sthorpej 
423c62a74e6Sthorpej static void
424c62a74e6Sthorpej pthread__create_tramp(void *(*start)(void *), void *arg)
425c62a74e6Sthorpej {
426c62a74e6Sthorpej 	void *retval;
427c62a74e6Sthorpej 
42894a458ceSchs 	retval = (*start)(arg);
429c62a74e6Sthorpej 
430c62a74e6Sthorpej 	pthread_exit(retval);
431c62a74e6Sthorpej 
432143f5a27Schristos 	/*NOTREACHED*/
433143f5a27Schristos 	pthread__abort();
434c62a74e6Sthorpej }
435c62a74e6Sthorpej 
43638b1c6f4Schristos int
43738b1c6f4Schristos pthread_suspend_np(pthread_t thread)
43838b1c6f4Schristos {
439ba70e96aSchs 	pthread_t self;
440ba70e96aSchs 
441ba70e96aSchs 	self = pthread__self();
44238b1c6f4Schristos 	if (self == thread) {
44338b1c6f4Schristos 		return EDEADLK;
44438b1c6f4Schristos 	}
445ba70e96aSchs #ifdef ERRORCHECK
446ba70e96aSchs 	if (pthread__find(self, thread) != 0)
447ba70e96aSchs 		return ESRCH;
448ba70e96aSchs #endif
44938b1c6f4Schristos 	SDPRINTF(("(pthread_suspend_np %p) Suspend thread %p (state %d).\n",
45038b1c6f4Schristos 		     self, thread, thread->pt_state));
45138b1c6f4Schristos 	pthread_spinlock(self, &thread->pt_statelock);
45282b6b2dbScl 	if (thread->pt_blockgen != thread->pt_unblockgen) {
45382b6b2dbScl 		/* XXX flaglock? */
45482b6b2dbScl 		thread->pt_flags |= PT_FLAG_SUSPENDED;
45582b6b2dbScl 		pthread_spinunlock(self, &thread->pt_statelock);
45682b6b2dbScl 		return 0;
45782b6b2dbScl 	}
45838b1c6f4Schristos 	switch (thread->pt_state) {
45938b1c6f4Schristos 	case PT_STATE_RUNNING:
46038b1c6f4Schristos 		pthread__abort();	/* XXX */
46138b1c6f4Schristos 		break;
46238b1c6f4Schristos 	case PT_STATE_SUSPENDED:
46338b1c6f4Schristos 		pthread_spinunlock(self, &thread->pt_statelock);
46438b1c6f4Schristos 		return 0;
46538b1c6f4Schristos 	case PT_STATE_RUNNABLE:
46638b1c6f4Schristos 		pthread_spinlock(self, &pthread__runqueue_lock);
46738b1c6f4Schristos 		PTQ_REMOVE(&pthread__runqueue, thread, pt_runq);
46838b1c6f4Schristos 		pthread_spinunlock(self, &pthread__runqueue_lock);
46938b1c6f4Schristos 		break;
47038b1c6f4Schristos 	case PT_STATE_BLOCKED_QUEUE:
47138b1c6f4Schristos 		pthread_spinlock(self, thread->pt_sleeplock);
47238b1c6f4Schristos 		PTQ_REMOVE(thread->pt_sleepq, thread, pt_sleep);
47338b1c6f4Schristos 		pthread_spinunlock(self, thread->pt_sleeplock);
47438b1c6f4Schristos 		break;
475ba70e96aSchs 	case PT_STATE_ZOMBIE:
476ba70e96aSchs 		goto out;
47738b1c6f4Schristos 	default:
47838b1c6f4Schristos 		break;			/* XXX */
47938b1c6f4Schristos 	}
48038b1c6f4Schristos 	pthread__suspend(self, thread);
481ba70e96aSchs 
482ba70e96aSchs out:
48338b1c6f4Schristos 	pthread_spinunlock(self, &thread->pt_statelock);
48438b1c6f4Schristos 	return 0;
48538b1c6f4Schristos }
48638b1c6f4Schristos 
48738b1c6f4Schristos int
48838b1c6f4Schristos pthread_resume_np(pthread_t thread)
48938b1c6f4Schristos {
490ba70e96aSchs 	pthread_t self;
49138b1c6f4Schristos 
492ba70e96aSchs 	self = pthread__self();
493ba70e96aSchs #ifdef ERRORCHECK
494ba70e96aSchs 	if (pthread__find(self, thread) != 0)
495ba70e96aSchs 		return ESRCH;
496ba70e96aSchs #endif
49738b1c6f4Schristos 	SDPRINTF(("(pthread_resume_np %p) Resume thread %p (state %d).\n",
49838b1c6f4Schristos 		     self, thread, thread->pt_state));
49938b1c6f4Schristos 	pthread_spinlock(self, &thread->pt_statelock);
50038b1c6f4Schristos 	/* XXX flaglock? */
50138b1c6f4Schristos 	thread->pt_flags &= ~PT_FLAG_SUSPENDED;
50238b1c6f4Schristos 	if (thread->pt_state == PT_STATE_SUSPENDED) {
50338b1c6f4Schristos 		pthread_spinlock(self, &pthread__runqueue_lock);
50438b1c6f4Schristos 		PTQ_REMOVE(&pthread__suspqueue, thread, pt_runq);
50538b1c6f4Schristos 		pthread_spinunlock(self, &pthread__runqueue_lock);
50638b1c6f4Schristos 		pthread__sched(self, thread);
50738b1c6f4Schristos 	}
50838b1c6f4Schristos 	pthread_spinunlock(self, &thread->pt_statelock);
50938b1c6f4Schristos 	return 0;
51038b1c6f4Schristos }
51138b1c6f4Schristos 
512c62a74e6Sthorpej 
513c62a74e6Sthorpej /*
514c62a74e6Sthorpej  * Other threads will switch to the idle thread so that they
515c62a74e6Sthorpej  * can dispose of any awkward locks or recycle upcall state.
516c62a74e6Sthorpej  */
517c62a74e6Sthorpej void
518c62a74e6Sthorpej pthread__idle(void)
519c62a74e6Sthorpej {
520c62a74e6Sthorpej 	pthread_t self;
521c62a74e6Sthorpej 
522c62a74e6Sthorpej 	PTHREADD_ADD(PTHREADD_IDLE);
523c62a74e6Sthorpej 	self = pthread__self();
524c62a74e6Sthorpej 	SDPRINTF(("(pthread__idle %p).\n", self));
525c62a74e6Sthorpej 
526c62a74e6Sthorpej 	/*
527c62a74e6Sthorpej 	 * The drill here is that we want to yield the processor,
528c62a74e6Sthorpej 	 * but for the thread itself to be recovered, we need to be on
529c62a74e6Sthorpej 	 * a list somewhere for the thread system to know about us.
530c62a74e6Sthorpej 	 */
531c62a74e6Sthorpej 	pthread_spinlock(self, &pthread__deadqueue_lock);
532f2f10664Scl 	PTQ_INSERT_TAIL(&pthread__reidlequeue[self->pt_vpid], self, pt_runq);
533f2f10664Scl 	pthread__concurrency--;
534f2f10664Scl 	SDPRINTF(("(yield %p concurrency) now %d\n", self,
535f2f10664Scl 		     pthread__concurrency));
5360878df5dSnathanw 	/* Don't need a flag lock; nothing else has a handle on this thread */
537c62a74e6Sthorpej 	self->pt_flags |= PT_FLAG_IDLED;
538c62a74e6Sthorpej 	pthread_spinunlock(self, &pthread__deadqueue_lock);
539c62a74e6Sthorpej 
540c62a74e6Sthorpej 	/*
541c62a74e6Sthorpej 	 * If we get to run this, then no preemption has happened
542c62a74e6Sthorpej 	 * (because the upcall handler will not continue an idle thread with
543c62a74e6Sthorpej 	 * PT_FLAG_IDLED set), and so we can yield the processor safely.
544c62a74e6Sthorpej 	 */
545c62a74e6Sthorpej 	SDPRINTF(("(pthread__idle %p) yielding.\n", self));
546c62a74e6Sthorpej 	sa_yield();
547c62a74e6Sthorpej 
548c62a74e6Sthorpej 	/* NOTREACHED */
549c62a74e6Sthorpej 	self->pt_spinlocks++; /* XXX make sure we get to finish the assert! */
550c62a74e6Sthorpej 	SDPRINTF(("(pthread__idle %p) Returned! Error.\n", self));
551143f5a27Schristos 	pthread__abort();
552c62a74e6Sthorpej }
553c62a74e6Sthorpej 
554c62a74e6Sthorpej 
555c62a74e6Sthorpej void
556c62a74e6Sthorpej pthread_exit(void *retval)
557c62a74e6Sthorpej {
55896b5a26dSnathanw 	pthread_t self;
559c62a74e6Sthorpej 	struct pt_clean_t *cleanup;
560b33971b9Sthorpej 	char *name;
5616b2b9c62Syamt 	int nt;
562c62a74e6Sthorpej 
563c62a74e6Sthorpej 	self = pthread__self();
564ba70e96aSchs 	SDPRINTF(("(pthread_exit %p) status %p, flags %x, cancel %d\n",
565ba70e96aSchs 		  self, retval, self->pt_flags, self->pt_cancel));
566c62a74e6Sthorpej 
567c62a74e6Sthorpej 	/* Disable cancellability. */
5680878df5dSnathanw 	pthread_spinlock(self, &self->pt_flaglock);
569c62a74e6Sthorpej 	self->pt_flags |= PT_FLAG_CS_DISABLED;
57066fcc1ceSnathanw 	self->pt_cancel = 0;
5710878df5dSnathanw 	pthread_spinunlock(self, &self->pt_flaglock);
572c62a74e6Sthorpej 
573c62a74e6Sthorpej 	/* Call any cancellation cleanup handlers */
574c62a74e6Sthorpej 	while (!PTQ_EMPTY(&self->pt_cleanup_stack)) {
575c62a74e6Sthorpej 		cleanup = PTQ_FIRST(&self->pt_cleanup_stack);
576c62a74e6Sthorpej 		PTQ_REMOVE(&self->pt_cleanup_stack, cleanup, ptc_next);
577c62a74e6Sthorpej 		(*cleanup->ptc_cleanup)(cleanup->ptc_arg);
578c62a74e6Sthorpej 	}
579c62a74e6Sthorpej 
580c62a74e6Sthorpej 	/* Perform cleanup of thread-specific data */
581c62a74e6Sthorpej 	pthread__destroy_tsd(self);
582c62a74e6Sthorpej 
583c62a74e6Sthorpej 	self->pt_exitval = retval;
584c62a74e6Sthorpej 
5856b2b9c62Syamt 	/*
5866b2b9c62Syamt 	 * it's safe to check PT_FLAG_DETACHED without pt_flaglock
5876b2b9c62Syamt 	 * because it's only set by pthread_detach with pt_join_lock held.
5886b2b9c62Syamt 	 */
5896b2b9c62Syamt 	pthread_spinlock(self, &self->pt_join_lock);
5906b2b9c62Syamt 	if (self->pt_flags & PT_FLAG_DETACHED) {
5916b2b9c62Syamt 		self->pt_state = PT_STATE_DEAD;
5926b2b9c62Syamt 		pthread_spinunlock(self, &self->pt_join_lock);
593b33971b9Sthorpej 		name = self->pt_name;
594b33971b9Sthorpej 		self->pt_name = NULL;
595c62a74e6Sthorpej 
596b33971b9Sthorpej 		if (name != NULL)
597b33971b9Sthorpej 			free(name);
598b33971b9Sthorpej 
599c62a74e6Sthorpej 		pthread_spinlock(self, &pthread__allqueue_lock);
600c62a74e6Sthorpej 		PTQ_REMOVE(&pthread__allqueue, self, pt_allq);
601c62a74e6Sthorpej 		nthreads--;
602c62a74e6Sthorpej 		nt = nthreads;
603c62a74e6Sthorpej 		pthread_spinunlock(self, &pthread__allqueue_lock);
604c62a74e6Sthorpej 
605c62a74e6Sthorpej 		if (nt == 0) {
606c62a74e6Sthorpej 			/* Whoah, we're the last one. Time to go. */
607c62a74e6Sthorpej 			exit(0);
608c62a74e6Sthorpej 		}
609c62a74e6Sthorpej 
610c62a74e6Sthorpej 		/* Yeah, yeah, doing work while we're dead is tacky. */
611c62a74e6Sthorpej 		pthread_spinlock(self, &pthread__deadqueue_lock);
612c62a74e6Sthorpej 		PTQ_INSERT_HEAD(&pthread__deadqueue, self, pt_allq);
613c62a74e6Sthorpej 		pthread__block(self, &pthread__deadqueue_lock);
614ba70e96aSchs 		SDPRINTF(("(pthread_exit %p) walking dead\n", self));
615c62a74e6Sthorpej 	} else {
6166b2b9c62Syamt 		self->pt_state = PT_STATE_ZOMBIE;
617b33971b9Sthorpej 		/* Note: name will be freed by the joiner. */
618c62a74e6Sthorpej 		pthread_spinlock(self, &pthread__allqueue_lock);
619c62a74e6Sthorpej 		nthreads--;
620c62a74e6Sthorpej 		nt = nthreads;
621c62a74e6Sthorpej 		pthread_spinunlock(self, &pthread__allqueue_lock);
622c62a74e6Sthorpej 		if (nt == 0) {
623c62a74e6Sthorpej 			/* Whoah, we're the last one. Time to go. */
624c62a74e6Sthorpej 			exit(0);
625c62a74e6Sthorpej 		}
62696b5a26dSnathanw 		/*
62796b5a26dSnathanw 		 * Wake up all the potential joiners. Only one can win.
628c62a74e6Sthorpej 		 * (Can you say "Thundering Herd"? I knew you could.)
629c62a74e6Sthorpej 		 */
63096b5a26dSnathanw 		pthread__sched_sleepers(self, &self->pt_joiners);
631c62a74e6Sthorpej 		pthread__block(self, &self->pt_join_lock);
632ba70e96aSchs 		SDPRINTF(("(pthread_exit %p) walking zombie\n", self));
633c62a74e6Sthorpej 	}
634c62a74e6Sthorpej 
635143f5a27Schristos 	/*NOTREACHED*/
636143f5a27Schristos 	pthread__abort();
637c62a74e6Sthorpej 	exit(1);
638c62a74e6Sthorpej }
639c62a74e6Sthorpej 
640c62a74e6Sthorpej 
641c62a74e6Sthorpej int
642c62a74e6Sthorpej pthread_join(pthread_t thread, void **valptr)
643c62a74e6Sthorpej {
644c62a74e6Sthorpej 	pthread_t self;
645b33971b9Sthorpej 	char *name;
646564fe117Snathanw 	int num;
647c62a74e6Sthorpej 
648c62a74e6Sthorpej 	self = pthread__self();
649c62a74e6Sthorpej 	SDPRINTF(("(pthread_join %p) Joining %p.\n", self, thread));
650c62a74e6Sthorpej 
651c62a74e6Sthorpej 	if (pthread__find(self, thread) != 0)
652c62a74e6Sthorpej 		return ESRCH;
653c62a74e6Sthorpej 
654c62a74e6Sthorpej 	if (thread->pt_magic != PT_MAGIC)
655c62a74e6Sthorpej 		return EINVAL;
656c62a74e6Sthorpej 
657c62a74e6Sthorpej 	if (thread == self)
658c62a74e6Sthorpej 		return EDEADLK;
659c62a74e6Sthorpej 
6600878df5dSnathanw 	pthread_spinlock(self, &thread->pt_flaglock);
661c62a74e6Sthorpej 
662c62a74e6Sthorpej 	if (thread->pt_flags & PT_FLAG_DETACHED) {
6630878df5dSnathanw 		pthread_spinunlock(self, &thread->pt_flaglock);
664c62a74e6Sthorpej 		return EINVAL;
665c62a74e6Sthorpej 	}
666c62a74e6Sthorpej 
667564fe117Snathanw 	num = thread->pt_num;
6680878df5dSnathanw 	pthread_spinlock(self, &thread->pt_join_lock);
669564fe117Snathanw 	while (thread->pt_state != PT_STATE_ZOMBIE) {
670564fe117Snathanw 		if ((thread->pt_state == PT_STATE_DEAD) ||
671564fe117Snathanw 		    (thread->pt_flags & PT_FLAG_DETACHED) ||
672564fe117Snathanw 		    (thread->pt_num != num)) {
673564fe117Snathanw 			/*
674564fe117Snathanw 			 * Another thread beat us to the join, or called
675564fe117Snathanw 			 * pthread_detach(). If num didn't match, the
676564fe117Snathanw 			 * thread died and was recycled before we got
677564fe117Snathanw 			 * another chance to run.
678564fe117Snathanw 			 */
679564fe117Snathanw 			pthread_spinunlock(self, &thread->pt_join_lock);
6800878df5dSnathanw 			pthread_spinunlock(self, &thread->pt_flaglock);
681564fe117Snathanw 			return ESRCH;
682564fe117Snathanw 		}
683c62a74e6Sthorpej 		/*
684c62a74e6Sthorpej 		 * "I'm not dead yet!"
685c62a74e6Sthorpej 		 * "You will be soon enough."
686c62a74e6Sthorpej 		 */
6870878df5dSnathanw 		pthread_spinunlock(self, &thread->pt_flaglock);
688c62a74e6Sthorpej 		pthread_spinlock(self, &self->pt_statelock);
689c62a74e6Sthorpej 		if (self->pt_cancel) {
690c62a74e6Sthorpej 			pthread_spinunlock(self, &self->pt_statelock);
691c62a74e6Sthorpej 			pthread_spinunlock(self, &thread->pt_join_lock);
692c62a74e6Sthorpej 			pthread_exit(PTHREAD_CANCELED);
693c62a74e6Sthorpej 		}
694c62a74e6Sthorpej 		self->pt_state = PT_STATE_BLOCKED_QUEUE;
695c62a74e6Sthorpej 		self->pt_sleepobj = thread;
696c62a74e6Sthorpej 		self->pt_sleepq = &thread->pt_joiners;
697c62a74e6Sthorpej 		self->pt_sleeplock = &thread->pt_join_lock;
698c62a74e6Sthorpej 		pthread_spinunlock(self, &self->pt_statelock);
699c62a74e6Sthorpej 
700c62a74e6Sthorpej 		PTQ_INSERT_TAIL(&thread->pt_joiners, self, pt_sleep);
701c62a74e6Sthorpej 		pthread__block(self, &thread->pt_join_lock);
7020878df5dSnathanw 		pthread_spinlock(self, &thread->pt_flaglock);
703c62a74e6Sthorpej 		pthread_spinlock(self, &thread->pt_join_lock);
704c62a74e6Sthorpej 	}
705c62a74e6Sthorpej 
706c62a74e6Sthorpej 	/* All ours. */
707c62a74e6Sthorpej 	thread->pt_state = PT_STATE_DEAD;
708b33971b9Sthorpej 	name = thread->pt_name;
709b33971b9Sthorpej 	thread->pt_name = NULL;
710c62a74e6Sthorpej 	pthread_spinunlock(self, &thread->pt_join_lock);
7110878df5dSnathanw 	pthread_spinunlock(self, &thread->pt_flaglock);
712c62a74e6Sthorpej 
713c62a74e6Sthorpej 	if (valptr != NULL)
714c62a74e6Sthorpej 		*valptr = thread->pt_exitval;
715c62a74e6Sthorpej 
716c62a74e6Sthorpej 	SDPRINTF(("(pthread_join %p) Joined %p.\n", self, thread));
717c62a74e6Sthorpej 
7186b2b9c62Syamt 	pthread__dead(self, thread);
719c62a74e6Sthorpej 
720b33971b9Sthorpej 	if (name != NULL)
721b33971b9Sthorpej 		free(name);
722b33971b9Sthorpej 
723c62a74e6Sthorpej 	return 0;
724c62a74e6Sthorpej }
725c62a74e6Sthorpej 
726c62a74e6Sthorpej 
727c62a74e6Sthorpej int
728c62a74e6Sthorpej pthread_equal(pthread_t t1, pthread_t t2)
729c62a74e6Sthorpej {
730c62a74e6Sthorpej 
731c62a74e6Sthorpej 	/* Nothing special here. */
732c62a74e6Sthorpej 	return (t1 == t2);
733c62a74e6Sthorpej }
734c62a74e6Sthorpej 
735c62a74e6Sthorpej 
736c62a74e6Sthorpej int
737c62a74e6Sthorpej pthread_detach(pthread_t thread)
738c62a74e6Sthorpej {
73996b5a26dSnathanw 	pthread_t self;
7406b2b9c62Syamt 	int doreclaim = 0;
7416b2b9c62Syamt 	char *name = NULL;
742c62a74e6Sthorpej 
743c62a74e6Sthorpej 	self = pthread__self();
744c62a74e6Sthorpej 
745c62a74e6Sthorpej 	if (pthread__find(self, thread) != 0)
746c62a74e6Sthorpej 		return ESRCH;
747c62a74e6Sthorpej 
748c62a74e6Sthorpej 	if (thread->pt_magic != PT_MAGIC)
749c62a74e6Sthorpej 		return EINVAL;
750c62a74e6Sthorpej 
7510878df5dSnathanw 	pthread_spinlock(self, &thread->pt_flaglock);
752c62a74e6Sthorpej 	pthread_spinlock(self, &thread->pt_join_lock);
753c62a74e6Sthorpej 
754c62a74e6Sthorpej 	if (thread->pt_flags & PT_FLAG_DETACHED) {
755c62a74e6Sthorpej 		pthread_spinunlock(self, &thread->pt_join_lock);
7560878df5dSnathanw 		pthread_spinunlock(self, &thread->pt_flaglock);
757c62a74e6Sthorpej 		return EINVAL;
758c62a74e6Sthorpej 	}
759c62a74e6Sthorpej 
760c62a74e6Sthorpej 	thread->pt_flags |= PT_FLAG_DETACHED;
761c62a74e6Sthorpej 
762c62a74e6Sthorpej 	/* Any joiners have to be punted now. */
76396b5a26dSnathanw 	pthread__sched_sleepers(self, &thread->pt_joiners);
764c62a74e6Sthorpej 
7656b2b9c62Syamt 	if (thread->pt_state == PT_STATE_ZOMBIE) {
7666b2b9c62Syamt 		thread->pt_state = PT_STATE_DEAD;
7676b2b9c62Syamt 		name = thread->pt_name;
7686b2b9c62Syamt 		thread->pt_name = NULL;
7696b2b9c62Syamt 		doreclaim = 1;
7706b2b9c62Syamt 	}
7716b2b9c62Syamt 
772c62a74e6Sthorpej 	pthread_spinunlock(self, &thread->pt_join_lock);
7730878df5dSnathanw 	pthread_spinunlock(self, &thread->pt_flaglock);
774c62a74e6Sthorpej 
7756b2b9c62Syamt 	if (doreclaim) {
7766b2b9c62Syamt 		pthread__dead(self, thread);
7776b2b9c62Syamt 		if (name != NULL)
7786b2b9c62Syamt 			free(name);
7796b2b9c62Syamt 	}
7806b2b9c62Syamt 
781c62a74e6Sthorpej 	return 0;
782c62a74e6Sthorpej }
783c62a74e6Sthorpej 
784c62a74e6Sthorpej 
7856b2b9c62Syamt static void
7866b2b9c62Syamt pthread__dead(pthread_t self, pthread_t thread)
7876b2b9c62Syamt {
7886b2b9c62Syamt 
7896b2b9c62Syamt 	SDPRINTF(("(pthread__dead %p) Reclaimed %p.\n", self, thread));
7906b2b9c62Syamt 	pthread__assert(thread != self);
7916b2b9c62Syamt 	pthread__assert(thread->pt_state == PT_STATE_DEAD);
7926b2b9c62Syamt 	pthread__assert(thread->pt_name == NULL);
7936b2b9c62Syamt 
7946b2b9c62Syamt 	/* Cleanup time. Move the dead thread from allqueue to the deadqueue */
7956b2b9c62Syamt 	pthread_spinlock(self, &pthread__allqueue_lock);
7966b2b9c62Syamt 	PTQ_REMOVE(&pthread__allqueue, thread, pt_allq);
7976b2b9c62Syamt 	pthread_spinunlock(self, &pthread__allqueue_lock);
7986b2b9c62Syamt 
7996b2b9c62Syamt 	pthread_spinlock(self, &pthread__deadqueue_lock);
8006b2b9c62Syamt 	PTQ_INSERT_HEAD(&pthread__deadqueue, thread, pt_allq);
8016b2b9c62Syamt 	pthread_spinunlock(self, &pthread__deadqueue_lock);
8026b2b9c62Syamt }
8036b2b9c62Syamt 
8046b2b9c62Syamt 
805c62a74e6Sthorpej int
806b33971b9Sthorpej pthread_getname_np(pthread_t thread, char *name, size_t len)
807c62a74e6Sthorpej {
808b33971b9Sthorpej 	pthread_t self;
809c62a74e6Sthorpej 
810b33971b9Sthorpej 	self = pthread__self();
811b33971b9Sthorpej 
812b33971b9Sthorpej 	if (pthread__find(self, thread) != 0)
813b33971b9Sthorpej 		return ESRCH;
814b33971b9Sthorpej 
815b33971b9Sthorpej 	if (thread->pt_magic != PT_MAGIC)
816b33971b9Sthorpej 		return EINVAL;
817b33971b9Sthorpej 
818b33971b9Sthorpej 	pthread_spinlock(self, &thread->pt_join_lock);
819b33971b9Sthorpej 	if (thread->pt_name == NULL)
820b33971b9Sthorpej 		name[0] = '\0';
821b33971b9Sthorpej 	else
822b33971b9Sthorpej 		strlcpy(name, thread->pt_name, len);
823b33971b9Sthorpej 	pthread_spinunlock(self, &thread->pt_join_lock);
824c62a74e6Sthorpej 
825c62a74e6Sthorpej 	return 0;
826c62a74e6Sthorpej }
827c62a74e6Sthorpej 
828c62a74e6Sthorpej 
829c62a74e6Sthorpej int
830b33971b9Sthorpej pthread_setname_np(pthread_t thread, const char *name, void *arg)
831b33971b9Sthorpej {
832ba70e96aSchs 	pthread_t self;
833b33971b9Sthorpej 	char *oldname, *cp, newname[PTHREAD_MAX_NAMELEN_NP];
834b33971b9Sthorpej 	int namelen;
835b33971b9Sthorpej 
836ba70e96aSchs 	self = pthread__self();
837b33971b9Sthorpej 	if (pthread__find(self, thread) != 0)
838b33971b9Sthorpej 		return ESRCH;
839b33971b9Sthorpej 
840b33971b9Sthorpej 	if (thread->pt_magic != PT_MAGIC)
841b33971b9Sthorpej 		return EINVAL;
842b33971b9Sthorpej 
843b33971b9Sthorpej 	namelen = snprintf(newname, sizeof(newname), name, arg);
844b33971b9Sthorpej 	if (namelen >= PTHREAD_MAX_NAMELEN_NP)
845b33971b9Sthorpej 		return EINVAL;
846b33971b9Sthorpej 
847b33971b9Sthorpej 	cp = strdup(newname);
848b33971b9Sthorpej 	if (cp == NULL)
849b33971b9Sthorpej 		return ENOMEM;
850b33971b9Sthorpej 
851b33971b9Sthorpej 	pthread_spinlock(self, &thread->pt_join_lock);
852b33971b9Sthorpej 
853b33971b9Sthorpej 	if (thread->pt_state == PT_STATE_DEAD) {
854b33971b9Sthorpej 		pthread_spinunlock(self, &thread->pt_join_lock);
855b33971b9Sthorpej 		free(cp);
856b33971b9Sthorpej 		return EINVAL;
857b33971b9Sthorpej 	}
858b33971b9Sthorpej 
859b33971b9Sthorpej 	oldname = thread->pt_name;
860b33971b9Sthorpej 	thread->pt_name = cp;
861b33971b9Sthorpej 
862b33971b9Sthorpej 	pthread_spinunlock(self, &thread->pt_join_lock);
863b33971b9Sthorpej 
864b33971b9Sthorpej 	if (oldname != NULL)
865b33971b9Sthorpej 		free(oldname);
866b33971b9Sthorpej 
867b33971b9Sthorpej 	return 0;
868b33971b9Sthorpej }
869b33971b9Sthorpej 
870b33971b9Sthorpej 
871b33971b9Sthorpej 
872c62a74e6Sthorpej /*
873c62a74e6Sthorpej  * XXX There should be a way for applications to use the efficent
874c62a74e6Sthorpej  *  inline version, but there are opacity/namespace issues.
875c62a74e6Sthorpej  */
876c62a74e6Sthorpej pthread_t
877c62a74e6Sthorpej pthread_self(void)
878c62a74e6Sthorpej {
879c62a74e6Sthorpej 
880c62a74e6Sthorpej 	return pthread__self();
881c62a74e6Sthorpej }
882c62a74e6Sthorpej 
883c62a74e6Sthorpej 
884c62a74e6Sthorpej int
885c62a74e6Sthorpej pthread_cancel(pthread_t thread)
886c62a74e6Sthorpej {
887c62a74e6Sthorpej 	pthread_t self;
888c62a74e6Sthorpej 
889ba70e96aSchs 	self = pthread__self();
890ba70e96aSchs #ifdef ERRORCHECK
891ba70e96aSchs 	if (pthread__find(self, thread) != 0)
892ba70e96aSchs 		return ESRCH;
893ba70e96aSchs #endif
894c62a74e6Sthorpej 	if (!(thread->pt_state == PT_STATE_RUNNING ||
895c62a74e6Sthorpej 	    thread->pt_state == PT_STATE_RUNNABLE ||
89682b6b2dbScl 	    thread->pt_state == PT_STATE_BLOCKED_QUEUE))
897c62a74e6Sthorpej 		return ESRCH;
898c62a74e6Sthorpej 
8990878df5dSnathanw 	pthread_spinlock(self, &thread->pt_flaglock);
9000878df5dSnathanw 	thread->pt_flags |= PT_FLAG_CS_PENDING;
9010878df5dSnathanw 	if ((thread->pt_flags & PT_FLAG_CS_DISABLED) == 0) {
902c62a74e6Sthorpej 		thread->pt_cancel = 1;
9030878df5dSnathanw 		pthread_spinunlock(self, &thread->pt_flaglock);
904c62a74e6Sthorpej 		pthread_spinlock(self, &thread->pt_statelock);
90582b6b2dbScl 		if (thread->pt_blockgen != thread->pt_unblockgen) {
906c62a74e6Sthorpej 			/*
907c62a74e6Sthorpej 			 * It's sleeping in the kernel. If we can wake
908c62a74e6Sthorpej 			 * it up, it will notice the cancellation when
909c62a74e6Sthorpej 			 * it returns. If it doesn't wake up when we
910c62a74e6Sthorpej 			 * make this call, then it's blocked
911c62a74e6Sthorpej 			 * uninterruptably in the kernel, and there's
912c62a74e6Sthorpej 			 * not much to be done about it.
913c62a74e6Sthorpej 			 */
914c62a74e6Sthorpej 			_lwp_wakeup(thread->pt_blockedlwp);
915c62a74e6Sthorpej 		} else if (thread->pt_state == PT_STATE_BLOCKED_QUEUE) {
916c62a74e6Sthorpej 			/*
917c62a74e6Sthorpej 			 * We're blocked somewhere (pthread__block()
9188bcff70bSnathanw 			 * was called). Cause it to wake up; it will
9198bcff70bSnathanw 			 * check for the cancellation if the routine
9208bcff70bSnathanw 			 * is a cancellation point, and loop and reblock
9218bcff70bSnathanw 			 * otherwise.
922c62a74e6Sthorpej 			 */
923c62a74e6Sthorpej 			pthread_spinlock(self, thread->pt_sleeplock);
924c62a74e6Sthorpej 			PTQ_REMOVE(thread->pt_sleepq, thread,
925c62a74e6Sthorpej 			    pt_sleep);
926c62a74e6Sthorpej 			pthread_spinunlock(self, thread->pt_sleeplock);
927c62a74e6Sthorpej 			pthread__sched(self, thread);
928c62a74e6Sthorpej 		} else {
929c62a74e6Sthorpej 			/*
930c62a74e6Sthorpej 			 * Nothing. The target thread is running and will
931c62a74e6Sthorpej 			 * notice at the next deferred cancellation point.
932c62a74e6Sthorpej 			 */
933c62a74e6Sthorpej 		}
934c62a74e6Sthorpej 		pthread_spinunlock(self, &thread->pt_statelock);
9350878df5dSnathanw 	} else
9360878df5dSnathanw 		pthread_spinunlock(self, &thread->pt_flaglock);
937c62a74e6Sthorpej 
938c62a74e6Sthorpej 	return 0;
939c62a74e6Sthorpej }
940c62a74e6Sthorpej 
941c62a74e6Sthorpej 
942c62a74e6Sthorpej int
943c62a74e6Sthorpej pthread_setcancelstate(int state, int *oldstate)
944c62a74e6Sthorpej {
945c62a74e6Sthorpej 	pthread_t self;
9460878df5dSnathanw 	int retval;
947c62a74e6Sthorpej 
948c62a74e6Sthorpej 	self = pthread__self();
9490878df5dSnathanw 	retval = 0;
950c62a74e6Sthorpej 
9510878df5dSnathanw 	pthread_spinlock(self, &self->pt_flaglock);
952c62a74e6Sthorpej 	if (oldstate != NULL) {
9530878df5dSnathanw 		if (self->pt_flags & PT_FLAG_CS_DISABLED)
954c62a74e6Sthorpej 			*oldstate = PTHREAD_CANCEL_DISABLE;
955c62a74e6Sthorpej 		else
956c62a74e6Sthorpej 			*oldstate = PTHREAD_CANCEL_ENABLE;
957c62a74e6Sthorpej 	}
958c62a74e6Sthorpej 
9590878df5dSnathanw 	if (state == PTHREAD_CANCEL_DISABLE) {
9600878df5dSnathanw 		self->pt_flags |= PT_FLAG_CS_DISABLED;
9610878df5dSnathanw 		if (self->pt_cancel) {
9620878df5dSnathanw 			self->pt_flags |= PT_FLAG_CS_PENDING;
9630878df5dSnathanw 			self->pt_cancel = 0;
9640878df5dSnathanw 		}
9650878df5dSnathanw 	} else if (state == PTHREAD_CANCEL_ENABLE) {
9660878df5dSnathanw 		self->pt_flags &= ~PT_FLAG_CS_DISABLED;
967c62a74e6Sthorpej 		/*
968c62a74e6Sthorpej 		 * If a cancellation was requested while cancellation
969c62a74e6Sthorpej 		 * was disabled, note that fact for future
970c62a74e6Sthorpej 		 * cancellation tests.
971c62a74e6Sthorpej 		 */
9720878df5dSnathanw 		if (self->pt_flags & PT_FLAG_CS_PENDING) {
973c62a74e6Sthorpej 			self->pt_cancel = 1;
974c62a74e6Sthorpej 			/* This is not a deferred cancellation point. */
9750878df5dSnathanw 			if (self->pt_flags & PT_FLAG_CS_ASYNC) {
9760878df5dSnathanw 				pthread_spinunlock(self, &self->pt_flaglock);
977c62a74e6Sthorpej 				pthread_exit(PTHREAD_CANCELED);
978c62a74e6Sthorpej 			}
9790878df5dSnathanw 		}
980c62a74e6Sthorpej 	} else
9810878df5dSnathanw 		retval = EINVAL;
982c62a74e6Sthorpej 
9830878df5dSnathanw 	pthread_spinunlock(self, &self->pt_flaglock);
9840878df5dSnathanw 	return retval;
985c62a74e6Sthorpej }
986c62a74e6Sthorpej 
987c62a74e6Sthorpej 
988c62a74e6Sthorpej int
989c62a74e6Sthorpej pthread_setcanceltype(int type, int *oldtype)
990c62a74e6Sthorpej {
991c62a74e6Sthorpej 	pthread_t self;
9920878df5dSnathanw 	int retval;
993c62a74e6Sthorpej 
994c62a74e6Sthorpej 	self = pthread__self();
9950878df5dSnathanw 	retval = 0;
9960878df5dSnathanw 
9970878df5dSnathanw 	pthread_spinlock(self, &self->pt_flaglock);
998c62a74e6Sthorpej 
999c62a74e6Sthorpej 	if (oldtype != NULL) {
10000878df5dSnathanw 		if (self->pt_flags & PT_FLAG_CS_ASYNC)
1001c62a74e6Sthorpej 			*oldtype = PTHREAD_CANCEL_ASYNCHRONOUS;
1002c62a74e6Sthorpej 		else
1003c62a74e6Sthorpej 			*oldtype = PTHREAD_CANCEL_DEFERRED;
1004c62a74e6Sthorpej 	}
1005c62a74e6Sthorpej 
1006c62a74e6Sthorpej 	if (type == PTHREAD_CANCEL_ASYNCHRONOUS) {
10070878df5dSnathanw 		self->pt_flags |= PT_FLAG_CS_ASYNC;
10080878df5dSnathanw 		if (self->pt_cancel) {
10090878df5dSnathanw 			pthread_spinunlock(self, &self->pt_flaglock);
1010c62a74e6Sthorpej 			pthread_exit(PTHREAD_CANCELED);
10110878df5dSnathanw 		}
1012c62a74e6Sthorpej 	} else if (type == PTHREAD_CANCEL_DEFERRED)
10130878df5dSnathanw 		self->pt_flags &= ~PT_FLAG_CS_ASYNC;
1014c62a74e6Sthorpej 	else
10150878df5dSnathanw 		retval = EINVAL;
1016c62a74e6Sthorpej 
10170878df5dSnathanw 	pthread_spinunlock(self, &self->pt_flaglock);
10180878df5dSnathanw 	return retval;
1019c62a74e6Sthorpej }
1020c62a74e6Sthorpej 
1021c62a74e6Sthorpej 
1022c62a74e6Sthorpej void
1023c62a74e6Sthorpej pthread_testcancel()
1024c62a74e6Sthorpej {
1025c62a74e6Sthorpej 	pthread_t self;
1026c62a74e6Sthorpej 
1027c62a74e6Sthorpej 	self = pthread__self();
1028c62a74e6Sthorpej 	if (self->pt_cancel)
1029c62a74e6Sthorpej 		pthread_exit(PTHREAD_CANCELED);
1030c62a74e6Sthorpej }
1031c62a74e6Sthorpej 
1032c62a74e6Sthorpej 
1033c62a74e6Sthorpej /*
1034c62a74e6Sthorpej  * POSIX requires that certain functions return an error rather than
1035c62a74e6Sthorpej  * invoking undefined behavior even when handed completely bogus
1036c62a74e6Sthorpej  * pthread_t values, e.g. stack garbage or (pthread_t)666. This
1037c62a74e6Sthorpej  * utility routine searches the list of threads for the pthread_t
1038c62a74e6Sthorpej  * value without dereferencing it.
1039c62a74e6Sthorpej  */
1040c62a74e6Sthorpej int
1041c62a74e6Sthorpej pthread__find(pthread_t self, pthread_t id)
1042c62a74e6Sthorpej {
1043c62a74e6Sthorpej 	pthread_t target;
1044c62a74e6Sthorpej 
1045c62a74e6Sthorpej 	pthread_spinlock(self, &pthread__allqueue_lock);
1046c62a74e6Sthorpej 	PTQ_FOREACH(target, &pthread__allqueue, pt_allq)
1047c62a74e6Sthorpej 	    if (target == id)
1048c62a74e6Sthorpej 		    break;
1049c62a74e6Sthorpej 	pthread_spinunlock(self, &pthread__allqueue_lock);
1050c62a74e6Sthorpej 
1051c62a74e6Sthorpej 	if (target == NULL)
1052c62a74e6Sthorpej 		return ESRCH;
1053c62a74e6Sthorpej 
1054c62a74e6Sthorpej 	return 0;
1055c62a74e6Sthorpej }
1056c62a74e6Sthorpej 
1057c62a74e6Sthorpej 
1058c62a74e6Sthorpej void
1059c62a74e6Sthorpej pthread__testcancel(pthread_t self)
1060c62a74e6Sthorpej {
1061c62a74e6Sthorpej 
1062c62a74e6Sthorpej 	if (self->pt_cancel)
1063c62a74e6Sthorpej 		pthread_exit(PTHREAD_CANCELED);
1064c62a74e6Sthorpej }
1065c62a74e6Sthorpej 
1066c62a74e6Sthorpej 
1067c62a74e6Sthorpej void
1068c62a74e6Sthorpej pthread__cleanup_push(void (*cleanup)(void *), void *arg, void *store)
1069c62a74e6Sthorpej {
1070c62a74e6Sthorpej 	pthread_t self;
1071c62a74e6Sthorpej 	struct pt_clean_t *entry;
1072c62a74e6Sthorpej 
1073c62a74e6Sthorpej 	self = pthread__self();
1074c62a74e6Sthorpej 	entry = store;
1075c62a74e6Sthorpej 	entry->ptc_cleanup = cleanup;
1076c62a74e6Sthorpej 	entry->ptc_arg = arg;
1077c62a74e6Sthorpej 	PTQ_INSERT_HEAD(&self->pt_cleanup_stack, entry, ptc_next);
1078c62a74e6Sthorpej }
1079c62a74e6Sthorpej 
1080c62a74e6Sthorpej 
1081c62a74e6Sthorpej void
1082c62a74e6Sthorpej pthread__cleanup_pop(int ex, void *store)
1083c62a74e6Sthorpej {
1084c62a74e6Sthorpej 	pthread_t self;
1085c62a74e6Sthorpej 	struct pt_clean_t *entry;
1086c62a74e6Sthorpej 
1087c62a74e6Sthorpej 	self = pthread__self();
1088c62a74e6Sthorpej 	entry = store;
1089c62a74e6Sthorpej 
1090c62a74e6Sthorpej 	PTQ_REMOVE(&self->pt_cleanup_stack, entry, ptc_next);
1091c62a74e6Sthorpej 	if (ex)
1092c62a74e6Sthorpej 		(*entry->ptc_cleanup)(entry->ptc_arg);
1093c62a74e6Sthorpej }
1094c62a74e6Sthorpej 
1095c62a74e6Sthorpej 
1096c62a74e6Sthorpej int *
1097c62a74e6Sthorpej pthread__errno(void)
1098c62a74e6Sthorpej {
1099c62a74e6Sthorpej 	pthread_t self;
1100c62a74e6Sthorpej 
1101c62a74e6Sthorpej 	self = pthread__self();
1102c62a74e6Sthorpej 
1103c62a74e6Sthorpej 	return &(self->pt_errno);
1104c62a74e6Sthorpej }
11058bcff70bSnathanw 
11060c967901Snathanw ssize_t	_sys_write(int, const void *, size_t);
11070c967901Snathanw 
11088bcff70bSnathanw void
11090e6c93b9Sdrochner pthread__assertfunc(const char *file, int line, const char *function,
11100e6c93b9Sdrochner 		    const char *expr)
11118bcff70bSnathanw {
11128bcff70bSnathanw 	char buf[1024];
11138bcff70bSnathanw 	int len;
11148bcff70bSnathanw 
111575a94788Smycroft 	SDPRINTF(("(af)\n"));
111675a94788Smycroft 
11178bcff70bSnathanw 	/*
11188bcff70bSnathanw 	 * snprintf should not acquire any locks, or we could
11198bcff70bSnathanw 	 * end up deadlocked if the assert caller held locks.
11208bcff70bSnathanw 	 */
11218bcff70bSnathanw 	len = snprintf(buf, 1024,
11228bcff70bSnathanw 	    "assertion \"%s\" failed: file \"%s\", line %d%s%s%s\n",
11238bcff70bSnathanw 	    expr, file, line,
11248bcff70bSnathanw 	    function ? ", function \"" : "",
11258bcff70bSnathanw 	    function ? function : "",
11268bcff70bSnathanw 	    function ? "\"" : "");
11278bcff70bSnathanw 
11280c967901Snathanw 	_sys_write(STDERR_FILENO, buf, (size_t)len);
11298bcff70bSnathanw 	(void)kill(getpid(), SIGABRT);
11308bcff70bSnathanw 
11318bcff70bSnathanw 	_exit(1);
11328bcff70bSnathanw }
1133df277271Snathanw 
1134df277271Snathanw 
1135df277271Snathanw void
11360e6c93b9Sdrochner pthread__errorfunc(const char *file, int line, const char *function,
11370e6c93b9Sdrochner 		   const char *msg)
1138df277271Snathanw {
1139df277271Snathanw 	char buf[1024];
11400172694eSnathanw 	size_t len;
1141df277271Snathanw 
11420172694eSnathanw 	if (pthread__diagassert == 0)
1143df277271Snathanw 		return;
1144df277271Snathanw 
1145df277271Snathanw 	/*
1146df277271Snathanw 	 * snprintf should not acquire any locks, or we could
1147df277271Snathanw 	 * end up deadlocked if the assert caller held locks.
1148df277271Snathanw 	 */
1149df277271Snathanw 	len = snprintf(buf, 1024,
11500172694eSnathanw 	    "%s: Error detected by libpthread: %s.\n"
11510172694eSnathanw 	    "Detected by file \"%s\", line %d%s%s%s.\n"
11520172694eSnathanw 	    "See pthread(3) for information.\n",
11530172694eSnathanw 	    getprogname(), msg, file, line,
1154df277271Snathanw 	    function ? ", function \"" : "",
1155df277271Snathanw 	    function ? function : "",
11560172694eSnathanw 	    function ? "\"" : "");
1157df277271Snathanw 
11580172694eSnathanw 	if (pthread__diagassert & DIAGASSERT_STDERR)
11590c967901Snathanw 		_sys_write(STDERR_FILENO, buf, len);
11600172694eSnathanw 
11610172694eSnathanw 	if (pthread__diagassert & DIAGASSERT_SYSLOG)
11620172694eSnathanw 		syslog(LOG_DEBUG | LOG_USER, "%s", buf);
11630172694eSnathanw 
11640172694eSnathanw 	if (pthread__diagassert & DIAGASSERT_ABORT) {
1165df277271Snathanw 		(void)kill(getpid(), SIGABRT);
1166df277271Snathanw 		_exit(1);
1167df277271Snathanw 	}
1168df277271Snathanw }
1169