summaryrefslogtreecommitdiff
path: root/lib/libpthread
diff options
context:
space:
mode:
authorthorpej <thorpej@NetBSD.org>2003-01-18 10:32:11 +0000
committerthorpej <thorpej@NetBSD.org>2003-01-18 10:32:11 +0000
commitc62a74e6d5cfb2fb5034e98b983da7e2fc87709d (patch)
treef1281fd7111ddb57951e4472b342b53a2f56244a /lib/libpthread
parentaf919042eceb8c19f42acd688efa8cf58168af5d (diff)
Merge the nathanw_sa branch.
Diffstat (limited to 'lib/libpthread')
-rw-r--r--lib/libpthread/Makefile79
-rw-r--r--lib/libpthread/TODO63
-rw-r--r--lib/libpthread/arch/alpha/_context_u.S126
-rw-r--r--lib/libpthread/arch/alpha/genassym.cf76
-rw-r--r--lib/libpthread/arch/alpha/pthread_md.h91
-rw-r--r--lib/libpthread/arch/alpha/pthread_switch.S293
-rw-r--r--lib/libpthread/arch/arm/_context_u.S137
-rw-r--r--lib/libpthread/arch/arm/genassym.cf69
-rw-r--r--lib/libpthread/arch/arm/pthread_md.h138
-rw-r--r--lib/libpthread/arch/arm/pthread_switch.S410
-rw-r--r--lib/libpthread/arch/i386/_context_u.S101
-rw-r--r--lib/libpthread/arch/i386/_getsetc.S100
-rw-r--r--lib/libpthread/arch/i386/genassym.cf86
-rw-r--r--lib/libpthread/arch/i386/pthread_md.c76
-rw-r--r--lib/libpthread/arch/i386/pthread_md.h153
-rw-r--r--lib/libpthread/arch/i386/pthread_switch.S367
-rw-r--r--lib/libpthread/arch/m68k/_context_u.S132
-rw-r--r--lib/libpthread/arch/m68k/genassym.cf89
-rw-r--r--lib/libpthread/arch/m68k/pthread_md.c91
-rw-r--r--lib/libpthread/arch/m68k/pthread_md.h124
-rw-r--r--lib/libpthread/arch/m68k/pthread_switch.S323
-rw-r--r--lib/libpthread/arch/mips/SYS.h131
-rw-r--r--lib/libpthread/arch/mips/_context_u.S128
-rw-r--r--lib/libpthread/arch/mips/genassym.cf63
-rw-r--r--lib/libpthread/arch/mips/pthread_md.h113
-rw-r--r--lib/libpthread/arch/mips/pthread_switch.S302
-rw-r--r--lib/libpthread/arch/powerpc/_context_u.S167
-rw-r--r--lib/libpthread/arch/powerpc/genassym.cf74
-rw-r--r--lib/libpthread/arch/powerpc/pthread_md.h106
-rw-r--r--lib/libpthread/arch/powerpc/pthread_switch.S353
-rw-r--r--lib/libpthread/arch/sparc/_context_u.S99
-rw-r--r--lib/libpthread/arch/sparc/genassym.cf56
-rw-r--r--lib/libpthread/arch/sparc/pthread_md.h83
-rw-r--r--lib/libpthread/arch/sparc/pthread_switch.S301
-rw-r--r--lib/libpthread/arch/sparc64/_context_u.S97
-rw-r--r--lib/libpthread/arch/sparc64/genassym.cf56
-rw-r--r--lib/libpthread/arch/sparc64/pthread_md.h86
-rw-r--r--lib/libpthread/arch/sparc64/pthread_switch.S290
-rw-r--r--lib/libpthread/debuglog.c157
-rw-r--r--lib/libpthread/genassym.sh155
-rw-r--r--lib/libpthread/pthread.c835
-rw-r--r--lib/libpthread/pthread.h190
-rw-r--r--lib/libpthread/pthread_alarms.c211
-rw-r--r--lib/libpthread/pthread_barrier.c232
-rw-r--r--lib/libpthread/pthread_cancelstub.c344
-rw-r--r--lib/libpthread/pthread_cond.c344
-rw-r--r--lib/libpthread/pthread_debug.c168
-rw-r--r--lib/libpthread/pthread_debug.h107
-rw-r--r--lib/libpthread/pthread_int.h349
-rw-r--r--lib/libpthread/pthread_lock.c368
-rw-r--r--lib/libpthread/pthread_mutex.c458
-rw-r--r--lib/libpthread/pthread_queue.h140
-rw-r--r--lib/libpthread/pthread_run.c229
-rw-r--r--lib/libpthread/pthread_rwlock.c455
-rw-r--r--lib/libpthread/pthread_sa.c734
-rw-r--r--lib/libpthread/pthread_sig.c612
-rw-r--r--lib/libpthread/pthread_specific.c257
-rw-r--r--lib/libpthread/pthread_stack.c146
-rw-r--r--lib/libpthread/pthread_types.h242
-rw-r--r--lib/libpthread/sched.c93
-rw-r--r--lib/libpthread/shlib_version5
61 files changed, 12460 insertions, 0 deletions
diff --git a/lib/libpthread/Makefile b/lib/libpthread/Makefile
new file mode 100644
index 00000000000..f6946e67ff4
--- /dev/null
+++ b/lib/libpthread/Makefile
@@ -0,0 +1,79 @@
+# $NetBSD: Makefile,v 1.9 2003/01/18 10:34:14 thorpej Exp $
+#
+
+WARNS= 2
+
+.include <bsd.own.mk>
+
+.if exists(${.CURDIR}/arch/${MACHINE_ARCH})
+ARCHSUBDIR= ${MACHINE_ARCH}
+.elif exists(${.CURDIR}/arch/${MACHINE_CPU})
+ARCHSUBDIR= ${MACHINE_CPU}
+.else
+.BEGIN:
+ @echo "no ARCHSUBDIR for ${MACHINE_ARCH}/${MACHINE_CPU}; skipping..."
+.endif
+
+.if defined(ARCHSUBDIR)
+
+ARCHDIR= ${.CURDIR}/arch/${ARCHSUBDIR}
+.PATH: ${ARCHDIR}
+
+CPPFLAGS+= -I${ARCHDIR} -I${.CURDIR} -I${.OBJDIR}
+
+DPSRCS+= assym.h
+
+assym.h: genassym.sh ${ARCHDIR}/genassym.cf
+ sh ${.CURDIR}/genassym.sh ${CC} ${CFLAGS} ${CPPFLAGS} ${PROF} \
+ < ${ARCHDIR}/genassym.cf > assym.h.tmp && \
+ mv -f assym.h.tmp assym.h
+
+LIB= pthread
+
+#
+# NOTE: When you create a new file for libpthread, make sure that pthread.c
+# gets a reference to a symbol in that file. Otherwise, Unix's stupid static
+# library semantics will end up discarding potentially important objects.
+#
+SRCS= pthread.c
+SRCS+= pthread_alarms.c
+SRCS+= pthread_barrier.c
+SRCS+= pthread_cancelstub.c
+SRCS+= pthread_cond.c
+SRCS+= pthread_lock.c
+SRCS+= pthread_mutex.c
+SRCS+= pthread_run.c
+SRCS+= pthread_rwlock.c
+SRCS+= pthread_sa.c
+SRCS+= pthread_sig.c
+SRCS+= pthread_specific.c
+SRCS+= pthread_stack.c
+SRCS+= pthread_debug.c
+SRCS+= sched.c
+# Architecture-dependent files
+SRCS+= pthread_switch.S _context_u.S
+.if exists(${ARCHDIR}/pthread_md.c)
+SRCS+= pthread_md.c
+.endif
+
+pthread_switch.S _context_u.S: assym.h
+
+INCS= pthread.h pthread_types.h pthread_queue.h
+INCSDIR=/usr/include
+
+debuglog: debuglog.o
+ $(CC) -o debuglog debuglog.o -lpthread
+
+.include <bsd.lib.mk>
+
+.else
+
+.include <bsd.man.mk>
+
+.endif
+
+# WARNS=2 sets -Wcast-qual. This causes problems for one of
+# pthread_setspecific() and pthread_getspecific(), since the constness
+# of the argument to setspecific() has to be discarded *somewhere*
+# before returning it from getspecific().
+CFLAGS+= -Wno-cast-qual
diff --git a/lib/libpthread/TODO b/lib/libpthread/TODO
new file mode 100644
index 00000000000..e3a3ed6f78c
--- /dev/null
+++ b/lib/libpthread/TODO
@@ -0,0 +1,63 @@
+Bugs to fix:
+
+- pthread_cond_timedwait() doesn't work if SA's aren't running yet,
+ because the alarm system isn't up and running. It would be weird to
+ use them that way, but it's perfectly legal.
+- There is a race between pthread_cancel() and
+ pthread_cond_broadcast() or pthread_exit() about removing an item
+ from the sleep queue. The locking protocols there need a little
+ adjustment.
+- pthread_sig.c: pthread__signal_tramp() is broken. It gets a ucontext,
+ but then hands off an empty (garbage from stack!) sigcontext to the
+ handler. The ucontext MUST be converted to a sigcontext, the handler
+ called, and then the sigcontext converted back to ucontext, before the
+ ucontext is used to perform the sigreturn-analogue. This is necessary
+ for e.g. C++/Java exceptions and some garbage-collectors.
+- pthread_sig.c: pthread__signal_tramp() should be changed so that it is
+ easy to add support for SA_SIGINFO later. (Mostly a function signature
+ issue.)
+- pthread_sig.c: Come up with a signal trampoline naming convention like
+ libc's, so that GDB will have an easier time with things.
+- Consider moving pthread__signal_tramp() to its own file, and building
+ it with -fasync-unwind-tables, so that DWARF2 EH unwinding works through
+ it. (This is required for e.g. GCC's libjava.)
+- Add locking to ld.elf_so so that multiple threads doing lazy binding
+ doesn't trash things.
+- Verify the cancel stub symbol trickery.
+
+
+Interfaces/features to implement:
+- pthread_kill()
+- pthread_atfork()
+- priority scheduling
+- libc integration:
+ - foo_r interfaces
+- system integration
+ - some macros and prototypes belong in headers other than pthread.h
+
+
+Features that need more/better regression tests:
+ - pthread_cond_broadcast()
+ - pthread_once()
+ - pthread_get/setspecific()
+ - signals
+
+
+Things that need fixing:
+- Recycle dead threads for new threads.
+
+Ideas to play with:
+- Explore the trapcontext vs. usercontext distinction in ucontext_t.
+- Get rid of thread structures when too many accumulate (is this
+ actually a good idea?)
+- Adaptive spin/sleep locks for mutexes.
+- Currently, each thread uses two real pages of memory: one at the top
+ of the stack for actual stack data, and one at the bottom for the
+ pthread_st. If we can get suitable space above the initial stack for
+ main(), we can cut this to one page per thread. Perhaps crt0 should
+ do something different (give us more space) if libpthread is linked
+ in?
+- Figure out whether/how to expose the inline version of
+ pthread_self().
+- Along the same lines, figure out whether/how to use registers reserved
+ in the ABI for thread-specific-data to implement pthread_self().
diff --git a/lib/libpthread/arch/alpha/_context_u.S b/lib/libpthread/arch/alpha/_context_u.S
new file mode 100644
index 00000000000..2004cd2845a
--- /dev/null
+++ b/lib/libpthread/arch/alpha/_context_u.S
@@ -0,0 +1,126 @@
+/* $NetBSD: _context_u.S,v 1.2 2003/01/18 10:34:17 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nathan J. Williams.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <machine/asm.h>
+#include "assym.h"
+
+/*
+ * Only save the callee-save regs, and set a special flag in uc_flags
+ * that says so.
+ */
+#define GETC(reg) ; \
+ stq s0, (UC_GREGS + _REG_S0 * 8)(reg) ; \
+ stq s1, (UC_GREGS + _REG_S1 * 8)(reg) ; \
+ stq s2, (UC_GREGS + _REG_S2 * 8)(reg) ; \
+ stq s3, (UC_GREGS + _REG_S3 * 8)(reg) ; \
+ stq s4, (UC_GREGS + _REG_S4 * 8)(reg) ; \
+ stq s5, (UC_GREGS + _REG_S5 * 8)(reg) ; \
+ stq s6, (UC_GREGS + _REG_S6 * 8)(reg) ; \
+ stq sp, (UC_GREGS + _REG_SP * 8)(reg) ; \
+ stq ra, (UC_GREGS + _REG_RA * 8)(reg) ; \
+ stq ra, (UC_GREGS + _REG_PC * 8)(reg) ; \
+ call_pal PAL_rdunique ; \
+ stq v0, (UC_GREGS + _REG_UNIQUE * 8)(reg) ; \
+ stt $f2, (UC_FPREGS + 2 * 8)(reg) ; \
+ stt $f3, (UC_FPREGS + 3 * 8)(reg) ; \
+ stt $f4, (UC_FPREGS + 4 * 8)(reg) ; \
+ stt $f5, (UC_FPREGS + 5 * 8)(reg) ; \
+ stt $f6, (UC_FPREGS + 6 * 8)(reg) ; \
+ stt $f7, (UC_FPREGS + 7 * 8)(reg) ; \
+ stt $f8, (UC_FPREGS + 8 * 8)(reg) ; \
+ stt $f9, (UC_FPREGS + 9 * 8)(reg) ; \
+ ldiq t0, 8 ; \
+ stq t0, (UC_GREGS + _REG_PS * 8)(reg) ; \
+ ldiq t0, 1 ; \
+ sll t0, _UC_USER_BIT, t0 ; \
+ bis t0, (_UC_CPU|_UC_FPU|_UC_UNIQUE), t0 ; \
+ stl t0, (UC_FLAGS)(reg)
+
+#define SETC(reg) ; \
+ ldl t0, (UC_FLAGS)(reg) ; \
+ ldiq t1, 1 ; \
+ sll t1, _UC_USER_BIT, t1 ; \
+ and t0, t1, t0 ; \
+ beq t0, 1f ; \
+ ; \
+ /* _UC_USER implies _UC_UNIQUE */ ; \
+ ldq a0, (UC_GREGS + _REG_UNIQUE * 8)(reg) ; \
+ call_pal PAL_wrunique ; \
+ ldq s0, (UC_GREGS + _REG_S0 * 8)(reg) ; \
+ ldq s1, (UC_GREGS + _REG_S1 * 8)(reg) ; \
+ ldq s2, (UC_GREGS + _REG_S2 * 8)(reg) ; \
+ ldq s3, (UC_GREGS + _REG_S3 * 8)(reg) ; \
+ ldq s4, (UC_GREGS + _REG_S4 * 8)(reg) ; \
+ ldq s5, (UC_GREGS + _REG_S5 * 8)(reg) ; \
+ ldq s6, (UC_GREGS + _REG_S6 * 8)(reg) ; \
+ ldq ra, (UC_GREGS + _REG_RA * 8)(reg) ; \
+ ldq t12, (UC_GREGS + _REG_PC * 8)(reg) ; \
+ ldt $f2, (UC_FPREGS + 2 * 8)(reg) ; \
+ ldt $f3, (UC_FPREGS + 3 * 8)(reg) ; \
+ ldt $f4, (UC_FPREGS + 4 * 8)(reg) ; \
+ ldt $f5, (UC_FPREGS + 5 * 8)(reg) ; \
+ ldt $f6, (UC_FPREGS + 6 * 8)(reg) ; \
+ ldt $f7, (UC_FPREGS + 7 * 8)(reg) ; \
+ ldt $f8, (UC_FPREGS + 8 * 8)(reg) ; \
+ ldt $f9, (UC_FPREGS + 9 * 8)(reg) ; \
+ ldq sp, (UC_GREGS + _REG_SP * 8)(reg) ; \
+ ; \
+ /* part procedure call, part RET */ ; \
+ jmp zero, (t12) ; \
+ /* NOTREACHED */ ; \
+1: mov reg, a0 ; \
+ CALL(setcontext) ; \
+ /* NOTREACHED */
+
+LEAF(_getcontext_u,1)
+ GETC(a0)
+ mov zero, v0
+ RET
+ END(_getcontext_u)
+
+NESTED(_setcontext_u,1,0,ra,0,0)
+ LDGP(pv)
+ mov a0, a1
+ SETC(a1)
+ END(_setcontext_u)
+
+NESTED(_swapcontext_u,2,0,ra,0,0)
+ LDGP(pv)
+ GETC(a0)
+ SETC(a1)
+ END(_swapcontext_u)
diff --git a/lib/libpthread/arch/alpha/genassym.cf b/lib/libpthread/arch/alpha/genassym.cf
new file mode 100644
index 00000000000..113f8390519
--- /dev/null
+++ b/lib/libpthread/arch/alpha/genassym.cf
@@ -0,0 +1,76 @@
+# $NetBSD: genassym.cf,v 1.2 2003/01/18 10:34:17 thorpej Exp $
+
+#
+# Copyright (c) 2001 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# This code is derived from software contributed to The NetBSD Foundation
+# by Nathan J. Williams.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the NetBSD
+# Foundation, Inc. and its contributors.
+# 4. Neither the name of The NetBSD Foundation nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+include <ucontext.h>
+include <sys/queue.h>
+include <machine/reg.h>
+include "pthread.h"
+include "pthread_int.h"
+include "pthread_md.h"
+
+define PT_NEXT offsetof(struct pthread_st, pt_next)
+define PT_STATE offsetof(struct pthread_st, pt_state)
+define PT_SWITCHTO offsetof(struct pthread_st, pt_switchto)
+define PT_SWITCHTOUC offsetof(struct pthread_st, pt_switchtouc)
+define PT_SLEEPUC offsetof(struct pthread_st, pt_sleepuc)
+define PT_SPINLOCKS offsetof(struct pthread_st, pt_spinlocks)
+define PT_HELDLOCK offsetof(struct pthread_st, pt_heldlock)
+define PT_UC offsetof(struct pthread_st, pt_uc)
+define CONTEXTSIZE sizeof(ucontext_t)
+define UC_FLAGS offsetof(ucontext_t, uc_flags)
+define UC_GREGS offsetof(ucontext_t, uc_mcontext.__gregs)
+define UC_FPREGS offsetof(ucontext_t, uc_mcontext.__fpregs)
+define PT_STATE_RECYCLABLE PT_STATE_RECYCLABLE
+define STACKSPACE STACKSPACE
+define _UC_USER_BIT _UC_USER_BIT
+define _UC_CPU _UC_CPU
+define _UC_FPU _UC_FPU
+define _UC_UNIQUE _UC_UNIQUE
+
+define _REG_S0 _REG_S0
+define _REG_S1 _REG_S1
+define _REG_S2 _REG_S2
+define _REG_S3 _REG_S3
+define _REG_S4 _REG_S4
+define _REG_S5 _REG_S5
+define _REG_S6 _REG_S6
+define _REG_SP _REG_SP
+define _REG_RA _REG_RA
+define _REG_PC _REG_PC
+define _REG_PS _REG_PS
+define _REG_UNIQUE _REG_UNIQUE
diff --git a/lib/libpthread/arch/alpha/pthread_md.h b/lib/libpthread/arch/alpha/pthread_md.h
new file mode 100644
index 00000000000..1bfaa4b3422
--- /dev/null
+++ b/lib/libpthread/arch/alpha/pthread_md.h
@@ -0,0 +1,91 @@
+/* $NetBSD: pthread_md.h,v 1.2 2003/01/18 10:34:17 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nathan J. Williams.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _LIB_PTHREAD_ALPHA_MD_H
+#define _LIB_PTHREAD_ALPHA_MD_H
+
+static __inline long
+pthread__sp(void)
+{
+ long ret;
+
+ __asm("mov $30, %0" : "=r" (ret));
+
+ return ret;
+}
+
+#define pthread__uc_sp(ucp) ((ucp)->uc_mcontext.__gregs[_REG_SP])
+#define pthread__uc_pc(ucp) ((ucp)->uc_mcontext.__gregs[_REG_PC])
+
+/*
+ * Set initial, sane values for registers whose values aren't just
+ * "don't care".
+ * 0x0008 is ALPHA_PSL_USERSET from arch/alpha/include/alpha_cpu.h
+ */
+#define _INITCONTEXT_U_MD(ucp) \
+ (ucp)->uc_mcontext.__gregs[_REG_PS] = 0x0008;
+#define STACKSPACE 32 /* 4 quad values */
+/*
+ * Conversions between struct reg and struct mcontext. Used by
+ * libpthread_dbg.
+ */
+
+#define PTHREAD_UCONTEXT_TO_REG(reg, uc) do { \
+ memcpy(&(reg)->r_regs, &(uc)->uc_mcontext.__gregs, \
+ 31 * sizeof(__greg_t)); \
+ (reg)->r_regs[R_ZERO] = (uc)->uc_mcontext.__gregs[_REG_PC]; \
+ } while (/*CONSTCOND*/0)
+
+#define PTHREAD_REG_TO_UCONTEXT(uc, reg) do { \
+ memcpy(&(uc)->uc_mcontext.__gregs, &(reg)->r_regs, \
+ 31 * sizeof(__greg_t)); \
+ (uc)->uc_mcontext.__gregs[_REG_PC] = (reg)->r_regs[R_ZERO]; \
+ (uc)->uc_flags = ((uc)->uc_flags | _UC_CPU) & ~_UC_USER; \
+ } while (/*CONSTCOND*/0)
+
+#define PTHREAD_UCONTEXT_TO_FPREG(freg, uc) \
+ memcpy((freg), &(uc)->uc_mcontext.__fpregs, \
+ sizeof(struct fpreg)) \
+
+#define PTHREAD_FPREG_TO_UCONTEXT(uc, freg) do { \
+ memcpy(&(uc)->uc_mcontext.__fpregs, (freg), \
+ sizeof(struct fpreg)); \
+ (uc)->uc_flags = ((uc)->uc_flags | _UC_FPU) & ~_UC_USER; \
+ } while (/*CONSTCOND*/0)
+
+#endif /* _LIB_PTHREAD_ALPHA_MD_H */
diff --git a/lib/libpthread/arch/alpha/pthread_switch.S b/lib/libpthread/arch/alpha/pthread_switch.S
new file mode 100644
index 00000000000..2616e2e3351
--- /dev/null
+++ b/lib/libpthread/arch/alpha/pthread_switch.S
@@ -0,0 +1,293 @@
+/* $NetBSD: pthread_switch.S,v 1.2 2003/01/18 10:34:17 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nathan J. Williams.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This file implements low-level routines that are exported to
+ * the machine-independent parts of the thread library. The routines are:
+ *
+ * void pthread__switch(pthread_t self, pthread_t next);
+ * void pthread__upcall_switch(pthread_t self, pthread_t next);
+ * void pthread__locked_switch(pthread_t self, pthread_t next,
+ * pt_spin_t *lock);
+ *
+ * as well as some utility code used by these routines.
+ */
+
+#include <machine/asm.h>
+#include "assym.h"
+
+/* Force an error when "notreached" code is reached. */
+#define NOTREACHED \
+ call_pal PAL_bugchk ; \
+ /* NOTREACHED */
+
+/* *** WARNING ***
+ * STACK_SWITCH is more subtle than it looks. Please go read the extended
+ * comment in the i386 pthread_switch.S file.
+ */
+
+#define STACK_SWITCH(pt,tmp) \
+ ldq tmp, PT_UC(pt) ; \
+ lda sp, -STACKSPACE(tmp)
+
+NESTED(pthread__switch, 2, CONTEXTSIZE + 24, ra, 0, 0)
+ LDGP(pv)
+ lda sp, -(CONTEXTSIZE + 24)(sp)
+ stq ra, 0(sp)
+ stq a0, 8(sp)
+ stq a1, 16(sp)
+
+ /* Get the current context */
+ lda a0, 24(sp)
+ CALL(_getcontext_u)
+ ldq a0, 8(sp)
+ ldq a1, 16(sp)
+
+ /* Edit the context to make it continue below, rather than here */
+ lda t3, 24(sp)
+ lda t1, switch_return_point
+ stq t1, (UC_GREGS + _REG_PC * 8)(t3)
+
+ lda t0, 24(sp)
+
+ STACK_SWITCH(a1, t2)
+
+ stq t0, PT_UC(a0)
+ mov t2, a0
+ CALL(_setcontext_u)
+switch_return_point:
+ ldq ra, 0(sp)
+ lda sp, (CONTEXTSIZE + 24)(sp)
+ RET
+ END(pthread__switch)
+
+/*
+ * Helper switch code used by pthread__locked_switch() and
+ * pthread__upcall_switch() when they discover spinlock preemption.
+ */
+
+NESTED(pthread__switch_away, 3, 0, ra, 0, 0)
+ STACK_SWITCH(a1, s2)
+
+ /* If we're invoked from the switch-to-next provisions of
+ * pthread__locked_switch or pthread__upcall_switch, there may
+ * be a fake spinlock-count set. If so, they will set a2 to
+ * let us know, and we decrement it now that we're no longer
+ * using the old stack.
+ */
+ beq a2, 1f
+ ldl t0, PT_SPINLOCKS(a0)
+ subl t0, 1, t0
+ stl t0, PT_SPINLOCKS(a0)
+
+1: mov s2, a0
+ CALL(_setcontext_u)
+ NOTREACHED
+ END(pthread__switch_away)
+
+NESTED(pthread__locked_switch, 3, CONTEXTSIZE + 32, ra, 0x00030000, 0)
+ LDGP(pv)
+ lda sp, -(CONTEXTSIZE + 32)(sp)
+ stq ra, 0(sp)
+ stq a0, 8(sp)
+ stq a1, 16(sp)
+ stq a2, 24(sp)
+
+ /* Make sure we get continuted */
+ ldl t0, PT_SPINLOCKS(a1)
+ addl t0, 1, t0
+ stl t0, PT_SPINLOCKS(a1)
+
+ /* Get the current context */
+ lda a0, 32(sp)
+ CALL(_getcontext_u)
+ ldq a0, 8(sp)
+ ldq a1, 16(sp)
+ ldq a2, 24(sp)
+
+ /* Edit the context to make it continue below, rather than here */
+ lda t3, 32(sp)
+ lda t1, locked_return_point
+ stq t1, (UC_GREGS + _REG_PC * 8)(t3)
+
+ lda t0, 32(sp)
+
+ STACK_SWITCH(a1, t2)
+
+ stq t0, PT_UC(a0)
+ /* Check if the switcher was preempted and continued to here. */
+ ldq t0, PT_NEXT(a0)
+ beq t0, 1f
+
+ /* Yes, it was. Stash the thread we were going to switch to,
+ * the lock the original thread was holding,
+ * and switch to the next thread in the continuation chain.
+ * Mark the fact that this was a locked switch, and so the
+ * thread does not need to be put on a run queue.
+ * Don't release the lock. It's possible that if we do so,
+ * PT_SWITCHTO will be stomped by another switch_lock and
+ * preemption.
+ */
+ stq a1, PT_SWITCHTO(a0)
+ stq t2, PT_SWITCHTOUC(a0)
+ stq a2, PT_HELDLOCK(a0)
+ ldl t4, PT_SPINLOCKS(a0)
+ subl t4, 1, t4
+ stl t4, PT_SPINLOCKS(a0)
+
+ /* Save the context we previously stored in PT_UC(a0);
+ * that was overwritten when we were preempted and continued,
+ * so we need to put it somewhere.
+ */
+ stq t3, PT_SLEEPUC(a0)
+
+ mov a1, a0
+ mov t0, a1
+ mov 1, a2
+ jmp zero, pthread__switch_away
+ NOTREACHED
+
+ /* No locked old-preemption */
+1: /* We've moved to the new stack, and the old context has been
+ * saved. The queue lock can be released.
+ */
+ /* Reduce the lock count... */
+ ldl t0, PT_SPINLOCKS(a0)
+ subl t0, 1, t0
+ stl t0, PT_SPINLOCKS(a0)
+ /* ... actually release the lock.. */
+ mb
+ stl zero, 0(a2)
+ /* .. and remove the fake lock */
+ ldl t0, PT_SPINLOCKS(a1)
+ subl t0, 1, t0
+ stl t0, PT_SPINLOCKS(a1)
+
+ /* Check if we were preempted while holding the fake lock. */
+ ldq t0, PT_NEXT(a1)
+ beq t0, 2f
+
+ /* Yes, we were. Go to the next element in the chain. */
+ stq a1, PT_SWITCHTO(a1)
+ stq t2, PT_SWITCHTOUC(a1)
+ mov a1, a0
+ mov t0, a1
+ mov zero, a2
+ jmp zero, pthread__switch_away
+ NOTREACHED
+
+2: mov t2, a0
+ CALL(_setcontext_u)
+locked_return_point:
+ ldq ra, 0(sp)
+ lda sp, (CONTEXTSIZE + 32)(sp)
+ RET
+ END(pthread__locked_switch)
+
+/*
+ * void pthread__upcall_switch(pthread_t self, pthread_t next);
+ *
+ * Quit an upcall, recycle it, and jump to the selected thread.
+ */
+
+NESTED(pthread__upcall_switch,2,0,ra,0,0)
+ LDGP(pv)
+ /* Loading the global pointer is unnecessary; this routine
+ * is only ever called from the pthreads module, and the C
+ * code will have gp set up.
+ *
+ * Also, this code never returns, so we can treat s0-s6 as
+ * convenient registers that will be saved for us by callees,
+ * but that we do not have to save.
+ */
+
+ /* Create a "fake" lock count so that this code will be continued */
+ ldl t0, PT_SPINLOCKS(a1)
+ addl t0, 1, t0
+ stl t0, PT_SPINLOCKS(a1)
+
+ STACK_SWITCH(a1,s2)
+
+ /* Check if the upcall code was preempted and continued to here. */
+ ldq t0, PT_NEXT(a0)
+ beq t0, 1f
+
+ /* Yes, it was. Stash the thread we were going to switch to,
+ * and switch to the next thread in the continuation chain.
+ */
+ stq a1, PT_SWITCHTO(a0)
+ stq s2, PT_SWITCHTOUC(a0)
+ mov PT_STATE_RECYCLABLE, t1
+ stl t1, PT_STATE(a0)
+ mov a1, a0
+ mov t0, a1
+ mov 1, a2
+ jmp zero, pthread__switch_away
+ NOTREACHED
+
+ /* No old-upcall-preemption */
+1: mov a0, s0
+ mov a1, s1
+ CALL(pthread__sa_recycle)
+ mov s0, a0
+ mov s1, a1
+
+ /* We can now release the fake lock. */
+ ldl t0, PT_SPINLOCKS(a1)
+ subl t0, 1, t0
+ stl t0, PT_SPINLOCKS(a1)
+
+ /* Check if we were preempted and continued while faking the lock */
+ ldq t0, PT_NEXT(a1)
+ beq t0, 2f
+
+ /* Yes, we were. Stash the to-be-switched-to context in our thread
+ * structure and go to the next link in the chain.
+ */
+ stq a1, PT_SWITCHTO(a1)
+ stq s2, PT_SWITCHTOUC(a1)
+ mov a1, a0
+ mov t0, a1
+ mov 0, a2
+ jmp zero, pthread__switch_away
+
+ /* No new-upcall-preemption */
+2: mov s2, a0
+ CALL(_setcontext_u)
+ NOTREACHED
+ END(pthread__upcall_switch)
diff --git a/lib/libpthread/arch/arm/_context_u.S b/lib/libpthread/arch/arm/_context_u.S
new file mode 100644
index 00000000000..3c29e860afc
--- /dev/null
+++ b/lib/libpthread/arch/arm/_context_u.S
@@ -0,0 +1,137 @@
+/* $NetBSD: _context_u.S,v 1.2 2003/01/18 10:34:18 thorpej Exp $ */
+
+/*
+ * Copyright (c) 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Jason R. Thorpe for Wasabi Systems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the NetBSD Project by
+ * Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <machine/asm.h>
+#include <arm/armreg.h>
+#include "assym.h"
+
+/*
+ * Define:
+ * int _getcontext_u(ucontext_t *ctx)
+ * Store the current context in the provided ctx structure.
+ * [only store the callee-saved registers]
+ * int _setcontext_u(const ucontext_t *ctx)
+ * Restore the current context from the provided ctx structure.
+ * int _swapcontext_u(ucontext_t *from_ctx, const ucontext_t *to_ctx)
+ * First, store the current context into from_ctx and then
+ * restore the current context from the to_ctx.
+ */
+
+/*
+ * XXX XXX XXX
+ * XXX Hardware FP context?
+ * XXX Software FP context?
+ * XXX XXX XXX
+ */
+
+/*
+ * NOTE: Clobbers r2.
+ */
+#define GETC(reg) \
+ /* \
+ * We're only saving the callee-saved registers, here, \
+ * so make a note of it for SETC() later. \
+ * \
+ * And, yes, even if we're only saving callee-saved regs, \
+ * we need to provide a valid CPSR in case the context is \
+ * used in a setcontext(2) system call later. (The CPSR \
+ * is only used in 32-bit mode, and ignored in 26-bit mode.) \
+ * \
+ * We play a trick with the PC, here. We don't save the \
+ * LR (it's caller-save), but we instead stash it in the \
+ * PC slot in the ucontext so that when the context is \
+ * resumed, we magically appear back at the call site. \
+ */ \
+ mov r2, #(_UC_USER | _UC_CPU) ; \
+ str r2, [reg, #(UC_FLAGS)] ; \
+ mov r2, #0 ; \
+ mrs r2, cpsr /* No-op on 26-bit systems. */ ; \
+ str r2, [reg, #(_REG_CPSR)] ; \
+ str lr, [reg, #(_REG_PC)] ; \
+ add reg, reg, #(_REG_R4) ; \
+ stmia reg, {r4-r13}
+
+/*
+ * NOTE: Clobbers r2.
+ */
+#define SETC(reg) \
+ /* \
+ * If _UC_USER is set, we only need to restore the \
+ * callee-saved registers (i.e. we were saved by \
+ * _getcontext_u(). If it is not set, we need to \
+ * restore the caller-saved regs, too. \
+ */ \
+ ldr r2, [reg, #(UC_FLAGS)] ; \
+ tst r2, #(_UC_USER) ; \
+ \
+ /* _UC_USER case */ ; \
+ addne reg, reg, #(_REG_R4) ; \
+ ldmneia reg, {r4-r15} ; \
+ /* NOTREACHED */ \
+ \
+ /* ! _UC_USER case */ \
+ teq r0, r0 /* set Z */ ; \
+ teq pc, r15 /* EQ => 32, NE => 26 */ ; \
+ bne 1f ; \
+ \
+ /* 32-bit */ \
+ ldr r2, [reg, #(_REG_CPSR)] ; \
+ add reg, reg, #(_REG_R0) ; \
+ msr cpsr, r2 ; \
+ ldmia reg, {r0-r15} ; \
+ /* NOTREACHED */ \
+ \
+1: /* 26-bit */ \
+ add reg, reg, #(_REG_R0) ; \
+ ldmia reg, {r0-r15}^ ; \
+ /* NOTREACHED */
+
+ENTRY(_getcontext_u)
+ GETC(r0)
+ mov r0, #0x00000000
+#ifdef __APCS_26__
+ movs pc, lr
+#else
+ mov pc, lr
+#endif
+
+ENTRY(_setcontext_u)
+ SETC(r0)
+
+ENTRY(_swapcontext_u)
+ GETC(r0)
+ SETC(r1)
diff --git a/lib/libpthread/arch/arm/genassym.cf b/lib/libpthread/arch/arm/genassym.cf
new file mode 100644
index 00000000000..97d9de1c960
--- /dev/null
+++ b/lib/libpthread/arch/arm/genassym.cf
@@ -0,0 +1,69 @@
+# $NetBSD: genassym.cf,v 1.2 2003/01/18 10:34:18 thorpej Exp $
+
+# Copyright (c) 2001 Wasabi Systems, Inc.
+# All rights reserved.
+#
+# Written by Jason R. Thorpe for Wasabi Systems, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed for the NetBSD Project by
+# Wasabi Systems, Inc.
+# 4. The name of Wasabi Systems, Inc. may not be used to endorse
+# or promote products derived from this software without specific prior
+# written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+include <ucontext.h>
+include <sys/lock.h>
+include <sys/queue.h>
+include "pthread.h"
+include "pthread_int.h"
+include "pthread_md.h"
+
+# for _context_u.S
+define UC_FLAGS offsetof(ucontext_t, uc_flags)
+define _REG_R0 offsetof(ucontext_t, uc_mcontext.__gregs[_REG_R0])
+define _REG_R4 offsetof(ucontext_t, uc_mcontext.__gregs[_REG_R4])
+define _REG_PC offsetof(ucontext_t, uc_mcontext.__gregs[_REG_PC])
+define _REG_CPSR offsetof(ucontext_t, uc_mcontext.__gregs[_REG_CPSR])
+
+# uc_flags
+define _UC_CPU _UC_CPU
+define _UC_USER _UC_USER
+
+#========================================================================
+# for pthread_switch.S
+define PT_UC offsetof(struct pthread_st, pt_uc)
+define PT_NEXT offsetof(struct pthread_st, pt_next)
+define PT_SPINLOCKS offsetof(struct pthread_st, pt_spinlocks)
+define PT_SWITCHTO offsetof(struct pthread_st, pt_switchto)
+define PT_SWITCHTOUC offsetof(struct pthread_st, pt_switchtouc)
+define PT_STATE offsetof(struct pthread_st, pt_state)
+define PT_HELDLOCK offsetof(struct pthread_st, pt_heldlock)
+define PT_SLEEPUC offsetof(struct pthread_st, pt_sleepuc)
+
+define PT_STATE_RECYCLABLE PT_STATE_RECYCLABLE
+define RND_CTXSIZE ((sizeof(ucontext_t) + 7) & ~7)
+define STACKSPACE STACKSPACE
+define __SIMPLELOCK_UNLOCKED __SIMPLELOCK_UNLOCKED
diff --git a/lib/libpthread/arch/arm/pthread_md.h b/lib/libpthread/arch/arm/pthread_md.h
new file mode 100644
index 00000000000..00bad470e08
--- /dev/null
+++ b/lib/libpthread/arch/arm/pthread_md.h
@@ -0,0 +1,138 @@
+/* $NetBSD: pthread_md.h,v 1.2 2003/01/18 10:34:18 thorpej Exp $ */
+
+/*
+ * Copyright (c) 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Jason R. Thorpe for Wasabi Systems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the NetBSD Project by
+ * Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _LIB_PTHREAD_ARM_MD_H
+#define _LIB_PTHREAD_ARM_MD_H
+
+static __inline long
+pthread__sp(void)
+{
+ long ret;
+
+ __asm __volatile("mov %0, sp"
+ : "=r" (ret));
+
+ return (ret);
+}
+
+#define pthread__uc_sp(ucp) ((ucp)->uc_mcontext.__gregs[_REG_SP])
+#define pthread__uc_pc(ucp) ((ucp)->uc_mcontext.__gregs[_REG_PC])
+
+/*
+ * Set initial, sane values for registers whose values aren't just
+ * "don't care".
+ */
+#ifdef __APCS_26__
+#define _INITCONTEXT_U_MD(ucp) \
+/* Set R15_MODE_USR in the PC */
+ (ucp)->uc_mcontext.__gregs[_REG_PC] = \
+ ((ucp)->uc_mcontext.__gregs[_REG_PC] & 0x3fffffc) | 0x0;
+#else
+/* Set CPSR to PSR_USE32_MODE (0x10) from arm/armreg.h */
+#define _INITCONTEXT_U_MD(ucp) \
+ (ucp)->uc_mcontext.__gregs[_REG_CPSR] = 0x10;
+#endif
+
+/*
+ * Usable stack space below the ucontext_t.
+ * For a good time, see comments in pthread_switch.S and
+ * ../i386/pthread_switch.S about STACK_SWITCH.
+ */
+#define STACKSPACE (6 * sizeof(long))
+
+/*
+ * Conversions between struct reg and struct mcontext. Used by
+ * libpthread_dbg.
+ */
+
+#define PTHREAD_ARM_UCONTEXT_TO_REG(reg, uc) \
+do { \
+ int _reg_; \
+ \
+ for (_reg_ = 0; _reg_ <= 12; _reg_++) \
+ (reg)->r[_reg_] = \
+ (uc)->uc_mcontext.__gregs[_REG_R0 + _reg_]; \
+ (reg)->r_sp = (uc)->uc_mcontext.__gregs[_REG_SP]; \
+ (reg)->r_lr = (uc)->uc_mcontext.__gregs[_REG_LR]; \
+ (reg)->r_pc = (uc)->uc_mcontext.__gregs[_REG_PC]; \
+ (reg)->r_cpsr = (uc)->uc_mcontext.__gregs[_REG_CPSR]; \
+} while (/*CONSTCOND*/0)
+
+#ifdef __APCS_26__
+#define PTHREAD_UCONTEXT_TO_REG(reg, uc) PTHREAD_ARM_UCONTEXT_TO_REG((reg), (uc))
+#else
+/* Need to signal in the CPSR that this is 32-bit ARM */
+#define PTHREAD_UCONTEXT_TO_REG(reg, uc) \
+do { \
+ PTHREAD_ARM_UCONTEXT_TO_REG((reg), (uc)); \
+ if ((uc)->uc_flags & _UC_USER) \
+ (reg)->r_cpsr = 0x10; \
+} while (/*CONSTCOND*/0)
+#endif
+
+
+#define PTHREAD_REG_TO_UCONTEXT(uc, reg) \
+do { \
+ int _reg_; \
+ \
+ for (_reg_ = 0; _reg_ <= 12; _reg_++) \
+ (uc)->uc_mcontext.__gregs[_REG_R0 + _reg_] = \
+ (reg)->r[_reg_]; \
+ (uc)->uc_mcontext.__gregs[_REG_SP] = (reg)->r_sp; \
+ (uc)->uc_mcontext.__gregs[_REG_LR] = (reg)->r_lr; \
+ (uc)->uc_mcontext.__gregs[_REG_PC] = (reg)->r_pc; \
+ (uc)->uc_mcontext.__gregs[_REG_CPSR] = (reg)->r_cpsr; \
+} while (/*CONSTCOND*/0)
+
+/*
+ * XXX Need to deal with VFP.
+ */
+#define PTHREAD_UCONTEXT_TO_FPREG(freg, uc) \
+do { \
+ (freg)->fpr_fpsr = (uc)->uc_mcontext.__fpu.__fpregs.__fp_fpsr; \
+ memcpy((freg)->fpr, (uc)->uc_mcontext.__fpu.__fpregs.__fp_fr, \
+ sizeof((freg)->fpr)); \
+} while (/*CONSTCOND*/0)
+
+#define PTHREAD_FPREG_TO_UCONTEXT(uc, freg) \
+do { \
+ (uc)->uc_mcontext.__fpu.__fpregs.__fp_fpsr = (freg)->fpr_fpsr; \
+ memcpy((uc)->uc_mcontext.__fpu.__fpregs.__fp_fr, (freg)->fpr, \
+ sizeof((uc)->uc_mcontext.__fpu.__fpregs.__fp_fr)); \
+} while (/*CONSTCOND*/0)
+
+#endif /* _LIB_PTHREAD_ARM_MD_H */
diff --git a/lib/libpthread/arch/arm/pthread_switch.S b/lib/libpthread/arch/arm/pthread_switch.S
new file mode 100644
index 00000000000..9ddda9ccf9f
--- /dev/null
+++ b/lib/libpthread/arch/arm/pthread_switch.S
@@ -0,0 +1,410 @@
+/* $NetBSD: pthread_switch.S,v 1.2 2003/01/18 10:34:18 thorpej Exp $ */
+
+/*
+ * Copyright (c) 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Jason R. Thorpe for Wasabi Systems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the NetBSD Project by
+ * Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <machine/asm.h>
+#include "assym.h"
+
+#define NOTREACHED /* nothing, for now */
+
+/*
+ * Define:
+ * void pthread__switch(pthread_t self, pthread_t next)
+ * void pthread__upcall_switch(pthread_t self, pthread_t next)
+ * void pthread__locked_switch(pthread_t self, pthread_t next,
+ * pt_spin_t *lock)
+ */
+
+/* *** WARNING ***
+ * STACK_SWITCH is more subtle than it looks. Please go read the extended
+ * comment in the i386 pthread_switch.S file.
+ */
+
+#define STACK_SWITCH(pt, tmp) \
+ ldr tmp, [pt, #(PT_UC)] ; \
+ sub sp, tmp, #(STACKSPACE)
+
+
+/*
+ * void
+ * pthread__switch(pthread_t self (r0), pthread_t next (r1))
+ *
+ * Plain switch that doesn't do any special checking or handle spin-
+ * preemption. It isn't used much by normal code, actually; its main
+ * purpose is to be a basic switch engine when the MI code is already
+ * dealing with spin-preemption or other gunk.
+ *
+ * What we do here is allocate the ucontext for the 'self' thread on its
+ * stack, saving the pointer in self's pthread structure, then swapping
+ * context to 'next', effectively deallocating next's context on the way
+ * out.
+ */
+ENTRY(pthread__switch)
+ sub sp, sp, #(RND_CTXSIZE) /* push space for ucontext_t */
+
+ mov r3, sp /* r3 = &context */
+
+ /* Save r0-r2, r4-r5, lr */
+ stmfd sp!, {r0-r2, r4-r5, lr}
+
+ /* Now r5 will point to &context for the duration. */
+ mov r5, r3
+
+/*
+ * Stack is:
+ * context (RND_CTXSIZE bytes)
+ * saved lr ( 4 bytes)
+ * saved r5 ( 4 bytes)
+ * saved r4 ( 4 bytes)
+ * saved r2 ( 4 bytes)
+ * saved r1 ( 4 bytes)
+ * saved r0 ( 4 bytes)
+ * Note, no space needed
+ * for callees.
+ */
+
+ /* Get the current context. */
+ mov r0, r5 /* r0 = &context */
+ bl PIC_SYM(_C_LABEL(_getcontext_u), PLT)
+
+ /* Edit the context to make it continue below, rather than here */
+ add r0, pc, #switch_return_point - . - 8
+ str r0, [r5, #(_REG_PC)]
+
+ /* Get back r0-r2 */
+ ldmfd sp, {r0-r2}
+
+ /* Side-effect: r4 = next->pt_uc */
+ STACK_SWITCH(r1, r4)
+
+ str r5, [r0, #(PT_UC)] /* self->pt_uc = &context */
+
+ /* NOTE: we can no longer recover registers stored on the stack */
+
+ mov r0, r4 /* r0 = next->pt_uc */
+ b PIC_SYM(_C_LABEL(_setcontext_u), PLT)
+ NOTREACHED
+
+switch_return_point:
+/*
+ * Stack is:
+ * context (RND_CTXSIZE bytes)
+ * saved lr ( 4 bytes)
+ * saved r5 ( 4 bytes)
+ * saved r4 ( 4 bytes)
+ * saved r2 ( 4 bytes)
+ * saved r1 ( 4 bytes)
+ * saved r0 ( 4 bytes)
+ * Note, no space needed
+ * for callees.
+ */
+
+ add sp, sp, #12 /* skip over r0-r2 */
+ ldmfd sp!, {r4-r5, lr} /* restore r4-r5, lr */
+ add sp, sp, #(RND_CTXSIZE) /* pop ucontext_t */
+#ifdef __APCS_26__
+ movs pc, lr
+#else
+ mov pc, lr
+#endif
+
+/*
+ * Helper routine; r0 = from, r1 = to, r2 = decr-spinlocks (boolean)
+ */
+pthread__switch_away:
+ /* Side-effect: r3 = r1->pt_uc */
+ STACK_SWITCH(r1, r3)
+
+ /*
+ * If we're invoked from the switch-to-next provisions of
+ * pthread__locked_switch() or pthread__upcall_switch(),
+ * there may be a fake spinlock-count set. If os, they will
+ * set r2 to let us know, and we decrement it now that we're
+ * no longer using the old stack.
+ */
+ teq r2, #0x00000000
+ beq 1f
+ ldr r2, [r0, #(PT_SPINLOCKS)]
+ sub r2, r2, #1
+ str r2, [r0, #(PT_SPINLOCKS)]
+
+1: mov r0, r3
+ b PIC_SYM(_C_LABEL(_setcontext_u), PLT)
+ NOTREACHED
+
+/*
+ * void
+ * pthread__upcall_switch(pthread_t self (r0), pthread_t next (r1))
+ *
+ * Quit an upcall, recycle it, and jump to the selected thread.
+ *
+ * Note: This code never returns, so we can use the callee-saved
+ * registers to our heart's content (and they will be preserved
+ * by functions that we call).
+ */
+ENTRY(pthread__upcall_switch)
+ /* Create a "fake" lock count so that this code will be continued */
+ ldr r3, [r1, #(PT_SPINLOCKS)]
+ add r3, r3, #1
+ str r3, [r1, #(PT_SPINLOCKS)]
+
+ /* Side-effect: r7 = next->pt_uc */
+ STACK_SWITCH(r1, r7)
+
+ /* Check if the upcall code was preempted and continued to here. */
+ ldr r3, [r0, #(PT_NEXT)]
+ teq r3, #0x00000000
+ beq 1f
+
+ /*
+ * Yes, it was. Stash the tread we were going to switch to,
+ * and switch to the next thread in the continuation chain.
+ */
+ str r1, [r0, #(PT_SWITCHTO)]
+ str r7, [r0, #(PT_SWITCHTOUC)]
+ mov r4, #(PT_STATE_RECYCLABLE)
+ str r4, [r0, #(PT_STATE)]
+ mov r0, r1 /* from = next */
+ mov r1, r3 /* to = self->pt_next */
+ mov r2, #0x00000001 /* decr-spinlocks = TRUE */
+ b pthread__switch_away
+ NOTREACHED
+
+1: /* No old-upcall-preemption */
+ mov r4, r0 /* stash from */
+ mov r5, r1 /* stash to */
+ bl PIC_SYM(_C_LABEL(pthread__sa_recycle), PLT)
+ mov r0, r4 /* restore from */
+ mov r1, r5 /* restore to */
+
+ /* We can now release the fake lock. */
+ ldr r3, [r1, #(PT_SPINLOCKS)]
+ sub r3, r3, #1
+ str r3, [r1, #(PT_SPINLOCKS)]
+
+ /* Check if we were preempted and continued while faking the lock */
+ ldr r3, [r1, #(PT_NEXT)]
+ teq r3, #0x00000000
+ beq 2f
+
+ /*
+ * Yes, we were. Stash the thread we were going to switch to
+ * in itself, and switch to the next thread in the continuation
+ * chain.
+ */
+ str r1, [r1, #(PT_SWITCHTO)]
+ str r7, [r1, #(PT_SWITCHTOUC)]
+ mov r0, r1 /* from = next */
+ mov r1, r3 /* to = next->pt_next */
+ mov r2, #0x00000000 /* decr-spinlocks = FALSE */
+ b pthread__switch_away
+ NOTREACHED
+
+2: /* No new-upcall-preemption */
+ mov r0, r7 /* r0 = next->pt_uc */
+ b PIC_SYM(_C_LABEL(_setcontext_u), PLT)
+
+/*
+ * void
+ * pthread__locked_switch(pthread_t self (r0), pthread_t next (r1),
+ * pt_spin_t *lock (r2))
+ *
+ * Total stack is:
+ * context (RND_CTXSIZE bytes)
+ * saved lr ( 4 bytes)
+ * saved r5 ( 4 bytes)
+ * saved r4 ( 4 bytes)
+ * saved r2 ( 4 bytes)
+ * saved r1 ( 4 bytes)
+ * saved r0 ( 4 bytes)
+ * Note, no space needed
+ * for callees.
+ *
+ * STACKSPACE is the space between the bottom of the stack and
+ * the ucontext on the stack. i.e., (6 * sizeof(reg)).
+ *
+ * Note: We use r4 as a temporary register, and r5 as a pointer to
+ * the context on the stack.
+ */
+ENTRY(pthread__locked_switch)
+ sub sp, sp, #(RND_CTXSIZE) /* push space for ucontext_t */
+
+ /* Make sure we get continued */
+ ldr r3, [r1, #(PT_SPINLOCKS)]
+ add r3, r3, #1
+ str r3, [r1, #(PT_SPINLOCKS)]
+
+ mov r3, sp /* r3 = &context */
+ str r3, [r0, #(PT_UC)] /* self->pt_uc = &context */
+
+ /* Save r0-r2, r4-r5, lr */
+ stmfd sp!, {r0-r2, r4-r5, lr}
+
+ /* Now r5 will point to &context for the duration. */
+ mov r5, r3
+
+/*
+ * Stack is:
+ * context (RND_CTXSIZE bytes)
+ * saved lr ( 4 bytes)
+ * saved r5 ( 4 bytes)
+ * saved r4 ( 4 bytes)
+ * saved r2 ( 4 bytes)
+ * saved r1 ( 4 bytes)
+ * saved r0 ( 4 bytes)
+ * Note, no space needed
+ * for callees.
+ */
+
+ /* Get the current context. */
+ mov r0, r5 /* r0 = &context */
+ bl PIC_SYM(_C_LABEL(_getcontext_u), PLT)
+
+ /* Edit the context to make it continue below, rather than here */
+ add r0, pc, #locked_return_point - . - 8
+ str r0, [r3, #(_REG_PC)]
+
+ /* Get back r0-r2 */
+ ldmfd sp, {r0-r2}
+
+ /* Side-effect: r4 = next->pt_uc */
+ STACK_SWITCH(r1, r4)
+
+ /* NOTE: we can no longer recover registers stored on the stack */
+
+ /* Check if the switcher was preempted and continued to here. */
+ ldr r3, [r0, #(PT_NEXT)]
+ teq r3, #0x00000000
+ beq 1f
+
+ /*
+ * Yes, it was. Stash the thread we were going to switch to,
+ * the lock the original thread was holding, and switch to
+ * the next thread in the continuation chain. Mark the fact
+ * that this was a locked switch, and so the thread does not
+ * need to be put on a run queue.
+ *
+ * DO NOT RELEASE THE LOCK. It's possible that if we do so,
+ * PT_SWITCHTO will be stomped on by another switch_lock and
+ * preemption.
+ */
+ str r1, [r0, #(PT_SWITCHTO)]
+ str r4, [r0, #(PT_SWITCHTOUC)]
+ str r2, [r0, #(PT_HELDLOCK)]
+
+ ldr r4, [r0, #(PT_SPINLOCKS)]
+ sub r4, r4, #1
+ str r4, [r0, #(PT_SPINLOCKS)]
+
+ /*
+ * Save the context we previously stored in self->pt_uc; that
+ * was overwritten when we were preempted and continued, so we
+ * need to put it someplace.
+ */
+ str r5, [r0, #(PT_SLEEPUC)]
+
+ mov r0, r1 /* from = next */
+ mov r1, r3 /* to = self->pt_next */
+ mov r2, #0x00000001 /* decr-spinlocks = TRUE */
+ b pthread__switch_away
+ NOTREACHED
+
+1: /* No locked-old-preemption */
+
+ /*
+ * Note: r4 == next->pt_uc still. We no longer need to
+ * reference the context on the stack, so put next->pt_uc
+ * in r5 so that we can use r4 as a temporary.
+ */
+ mov r5, r4
+
+ /*
+ * We've moved to the new stack, and the old context has
+ * been saved. The queue lock can be released.
+ */
+ ldr r4, [r0, #(PT_SPINLOCKS)]
+ sub r4, r4, #1
+ str r4, [r0, #(PT_SPINLOCKS)]
+
+ /* ...actually release the lock... */
+ mov r4, #(__SIMPLELOCK_UNLOCKED)
+ str r4, [r2]
+
+ /* ...and remove the fake lock... */
+ ldr r4, [r1, #(PT_SPINLOCKS)]
+ sub r4, r4, #1
+ str r4, [r1, #(PT_SPINLOCKS)]
+
+ /* Check if we were preempted while holding the fake lock. */
+ ldr r3, [r1, #(PT_NEXT)]
+ teq r3, #0x00000000
+ beq 2f
+
+ /* Yes, we were. Go to the next thread in the chain. */
+ str r1, [r1, #(PT_SWITCHTO)]
+ str r5, [r1, #(PT_SWITCHTOUC)]
+ mov r0, r1 /* from = next */
+ mov r1, r3 /* to = next->pt_next */
+ mov r2, #0x00000000 /* decr-spinlocks = FALSE */
+ b pthread__switch_away
+
+2: /* No locked-new-preemption */
+ mov r0, r5 /* r0 = next->pt_uc */
+ b PIC_SYM(_C_LABEL(_setcontext_u), PLT)
+ NOTREACHED
+
+locked_return_point:
+/*
+ * Stack is:
+ * context (RND_CTXSIZE bytes)
+ * saved lr ( 4 bytes)
+ * saved r5 ( 4 bytes)
+ * saved r4 ( 4 bytes)
+ * saved r2 ( 4 bytes)
+ * saved r1 ( 4 bytes)
+ * saved r0 ( 4 bytes)
+ * Note, no space needed
+ * for callees.
+ */
+
+ add sp, sp, #12 /* skip over r0-r2 */
+ ldmfd sp!, {r4-r5, lr} /* restore r4-r5, lr */
+ add sp, sp, #(RND_CTXSIZE) /* pop ucontext_t */
+#ifdef __APCS_26__
+ movs pc, lr
+#else
+ mov pc, lr
+#endif
diff --git a/lib/libpthread/arch/i386/_context_u.S b/lib/libpthread/arch/i386/_context_u.S
new file mode 100644
index 00000000000..dc500ce93f5
--- /dev/null
+++ b/lib/libpthread/arch/i386/_context_u.S
@@ -0,0 +1,101 @@
+/* $NetBSD: _context_u.S,v 1.2 2003/01/18 10:34:18 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nathan J. Williams.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <machine/asm.h>
+#include "assym.h"
+
+
+#define FPSAVE(reg) fnsave UC_FPREGS(reg)
+#define FPLOAD(reg) frstor UC_FPREGS(reg)
+#include "_getsetc.S"
+
+ENTRY(_getcontext_u_s87)
+ movl 4(%esp), %ecx
+ movl 0(%esp), %edx
+ GETC
+ movl %esp, %eax
+ addl $4, %eax
+ movl %eax, (UC_REGS + _REG_UESP * 4)(%ecx)
+ xorl %eax, %eax
+ ret
+
+ENTRY(_setcontext_u_s87)
+ movl 4(%esp), %ecx
+ SETC
+
+ENTRY(_swapcontext_u_s87)
+ movl 4(%esp), %ecx
+ movl 0(%esp), %edx
+ GETC
+ movl %esp, %eax
+ addl $4, %eax
+ movl %eax, (UC_REGS + _REG_UESP * 4)(%ecx)
+ movl 8(%esp), %ecx
+ SETC
+
+
+#undef FPSAVE
+#undef FPLOAD
+
+#define FPSAVE(reg) fxsave UC_FXSAVEREGS(reg)
+#define FPLOAD(reg) fxrstor UC_FXSAVEREGS(reg)
+#include "_getsetc.S"
+
+ENTRY(_getcontext_u_xmm)
+ movl 4(%esp), %ecx
+ movl 0(%esp), %edx
+ GETC
+ movl %esp, %eax
+ addl $4, %eax
+ movl %eax, (UC_REGS + _REG_UESP * 4)(%ecx)
+ xorl %eax, %eax
+ ret
+
+ENTRY(_setcontext_u_xmm)
+ movl 4(%esp), %ecx
+ SETC
+
+ENTRY(_swapcontext_u_xmm)
+ movl 4(%esp), %ecx
+ movl 0(%esp), %edx
+ GETC
+ movl %esp, %eax
+ addl $4, %eax
+ movl %eax, (UC_REGS + _REG_UESP * 4)(%ecx)
+ movl 8(%esp), %ecx
+ SETC
diff --git a/lib/libpthread/arch/i386/_getsetc.S b/lib/libpthread/arch/i386/_getsetc.S
new file mode 100644
index 00000000000..1b9feda6abf
--- /dev/null
+++ b/lib/libpthread/arch/i386/_getsetc.S
@@ -0,0 +1,100 @@
+/* $NetBSD: _getsetc.S,v 1.2 2003/01/18 10:34:18 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nathan J. Williams.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#undef GETC
+#undef SETC
+
+#define GETC \
+ movl %edi, (UC_REGS + _REG_EDI * 4)(%ecx) ; \
+ movl %esi, (UC_REGS + _REG_ESI * 4)(%ecx) ; \
+ movl %ebp, (UC_REGS + _REG_EBP * 4)(%ecx) ; \
+ movl %ebx, (UC_REGS + _REG_EBX * 4)(%ecx) ; \
+ movl %edx, (UC_REGS + _REG_EIP * 4)(%ecx) ; \
+ FPSAVE(%ecx) ; \
+ movl $(_UC_USER | _UC_CPU | _UC_FPU),%eax ; \
+ movl %eax, UC_FLAGS(%ecx)
+
+#define SETC \
+ movl UC_FLAGS(%ecx), %eax ; \
+ bt $_UC_USER_BIT, %eax ; \
+ jnc 1f ; \
+ ; \
+ FPLOAD(%ecx) ; \
+ movl (UC_REGS + _REG_EDI * 4)(%ecx), %edi ; \
+ movl (UC_REGS + _REG_ESI * 4)(%ecx), %esi ; \
+ movl (UC_REGS + _REG_EBP * 4)(%ecx), %ebp ; \
+ movl (UC_REGS + _REG_EBX * 4)(%ecx), %ebx ; \
+ movl (UC_REGS + _REG_UESP * 4)(%ecx), %edx ; \
+ movl (UC_REGS + _REG_EIP * 4)(%ecx), %eax ; \
+ movl %eax, -4(%edx) ; \
+ leal -4(%edx), %esp ; \
+ ret ; \
+ ; \
+1: and $_UC_FPU, %eax ; \
+ jz 2f ; \
+ FPLOAD(%ecx) ; \
+2: movl (UC_REGS + _REG_GS * 4)(%ecx), %gs ; \
+ movl (UC_REGS + _REG_FS * 4)(%ecx), %fs ; \
+ movl (UC_REGS + _REG_ES * 4)(%ecx), %es ; \
+ movl (UC_REGS + _REG_EDI * 4)(%ecx), %edi ; \
+ movl (UC_REGS + _REG_ESI * 4)(%ecx), %esi ; \
+ movl (UC_REGS + _REG_EBP * 4)(%ecx), %ebp ; \
+ movl (UC_REGS + _REG_EBX * 4)(%ecx), %ebx ; \
+ movl (UC_REGS + _REG_UESP * 4)(%ecx), %edx ; \
+ movl (UC_REGS + _REG_CS * 4)(%ecx), %eax ; \
+ mov %eax, -4(%edx) ; \
+ movl (UC_REGS + _REG_EIP * 4)(%ecx), %eax ; \
+ movl %eax, -8(%edx) ; \
+ movl (UC_REGS + _REG_DS * 4)(%ecx), %eax ; \
+ movl %eax, -12(%edx) ; \
+ movl (UC_REGS + _REG_EDX * 4)(%ecx), %eax ; \
+ movl %eax, -16(%edx) ; \
+ movl (UC_REGS + _REG_ECX * 4)(%ecx), %eax ; \
+ movl %eax, -20(%edx) ; \
+ movl (UC_REGS + _REG_EAX * 4)(%ecx), %eax ; \
+ movl %eax, -24(%edx) ; \
+ movl (UC_REGS + _REG_EFL * 4)(%ecx), %eax ; \
+ movl %eax, -28(%edx) ; \
+ movl (UC_REGS + _REG_SS * 4)(%ecx), %ss ; \
+ leal -28(%edx), %esp ; \
+ popfl ; \
+ popl %eax ; \
+ popl %ecx ; \
+ popl %edx ; \
+ popl %ds ; \
+ lret
diff --git a/lib/libpthread/arch/i386/genassym.cf b/lib/libpthread/arch/i386/genassym.cf
new file mode 100644
index 00000000000..b036b4b2b6d
--- /dev/null
+++ b/lib/libpthread/arch/i386/genassym.cf
@@ -0,0 +1,86 @@
+# $NetBSD: genassym.cf,v 1.2 2003/01/18 10:34:18 thorpej Exp $
+
+# Copyright (c) 2001 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# This code is derived from software contributed to The NetBSD Foundation
+# by Nathan J. Williams.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the NetBSD
+# Foundation, Inc. and its contributors.
+# 4. Neither the name of The NetBSD Foundation nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+
+include <ucontext.h>
+include <sys/queue.h>
+include "pthread.h"
+include "pthread_int.h"
+include "pthread_md.h"
+
+define PT_NEXT offsetof(struct pthread_st, pt_next)
+define PT_STATE offsetof(struct pthread_st, pt_state)
+define PT_SWITCHTO offsetof(struct pthread_st, pt_switchto)
+define PT_SWITCHTOUC offsetof(struct pthread_st, pt_switchtouc)
+define PT_SLEEPUC offsetof(struct pthread_st, pt_sleepuc)
+define PT_SPINLOCKS offsetof(struct pthread_st, pt_spinlocks)
+define PT_HELDLOCK offsetof(struct pthread_st, pt_heldlock)
+define PT_UC offsetof(struct pthread_st, pt_uc)
+define CONTEXTSIZE sizeof(ucontext_t)
+define UC_FLAGS offsetof(ucontext_t, uc_flags)
+define UC_REGS offsetof(ucontext_t, uc_mcontext.__gregs)
+define UC_EIP offsetof(ucontext_t, uc_mcontext.__gregs[_REG_EIP])
+define UC_FPREGS offsetof(ucontext_t, uc_mcontext.__fpregs.__fp_reg_set.__fpchip_state.__fp_state)
+define UC_FXSAVEREGS offsetof(ucontext_t, uc_mcontext.__fpregs.__fp_reg_set.__fp_xmm_state.__fp_xmm)
+
+define PT_STATE_RECYCLABLE PT_STATE_RECYCLABLE
+define STACKSPACE STACKSPACE
+
+define _UC_CPU _UC_CPU
+define _UC_FPU _UC_FPU
+define _UC_USER _UC_USER
+define _UC_USER_BIT _UC_USER_BIT
+
+define _REG_GS _REG_GS
+define _REG_FS _REG_FS
+define _REG_ES _REG_ES
+define _REG_DS _REG_DS
+define _REG_EDI _REG_EDI
+define _REG_ESI _REG_ESI
+define _REG_EBP _REG_EBP
+define _REG_ESP _REG_ESP
+define _REG_EBX _REG_EBX
+define _REG_EDX _REG_EDX
+define _REG_ECX _REG_ECX
+define _REG_EAX _REG_EAX
+define _REG_TRAPNO _REG_TRAPNO
+define _REG_ERR _REG_ERR
+define _REG_EIP _REG_EIP
+define _REG_CS _REG_CS
+define _REG_EFL _REG_EFL
+define _REG_UESP _REG_UESP
+define _REG_SS _REG_SS
diff --git a/lib/libpthread/arch/i386/pthread_md.c b/lib/libpthread/arch/i386/pthread_md.c
new file mode 100644
index 00000000000..71790ae56cf
--- /dev/null
+++ b/lib/libpthread/arch/i386/pthread_md.c
@@ -0,0 +1,76 @@
+/* $NetBSD: pthread_md.c,v 1.2 2003/01/18 10:34:18 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nathan J. Williams.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <machine/cpu.h>
+
+#include "pthread.h"
+#include "pthread_int.h"
+
+int (*_md_getcontext_u)(ucontext_t *);
+int (*_md_setcontext_u)(const ucontext_t *);
+int (*_md_swapcontext_u)(ucontext_t *, const ucontext_t *);
+
+/*
+ * Initialize the function pointers for get/setcontext to the
+ * old-fp-save (s87) or new-fxsave (xmm) FP routines.
+ */
+void pthread__i386_init(void)
+{
+ int mib[2];
+ size_t len;
+ int use_fxsave;
+
+ mib[0] = CTL_MACHDEP;
+ mib[1] = CPU_OSFXSR;
+
+ len = sizeof(use_fxsave);
+ sysctl(mib, 2, &use_fxsave, &len, NULL, 0);
+
+ if (use_fxsave) {
+ _md_getcontext_u = _getcontext_u_xmm;
+ _md_setcontext_u = _setcontext_u_xmm;
+ _md_swapcontext_u = _swapcontext_u_xmm;
+ } else {
+ _md_getcontext_u = _getcontext_u_s87;
+ _md_setcontext_u = _setcontext_u_s87;
+ _md_swapcontext_u = _swapcontext_u_s87;
+ }
+
+}
diff --git a/lib/libpthread/arch/i386/pthread_md.h b/lib/libpthread/arch/i386/pthread_md.h
new file mode 100644
index 00000000000..02a4261e310
--- /dev/null
+++ b/lib/libpthread/arch/i386/pthread_md.h
@@ -0,0 +1,153 @@
+/* $NetBSD: pthread_md.h,v 1.2 2003/01/18 10:34:18 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nathan J. Williams.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _LIB_PTHREAD_I386_MD_H
+#define _LIB_PTHREAD_I386_MD_H
+
+#include <sys/ucontext.h>
+
+extern int (*_md_getcontext_u)(ucontext_t *);
+extern int (*_md_setcontext_u)(const ucontext_t *);
+extern int (*_md_swapcontext_u)(ucontext_t *, const ucontext_t *);
+
+#define _getcontext_u(uc) (*_md_getcontext_u)((uc))
+#define _setcontext_u(uc) (*_md_setcontext_u)((uc))
+#define _swapcontext_u(ouc, nuc) (*_md_swapcontext_u)((ouc), (nuc))
+
+int _getcontext_u_s87(ucontext_t *);
+int _setcontext_u_s87(const ucontext_t *);
+int _swapcontext_u_s87(ucontext_t *, const ucontext_t *);
+int _getcontext_u_xmm(ucontext_t *);
+int _setcontext_u_xmm(const ucontext_t *);
+int _swapcontext_u_xmm(ucontext_t *, const ucontext_t *);
+
+void pthread__i386_init(void);
+
+#define PTHREAD_MD_INIT pthread__i386_init();
+
+static __inline long
+pthread__sp(void)
+{
+ long ret;
+ __asm("movl %%esp, %0" : "=g" (ret));
+
+ return ret;
+}
+
+#define pthread__uc_sp(ucp) ((ucp)->uc_mcontext.__gregs[_REG_UESP])
+#define pthread__uc_pc(ucp) ((ucp)->uc_mcontext.__gregs[_REG_EIP])
+
+/*
+ * Set initial, sane values for registers whose values aren't just
+ * "don't care".
+ * 0x2b is GSEL(GUDATA_SEL, SEL_UPL), and
+ * 0x23 is GSEL(GUCODE_SEL, SEL_UPL), from arch/i386/include/segments.h.
+ * 0x202 is PSL_USERSET from arch/i386/include/psl.h
+ */
+#define _INITCONTEXT_U_MD(ucp) \
+ (ucp)->uc_mcontext.__gregs[_REG_GS] = 0x2b; \
+ (ucp)->uc_mcontext.__gregs[_REG_FS] = 0x2b; \
+ (ucp)->uc_mcontext.__gregs[_REG_ES] = 0x2b; \
+ (ucp)->uc_mcontext.__gregs[_REG_DS] = 0x2b; \
+ (ucp)->uc_mcontext.__gregs[_REG_CS] = 0x23; \
+ (ucp)->uc_mcontext.__gregs[_REG_SS] = 0x2b; \
+ (ucp)->uc_mcontext.__gregs[_REG_EFL] = 0x202;
+
+/*
+ * Usable stack space below the ucontext_t.
+ * See comment in pthread_switch.S about STACK_SWITCH.
+ */
+#define STACKSPACE 32 /* room for 8 integer values */
+
+/*
+ * Conversions between struct reg and struct mcontext. Used by
+ * libpthread_dbg.
+ */
+
+#define PTHREAD_UCONTEXT_TO_REG(reg, uc) do { \
+ (reg)->r_gs = (uc)->uc_mcontext.__gregs[_REG_GS]; \
+ (reg)->r_fs = (uc)->uc_mcontext.__gregs[_REG_FS]; \
+ (reg)->r_es = (uc)->uc_mcontext.__gregs[_REG_ES]; \
+ (reg)->r_ds = (uc)->uc_mcontext.__gregs[_REG_DS]; \
+ (reg)->r_edi = (uc)->uc_mcontext.__gregs[_REG_EDI]; \
+ (reg)->r_esi = (uc)->uc_mcontext.__gregs[_REG_ESI]; \
+ (reg)->r_ebp = (uc)->uc_mcontext.__gregs[_REG_EBP]; \
+ (reg)->r_ebx = (uc)->uc_mcontext.__gregs[_REG_EBX]; \
+ (reg)->r_edx = (uc)->uc_mcontext.__gregs[_REG_EDX]; \
+ (reg)->r_ecx = (uc)->uc_mcontext.__gregs[_REG_ECX]; \
+ (reg)->r_eax = (uc)->uc_mcontext.__gregs[_REG_EAX]; \
+ (reg)->r_eip = (uc)->uc_mcontext.__gregs[_REG_EIP]; \
+ (reg)->r_cs = (uc)->uc_mcontext.__gregs[_REG_CS]; \
+ (reg)->r_eflags = (uc)->uc_mcontext.__gregs[_REG_EFL]; \
+ (reg)->r_esp = (uc)->uc_mcontext.__gregs[_REG_UESP]; \
+ (reg)->r_ss = (uc)->uc_mcontext.__gregs[_REG_SS]; \
+ } while (/*CONSTCOND*/0)
+
+#define PTHREAD_REG_TO_UCONTEXT(uc, reg) do { \
+ (uc)->uc_mcontext.__gregs[_REG_GS] = (reg)->r_gs; \
+ (uc)->uc_mcontext.__gregs[_REG_FS] = (reg)->r_fs; \
+ (uc)->uc_mcontext.__gregs[_REG_ES] = (reg)->r_es; \
+ (uc)->uc_mcontext.__gregs[_REG_DS] = (reg)->r_ds; \
+ (uc)->uc_mcontext.__gregs[_REG_EDI] = (reg)->r_edi; \
+ (uc)->uc_mcontext.__gregs[_REG_ESI] = (reg)->r_esi; \
+ (uc)->uc_mcontext.__gregs[_REG_EBP] = (reg)->r_ebp; \
+ (uc)->uc_mcontext.__gregs[_REG_EBX] = (reg)->r_ebx; \
+ (uc)->uc_mcontext.__gregs[_REG_EDX] = (reg)->r_edx; \
+ (uc)->uc_mcontext.__gregs[_REG_ECX] = (reg)->r_ecx; \
+ (uc)->uc_mcontext.__gregs[_REG_EAX] = (reg)->r_eax; \
+ (uc)->uc_mcontext.__gregs[_REG_EIP] = (reg)->r_eip; \
+ (uc)->uc_mcontext.__gregs[_REG_CS] = (reg)->r_cs; \
+ (uc)->uc_mcontext.__gregs[_REG_EFL] = (reg)->r_eflags; \
+ (uc)->uc_mcontext.__gregs[_REG_UESP]= (reg)->r_esp; \
+ (uc)->uc_mcontext.__gregs[_REG_SS] = (reg)->r_ss; \
+ (uc)->uc_flags = ((uc)->uc_flags | _UC_CPU) & ~_UC_USER; \
+ } while (/*CONSTCOND*/0)
+
+#define PTHREAD_UCONTEXT_TO_FPREG(freg, uc) \
+ memcpy((freg)->__data, \
+ (uc)->uc_mcontext.__fpregs.__fp_reg_set.__fpchip_state.__fp_state, \
+ sizeof(struct fpreg))
+
+#define PTHREAD_FPREG_TO_UCONTEXT(uc, freg) do { \
+ memcpy( \
+ (uc)->uc_mcontext.__fpregs.__fp_reg_set.__fpchip_state.__fp_state, \
+ (freg)->__data, sizeof(struct fpreg)); \
+ (uc)->uc_flags = ((uc)->uc_flags | _UC_FPU) & ~_UC_USER; \
+ } while (/*CONSTCOND*/0)
+
+#endif /* _LIB_PTHREAD_I386_MD_H */
diff --git a/lib/libpthread/arch/i386/pthread_switch.S b/lib/libpthread/arch/i386/pthread_switch.S
new file mode 100644
index 00000000000..8dcc5384837
--- /dev/null
+++ b/lib/libpthread/arch/i386/pthread_switch.S
@@ -0,0 +1,367 @@
+/* $NetBSD: pthread_switch.S,v 1.2 2003/01/18 10:34:18 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nathan J. Williams.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <machine/asm.h>
+#include "assym.h"
+
+/*
+ * This file implements low-level routines that are exported to
+ * the machine-independent parts of the thread library. The routines are:
+ *
+ * void pthread__switch(pthread_t self, pthread_t next);
+ * void pthread__upcall_switch(pthread_t self, pthread_t next);
+ * void pthread__locked_switch(pthread_t self, pthread_t next,
+ * pt_spin_t *lock);
+ *
+ * as well as some utility code used by these routines.
+ */
+
+/* Force an error when "notreached" code is reached. */
+#define NOTREACHED \
+ int3
+
+
+/*
+ * Utility macro to switch to the stack of another thread.
+ * WARNING: This is more subtle than it looks.
+ *
+ * There are a couple of motivations for stack switching. One is that
+ * when exiting an upcall and running a thread, we want to recycle the
+ * state of the upcall before continuing the thread. However, the
+ * stack is part of the upcall state, and we can't be on the upcall
+ * stack when we want to recycle it. Therefore, we pre-switch to the
+ * stack of the thread we're about to switch to and borrow some space
+ * on its stack so that we can recycle the upcall.
+ *
+ * Another is that when performing the slightly sensitive operation of
+ * putting the currently running thread on a (locked) sleep queue then
+ * switching to another thread, we don't want to release the queue
+ * lock until we are no longer on the original stack - if we released
+ * it any earlier, it's possible (epecially on a multiprocessor
+ * system) that the original thread could get taken off the queue and
+ * restarted while we're still using its stack, which would be bad
+ * ("Important safety tip. Thanks, Egon").
+ *
+ * The subtlety comes from the contents of the stack of the thread
+ * being switched to. Our convention is that the ucontext_t of the
+ * thread is stored near the top of the stack. When the kernel
+ * preempts a thread, it stores the ucontext_t just above the current
+ * top-of-stack and passes the upcall handler the pointer to the
+ * ucontext_t, and the upcall handler stores it in the the pt_uc field
+ * of the thread structure. When user-level code voluntairly switches
+ * from one thread to another, the ucontext_t is just below the top of
+ * the stack, and we impose a limit on the amount of stack space above
+ * the ucontext_t that may be used. This way, we can perform the stack
+ * switch safely by setting the stack pointer to thread->pt_uc -
+ * STACKSPACE.
+ * Note carefully that we do the subtraction *before* putting the
+ * value in the stack pointer. Since preemption can occur at any time,
+ * and the kernel will happily write the new ucontext_t below the
+ * current stack pointer, it is unsafe for us to ever set the stack
+ * pointer above potentially valid data, even for one instruction.
+ *
+ * Got it? Good. Now go read it again.
+ *
+ */
+
+#define STACK_SWITCH \
+ movl PT_UC(%ecx), %esi ; \
+ lea (-STACKSPACE)(%esi), %esp
+
+
+/*
+ * void pthread__switch(pthread_t self, pthread_t next);
+ *
+ * Plain switch that doesn't do any special checking or handle
+ * spin-preemption. It isn't used much by normal code, actually; it's
+ * main purpose is to be a basic switch engine when the MI code is
+ * already dealing with spin-preemption or other gunk.
+ */
+ENTRY(pthread__switch)
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %esi /* callee save */
+ pushl %edi /* callee save */
+ PIC_PROLOGUE
+ movl 8(%ebp), %eax /* eax holds the current thread */
+ movl 12(%ebp), %ecx /* ecx holds the thread to switch to */
+ subl $CONTEXTSIZE+12, %esp /* Allocate space for the ucontext_t */
+ leal 12(%esp), %edi
+ andl $~(0xf), %edi /* 16-byte-align the ucontext_t area */
+ /*
+ * Note: Alignment space below the ucontext_t can consume up to
+ * 12 bytes of STACKSPACE.
+ */
+ pushl %eax /* caller save */
+ pushl %ecx /* caller save */
+ pushl %edi /* ucontext_t *ucp */
+ call *PIC_GOTOFF(_C_LABEL(_md_getcontext_u))
+ addl $4, %esp
+ popl %ecx
+ popl %eax
+ /*
+ * Edit the context so that it continues as if returning from
+ * the _setcontext_u below.
+ */
+#ifdef PIC
+ movl PIC_GOT(switch_return_point), %esi
+#else
+ leal locked_return_point, %esi
+#endif
+ movl %esi, UC_EIP(%edi)
+
+ STACK_SWITCH
+
+ movl %edi, PT_UC(%eax)
+
+ pushl %esi /* ucontext_t *ucp */
+ call *PIC_GOTOFF(_C_LABEL(_md_setcontext_u))
+switch_return_point:
+ /* We're back on the original stack. */
+ addl $CONTEXTSIZE+24, %esp
+ PIC_EPILOGUE
+ popl %edi
+ popl %esi
+ movl %ebp, %esp
+ popl %ebp
+ ret
+
+/*
+ * Helper switch code used by pthread__locked_switch() and
+ * pthread__upcall_switch() when they discover spinlock preemption.
+ */
+.globl pthread__switch_away
+pthread__switch_away:
+ STACK_SWITCH
+
+ /*
+ * If we're invoked from the switch-to-next provisions of
+ * pthread__locked_switch or pthread__upcall_switch, there
+ * may be a fake spinlock-count set. If so, they will set %edx
+ * to let us know, and we decrement it once we're no longer using
+ * the old stack.
+ */
+ cmpl $0, %edx
+ jle pthread__switch_no_decrement
+ decl PT_SPINLOCKS(%eax)
+
+pthread__switch_no_decrement:
+ pushl %esi /* ucontext_t *ucp */
+ call *PIC_GOTOFF(_C_LABEL(_md_setcontext_u))
+ NOTREACHED
+
+/*
+ * void pthread__locked_switch(pthread_t self, pthread_t next,
+ * pt_spin_t *lock);
+ *
+ * Switch away from a thread that is holding a lock on a queue (to
+ * prevent being removed from the queue before being switched away).
+ */
+ENTRY(pthread__locked_switch)
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %esi /* callee save */
+ pushl %edi /* callee save */
+ PIC_PROLOGUE
+ movl 8(%ebp), %eax /* eax holds the current thread */
+ movl 12(%ebp), %ecx /* ecx holds the thread to switch to */
+ movl 16(%ebp), %edx /* edx holds the spinlock pointer */
+ incl PT_SPINLOCKS(%ecx) /* Make sure we get continued */
+ subl $CONTEXTSIZE+12, %esp /* Allocate space for the ucontext_t */
+ leal 12(%esp), %edi
+ andl $~(0xf), %edi /* 16-byte-align the ucontext_t area */
+ /*
+ * Note: Alignment space below the ucontext_t can consume up to
+ * 12 bytes of STACKSPACE.
+ */
+ pushl %eax /* caller save */
+ pushl %ecx /* caller save */
+ pushl %edx /* caller save */
+ pushl %edi /* ucontext_t *ucp */
+ call *PIC_GOTOFF(_C_LABEL(_md_getcontext_u))
+ addl $4, %esp
+ popl %edx
+ popl %ecx
+ popl %eax
+ /*
+ * Edit the context so that it continues as if returning from
+ * the _setcontext_u below.
+ */
+#ifdef PIC
+ movl PIC_GOT(locked_return_point), %esi
+#else
+ leal locked_return_point, %esi
+#endif
+ movl %esi, UC_EIP(%edi)
+
+ STACK_SWITCH
+
+ movl %edi, PT_UC(%eax)
+ /*
+ * Check if the original thread was preempted while holding
+ * its queue lock.
+ */
+ cmpl $0, PT_NEXT(%eax)
+ je locked_no_old_preempt
+
+ /*
+ * Yes, it was. Stash the thread we were going to
+ * switch to, the lock the original thread was holding,
+ * and go to the next thread in the chain.
+ * Mark the fact that this was a locked switch, and so the
+ * thread does not need to be put on a run queue.
+ * Don't release the lock. It's possible that if we do so,
+ * PT_SWITCHTO will be stomped by another switch_lock and
+ * preemption.
+ */
+ movl %ecx, PT_SWITCHTO(%eax)
+ movl %esi, PT_SWITCHTOUC(%eax)
+ movl %edx, PT_HELDLOCK(%eax)
+ decl PT_SPINLOCKS(%eax)
+
+ /*
+ * Save the context we previously stored in PT_UC(%eax);
+ * that was overwritten when we were preempted and continued,
+ * so we need to put it somewhere.
+ */
+ movl %edi, PT_SLEEPUC(%eax)
+
+ movl PT_NEXT(%eax), %edx
+ movl %ecx, %eax
+ movl %edx, %ecx
+ movl $1, %edx
+ jmp pthread__switch_away
+ NOTREACHED
+
+locked_no_old_preempt:
+ /*
+ * We've moved to the new stack, and the old context has been
+ * saved. The queue lock can be released.
+ */
+ decl PT_SPINLOCKS(%eax)
+ /* We happen to know that this is the right way to release a lock. */
+ movl $0, 0(%edx)
+
+ decl PT_SPINLOCKS(%ecx)
+ /* Check if we were preempted while holding the fake lock. */
+ cmpl $0, PT_NEXT(%ecx)
+ je locked_no_new_preempt
+ /* Yes, we were. Bummer. Go to the next element in the chain. */
+ movl %ecx, PT_SWITCHTO(%ecx)
+ movl %esi, PT_SWITCHTOUC(%ecx)
+ movl %ecx, %eax
+ movl PT_NEXT(%ecx), %ecx
+ movl $-2, %edx
+ jmp pthread__switch_away
+ NOTREACHED
+
+locked_no_new_preempt:
+ pushl %esi /* ucontext_t *ucp */
+ call *PIC_GOTOFF(_C_LABEL(_md_setcontext_u))
+locked_return_point:
+ /* We're back on the original stack. */
+ addl $CONTEXTSIZE+28, %esp
+ PIC_EPILOGUE
+ popl %edi
+ popl %esi
+ movl %ebp, %esp
+ popl %ebp
+ ret
+
+
+
+/*
+ * void pthread__upcall_switch(pthread_t self, pthread_t next);
+ *
+ * Quit an upcall, recycle it, and jump to the selected thread.
+ */
+ENTRY(pthread__upcall_switch)
+ /* Save args into registers so we can stop using the old stack. */
+ pushl %ebp
+ movl %esp, %ebp
+ /* No need to save %esi, %edi; this function never returns */
+ PIC_PROLOGUE
+ movl 8(%ebp), %eax /* eax holds the upcall thread */
+ movl 12(%ebp), %ecx /* ecx holds the thread to switch to */
+ incl PT_SPINLOCKS(%ecx)
+
+ STACK_SWITCH
+
+ /* Check if the upcall was preempted and continued. */
+ cmpl $0, PT_NEXT(%eax)
+ je upcall_no_old_preempt
+ /*
+ * Yes, it was. Stash the thread we were going to
+ * switch to, and go to the next thread in the chain.
+ */
+ movl %ecx, PT_SWITCHTO(%eax)
+ movl %esi, PT_SWITCHTOUC(%eax)
+ movl $PT_STATE_RECYCLABLE, PT_STATE(%eax)
+ movl PT_NEXT(%eax), %edx
+ movl %ecx, %eax
+ movl %edx, %ecx
+ movl $1, %edx
+ jmp pthread__switch_away
+ NOTREACHED
+
+upcall_no_old_preempt:
+ pushl %eax /* caller save */
+ pushl %ecx /* caller save */
+ pushl %ecx /* pthread_t new */
+ pushl %eax /* pthread_t old */
+ call PIC_PLT(_C_LABEL(pthread__sa_recycle))
+ addl $8, %esp
+ popl %ecx
+ popl %eax
+ decl PT_SPINLOCKS(%ecx)
+ /* Check if we were preempted while holding the fake lock. */
+ cmpl $0, PT_NEXT(%ecx)
+ je upcall_no_new_preempt
+ /* Yes, we were. Bummer. Go to the next element in the chain. */
+ movl %ecx, PT_SWITCHTO(%ecx)
+ movl %esi, PT_SWITCHTOUC(%ecx)
+ movl %ecx, %eax
+ movl PT_NEXT(%ecx), %ecx
+ movl $-1, %edx
+ jmp pthread__switch_away
+ NOTREACHED
+
+upcall_no_new_preempt:
+ pushl %esi
+ call *PIC_GOTOFF(_C_LABEL(_md_setcontext_u))
+ NOTREACHED
diff --git a/lib/libpthread/arch/m68k/_context_u.S b/lib/libpthread/arch/m68k/_context_u.S
new file mode 100644
index 00000000000..b6ce68a59a9
--- /dev/null
+++ b/lib/libpthread/arch/m68k/_context_u.S
@@ -0,0 +1,132 @@
+/* $NetBSD: _context_u.S,v 1.2 2003/01/18 10:34:19 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nathan J. Williams and Steve C. Woodford.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <machine/asm.h>
+#include "assym.h"
+
+/*
+ * Save the current context into the ucontext_t structure pointed to
+ * by %a0. The value of %pc to save is passed in %a1.
+ *
+ * We only need to save the `callee' saved registers.
+ */
+#define GETC \
+ moveml %d2-%d7,%a0@(UC_REGS + (_REG_D2 * 4)) ; \
+ moveml %a2-%a7,%a0@(UC_REGS + (_REG_A2 * 4)) ; \
+ movl %a1,%a0@(UC_REGS + (_REG_PC * 4)) ; \
+ clrl %a0@(UC_REGS + (_REG_PS * 4)) ; \
+ fnop ; \
+ fmovem %fpcr,%a0@(UC_FPREGS + _FPREG_PCR) ; \
+ fmovem %fpsr,%a0@(UC_FPREGS + _FPREG_PSR) ; \
+ fmovem %fpiar,%a0@(UC_FPREGS + _FPREG_PIADDR) ; \
+ fmovem %fp0-%fp7,%a0@(UC_FPREGS + _FPREG_FP0) ; \
+ movl #(_UC_CPU+_UC_FPU+_UC_USER),%a0@/*(UC_FLAGS)*/
+
+/*
+ * Restore the complete context from the ucontext_t structure pointed
+ * to by %a0. We can't short-cut caller saved registers here. In fact,
+ * we have to do some gymnastics to avoid restoring the saved %sp until
+ * all other saved registers have been read out. This is because there's
+ * a chance the ucontext_t structure is allocated at the base of the new
+ * context's stack.
+ */
+#define SETC \
+ movl %a0@/*(UC_FLAGS)*/,%d0 ; \
+ btstl #_UC_USER_BIT,%d0 ; \
+ beqs 2f ; \
+ andw #_UC_FPU,%d0 ; \
+ beqs 1f ; \
+ fnop ; \
+ fmovem %a0@(UC_FPREGS + _FPREG_PCR),%fpcr ; \
+ fmovem %a0@(UC_FPREGS + _FPREG_PSR),%fpsr ; \
+ fmovem %a0@(UC_FPREGS + _FPREG_PIADDR),%fpiar ; \
+ fmovem %a0@(UC_FPREGS + _FPREG_FP0),%fp0-%fp7 ; \
+1: moveml %a0@(UC_REGS + (_REG_D2 * 4)),%d2-%d7 ; \
+ moveml %a0@(UC_REGS + (_REG_A2 * 4)),%a2-%a6 ; \
+ movl %a0@(UC_REGS + (_REG_SP * 4)),%a1 ; \
+ movl %a0@(UC_REGS + (_REG_PC * 4)),%a1@- ; \
+ movw %a0@(UC_REGS + (_REG_PS * 4) + 2),%cc ; \
+ movl %a1,%sp ; \
+ rts ; \
+2: pea %a0@ ; \
+ jbsr PIC_PLT(_C_LABEL(setcontext))
+ /* NOTREACHED */
+
+#if 1
+/*
+ * int _getcontext_u(ucontext_t *ctx)
+ * Store the current context in the provided ctx structure.
+ */
+ENTRY_NOPROFILE(_getcontext_u)
+ movl %sp@+,%a1 /* a1 -> return address */
+ movl %sp@,%a0 /* a0 -> `from' */
+ GETC
+ moveql #0,%d0
+ jmp %a1@
+
+/*
+ * int _swapcontext_u(ucontext_t *from, const ucontext_t *to)
+ * Save the current context in `from' before switching to the
+ * new context in `to'.
+ */
+ENTRY_NOPROFILE(_swapcontext_u)
+ movl %sp@+,%a1 /* a1 -> return address */
+ movl %sp@,%a0 /* a0 -> `from' */
+ GETC
+ /* FALLTHROUGH */
+
+/*
+ * int _setcontext_u(const ucontext_t *ctx)
+ * Make the context stored in ctx current.
+ */
+ENTRY_NOPROFILE(_setcontext_u)
+ movl %sp@(4),%a0
+ SETC
+ /* NOTREACHED */
+
+
+#else
+ENTRY_NOPROFILE(_getcontext_u)
+ jbra PIC_PLT(_C_LABEL(getcontext))
+
+ENTRY_NOPROFILE(_swapcontext_u)
+ jbra PIC_PLT(_C_LABEL(swapcontext))
+
+ENTRY_NOPROFILE(_setcontext_u)
+ jbra PIC_PLT(_C_LABEL(setcontext))
+#endif
diff --git a/lib/libpthread/arch/m68k/genassym.cf b/lib/libpthread/arch/m68k/genassym.cf
new file mode 100644
index 00000000000..ddad8fc0708
--- /dev/null
+++ b/lib/libpthread/arch/m68k/genassym.cf
@@ -0,0 +1,89 @@
+# $NetBSD: genassym.cf,v 1.2 2003/01/18 10:34:19 thorpej Exp $
+
+# Copyright (c) 2001 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# This code is derived from software contributed to The NetBSD Foundation
+# by Nathan J. Williams and Steve C. Woodford.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the NetBSD
+# Foundation, Inc. and its contributors.
+# 4. Neither the name of The NetBSD Foundation nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+
+include <ucontext.h>
+include <sys/queue.h>
+include "pthread.h"
+include "pthread_int.h"
+include "pthread_md.h"
+
+define PT_NEXT offsetof(struct pthread_st, pt_next)
+define PT_STATE offsetof(struct pthread_st, pt_state)
+define PT_SWITCHTO offsetof(struct pthread_st, pt_switchto)
+define PT_SWITCHTOUC offsetof(struct pthread_st, pt_switchtouc)
+define PT_SLEEPUC offsetof(struct pthread_st, pt_sleepuc)
+define PT_SPINLOCKS offsetof(struct pthread_st, pt_spinlocks)
+define PT_HELDLOCK offsetof(struct pthread_st, pt_heldlock)
+define PT_UC offsetof(struct pthread_st, pt_uc)
+define UC_FLAGS offsetof(ucontext_t, uc_flags)
+define UC_REGS offsetof(ucontext_t, uc_mcontext.__gregs)
+define UC_FPREGS offsetof(ucontext_t, uc_mcontext.__fpregs)
+define UC_PC offsetof(ucontext_t, uc_mcontext.__gregs[_REG_PC])
+
+define PT_STATE_RECYCLABLE PT_STATE_RECYCLABLE
+define RND_CTXSIZE ((sizeof(ucontext_t) + 3) & ~3)
+define STACKSPACE STACKSPACE
+
+define _UC_CPU _UC_CPU
+define _UC_FPU _UC_FPU
+define _UC_USER _UC_USER
+define _UC_USER_BIT _UC_USER_BIT
+
+define _REG_D0 _REG_D0
+define _REG_D1 _REG_D1
+define _REG_D2 _REG_D2
+define _REG_D3 _REG_D3
+define _REG_D4 _REG_D4
+define _REG_D5 _REG_D5
+define _REG_D6 _REG_D6
+define _REG_D7 _REG_D7
+define _REG_A0 _REG_A0
+define _REG_A1 _REG_A1
+define _REG_A2 _REG_A2
+define _REG_A3 _REG_A3
+define _REG_A4 _REG_A4
+define _REG_A5 _REG_A5
+define _REG_A6 _REG_A6
+define _REG_SP _REG_A7
+define _REG_PC _REG_PC
+define _REG_PS _REG_PS
+
+define _FPREG_FP0 offsetof(ucontext_t, uc_mcontext.__fpregs.__fp_fpregs[0])
+define _FPREG_PCR offsetof(ucontext_t, uc_mcontext.__fpregs.__fp_pcr)
+define _FPREG_PSR offsetof(ucontext_t, uc_mcontext.__fpregs.__fp_psr)
+define _FPREG_PIADDR offsetof(ucontext_t, uc_mcontext.__fpregs.__fp_piaddr)
diff --git a/lib/libpthread/arch/m68k/pthread_md.c b/lib/libpthread/arch/m68k/pthread_md.c
new file mode 100644
index 00000000000..faa55bcad3e
--- /dev/null
+++ b/lib/libpthread/arch/m68k/pthread_md.c
@@ -0,0 +1,91 @@
+/* $NetBSD: pthread_md.c,v 1.2 2003/01/18 10:34:19 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2003 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define __PTHREAD_SIGNAL_PRIVATE
+
+#include <assert.h>
+
+#include "pthread.h"
+#include "pthread_int.h"
+
+/*
+ * We only need this to know how much data to copy.
+ */
+static const int exframesize[] = {
+ FMT0SIZE, /* type 0 - normal (68020/030/040/060) */
+ FMT1SIZE, /* type 1 - throwaway (68020/030/040) */
+ FMT2SIZE, /* type 2 - normal 6-word (68020/030/040/060) */
+ FMT3SIZE, /* type 3 - FP post-instruction (68040/060) */
+ FMT4SIZE, /* type 4 - access error/fp disabled (68060) */
+ -1, -1, /* type 5-6 - undefined */
+ FMT7SIZE, /* type 7 - access error (68040) */
+ 58, /* type 8 - bus fault (68010) */
+ FMT9SIZE, /* type 9 - coprocessor mid-instruction (68020/030) */
+ FMTASIZE, /* type A - short bus fault (68020/030) */
+ FMTBSIZE, /* type B - long bus fault (68020/030) */
+ -1, -1, -1, -1 /* type C-F - undefined */
+};
+#define _SIGSTATE_EXFRAMESIZE(fmt) exframesize[(fmt)]
+
+/*
+ * m68k signal contexts are quite screwey, so we have to go through
+ * a fair bit of effort to convert them.
+ */
+void
+pthread__ucontext_to_sigcontext(const sigset_t *mask, ucontext_t *uc,
+ struct pthread__sigcontext *psc)
+{
+
+ uc->uc_sigmask = *mask;
+
+ _MCONTEXT_TO_SIGSTATE(uc, &psc->psc_state); /* must be first */
+ _UCONTEXT_TO_SIGCONTEXT(uc, &psc->psc_context);
+
+ psc->psc_context.sc_ap = (int) &psc->psc_state;
+}
+
+void
+pthread__sigcontext_to_ucontext(const struct pthread__sigcontext *psc,
+ ucontext_t *uc)
+{
+
+ _SIGSTATE_TO_MCONTEXT(&psc->psc_state, uc); /* must be first */
+ _SIGCONTEXT_TO_UCONTEXT(&psc->psc_context, uc);
+
+ uc->uc_flags &= ~_UC_SIGMASK;
+}
diff --git a/lib/libpthread/arch/m68k/pthread_md.h b/lib/libpthread/arch/m68k/pthread_md.h
new file mode 100644
index 00000000000..a4f4282640e
--- /dev/null
+++ b/lib/libpthread/arch/m68k/pthread_md.h
@@ -0,0 +1,124 @@
+/* $NetBSD: pthread_md.h,v 1.2 2003/01/18 10:34:19 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nathan J. Williams.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Based on the i386 version.
+ */
+
+#ifndef _LIB_PTHREAD_M68K_MD_H
+#define _LIB_PTHREAD_M68K_MD_H
+
+
+static __inline long
+pthread__sp(void)
+{
+ long ret;
+ __asm("movl %%sp, %0" : "=g" (ret));
+
+ return ret;
+}
+
+#define pthread__uc_sp(ucp) ((ucp)->uc_mcontext.__gregs[_REG_A7])
+#define pthread__uc_pc(ucp) ((ucp)->uc_mcontext.__gregs[_REG_PC])
+
+/*
+ * Usable stack space below the ucontext_t.
+ * See comment in pthread_switch.S about STACK_SWITCH.
+ */
+#define STACKSPACE 12 /* room for 3 integer values */
+
+/*
+ * Conversions between struct reg and struct mcontext. Used by
+ * libpthread_dbg.
+ */
+
+#define PTHREAD_UCONTEXT_TO_REG(reg, uc) do { \
+ memcpy(&(reg)->r_regs, &(uc)->uc_mcontext.__gregs, \
+ _REG_PC * sizeof(__greg_t)); \
+ (reg)->r_sr = (uc)->uc_mcontext.__gregs[_REG_PS]; \
+ (reg)->r_pc = (uc)->uc_mcontext.__gregs[_REG_PC]; \
+ } while (/*CONSTCOND*/0)
+
+#define PTHREAD_REG_TO_UCONTEXT(uc, reg) do { \
+ memcpy(&(uc)->uc_mcontext.__gregs, &(reg)->r_regs, \
+ _REG_PC * sizeof(__greg_t)); \
+ (uc)->uc_mcontext.__gregs[_REG_PS] = (reg)->r_sr; \
+ (uc)->uc_mcontext.__gregs[_REG_PC] = (reg)->r_pc; \
+ (uc)->uc_flags = ((uc)->uc_flags | _UC_CPU) & ~_UC_USER; \
+ } while (/*CONSTCOND*/0)
+
+#define PTHREAD_UCONTEXT_TO_FPREG(freg, uc) do { \
+ memcpy(&(freg)->r_regs, &(uc)->uc_mcontext.__fpregs.__fp_fpregs,\
+ 8 * 3 * sizeof(int)); \
+ (freg)->r_fpcr = (uc)->uc_mcontext.__fpregs.__fp_pcr; \
+ (freg)->r_fpsr = (uc)->uc_mcontext.__fpregs.__fp_psr; \
+ (freg)->r_fpiar = (uc)->uc_mcontext.__fpregs.__fp_piaddr; \
+ } while (/*CONSTCOND*/0)
+
+#define PTHREAD_FPREG_TO_UCONTEXT(uc, freg) do { \
+ memcpy(&(uc)->uc_mcontext.__fpregs.__fp_fpregs, &(freg)->r_regs,\
+ 8 * 3 * sizeof(int)); \
+ (uc)->uc_mcontext.__fpregs.__fp_pcr = (freg)->r_fpcr; \
+ (uc)->uc_mcontext.__fpregs.__fp_psr = (freg)->r_fpsr; \
+ (uc)->uc_mcontext.__fpregs.__fp_piaddr = (freg)->r_fpiar; \
+ (uc)->uc_flags = ((uc)->uc_flags | _UC_FPU) & ~_UC_USER; \
+ } while (/*CONSTCOND*/0)
+
+#ifdef __PTHREAD_SIGNAL_PRIVATE
+
+#define __M68K_SIGNAL_PRIVATE
+#define __M68K_MCONTEXT_PRIVATE
+
+/*
+ * We need to include signal.h early so that __M68K_SIGNAL_PRIVATE
+ * is noticed in time.
+ */
+#include <signal.h>
+
+#define PTHREAD_SIGCONTEXT_EXTRA \
+ struct sigstate psc_state;
+
+#define PTHREAD_UCONTEXT_TO_SIGCONTEXT(mask, uc, psc) \
+ pthread__ucontext_to_sigcontext((mask), (uc), (psc))
+
+#define PTHREAD_SIGCONTEXT_TO_UCONTEXT(psc, uc) \
+ pthread__sigcontext_to_ucontext((psc), (uc))
+
+#endif /* __PTHREAD_SIGNAL_PRIVATE */
+
+#endif /* _LIB_PTHREAD_M68K_MD_H */
diff --git a/lib/libpthread/arch/m68k/pthread_switch.S b/lib/libpthread/arch/m68k/pthread_switch.S
new file mode 100644
index 00000000000..cd6a300fb30
--- /dev/null
+++ b/lib/libpthread/arch/m68k/pthread_switch.S
@@ -0,0 +1,323 @@
+/* $NetBSD: pthread_switch.S,v 1.2 2003/01/18 10:34:19 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nathan J. Williams and Steve C. Woodford.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Based on the i386 version.
+ */
+
+#include <machine/asm.h>
+#include "assym.h"
+
+/*
+ * This file implements low-level routines that are exported to
+ * the machine-independent parts of the thread library. The routines are:
+ *
+ * void pthread__switch(pthread_t self, pthread_t next);
+ * void pthread__upcall_switch(pthread_t self, pthread_t next);
+ * void pthread__locked_switch(pthread_t self, pthread_t next,
+ * pt_spin_t *lock);
+ *
+ * as well as some utility code used by these routines.
+ */
+
+/* Force an error when "notreached" code is reached. */
+#define NOTREACHED \
+ illegal
+
+/*
+ * void pthread__switch(pthread_t self, pthread_t next);
+ *
+ * Plain switch that doesn't do any special checking or handle
+ * spin-preemption. It isn't used much by normal code, actually; it's
+ * main purpose is to be a basic switch engine when the MI code is
+ * already dealing with spin-preemption or other gunk.
+ */
+
+ENTRY(pthread__switch)
+ movl %sp@(4),%a0 /* a0 = self */
+ movl %sp@(8),%a1 /* a1 = next */
+ lea %sp@(-RND_CTXSIZE),%sp /* Allocate space for the ucontext_t */
+ movl %sp,%a0@(PT_UC)
+ movl %a1@(PT_UC),%sp@-
+ movl %a0@(PT_UC),%sp@-
+ jbsr PIC_PLT(_C_LABEL(_swapcontext_u))
+ lea %sp@(RND_CTXSIZE+8),%sp /* Pop ucontext_t and args */
+ rts
+
+/*
+ * Utility macro to switch to the stack of another thread.
+ * WARNING: This is more subtle than it looks.
+ *
+ * There are a couple of motivations for stack switching. One is that
+ * when exiting an upcall and running a thread, we want to recycle the
+ * state of the upcall before continuing the thread. However, the
+ * stack is part of the upcall state, and we can't be on the upcall
+ * stack when we want to recycle it. Therefore, we pre-switch to the
+ * stack of the thread we're about to switch to and borrow some space
+ * on its stack so that we can recycle the upcall.
+ *
+ * Another is that when performing the slightly sensitive operation of
+ * putting the currently running thread on a (locked) sleep queue then
+ * switching to another thread, we don't want to release the queue
+ * lock until we are no longer on the original stack - if we released
+ * it any earlier, it's possible (epecially on a multiprocessor
+ * system) that the original thread could get taken off the queue and
+ * restarted while we're still using its stack, which would be bad
+ * ("Important saftey tip. Thanks, Egon").
+ *
+ * The subtlety comes from the contents of the stack of the thread
+ * being switched to. Our convention is that the ucontext_t of the
+ * thread is stored near the top of the stack. When the kernel
+ * preempts a thread, it stores the ucontext_t just above the current
+ * top-of-stack and passes the upcall handler the pointer to the
+ * ucontext_t, and the upcall handler stores it in the the pt_uc field
+ * of the thread structure. When user-level code voluntairly switches
+ * from one thread to another, the ucontext_t is just below the top of
+ * the stack, and we impose a limit on the amount of stack space above
+ * the ucontext_t that may be used. This way, we can perform the stack
+ * switch safely by setting the stack pointer to thread->pt_uc -
+ * STACKSPACE.
+ * Note carefully that we do the subtraction *before* putting the
+ * value in the stack pointer. Since preemption can occur at any time,
+ * and the kernel will happily write the new ucontext_t below the
+ * current stack pointer, it is unsafe for us to ever set the stack
+ * pointer above potentially valid data, even for one instruction.
+ *
+ * Got it? Good. Now go read it again.
+ *
+ */
+
+#define STACK_SWITCH(pt,tmp) \
+ movl %pt@(PT_UC),%tmp ; \
+ lea %tmp@(-STACKSPACE),%sp
+
+/*
+ * Helper switch code used by pthread__locked_switch() and
+ * pthread__upcall_switch() when they discover spinlock preemption.
+ *
+ * Call with:
+ * a1 = from, a2 = to
+ */
+Lpthread__switch_away_decrement:
+ STACK_SWITCH(a2,a0) /* side effect: a0 = to->pt_uc */
+
+ /* If we're invoked from the switch-to-next provisions of
+ * pthread__locked_switch or pthread__upcall_switch, there
+ * may be a fake spinlock-count set. If so, we decrement it
+ * once we're no longer using the old stack.
+ */
+ subql #1, %a1@(PT_SPINLOCKS)
+ pea %a0@
+ jbsr PIC_PLT(_C_LABEL(_setcontext_u))
+ NOTREACHED
+
+Lpthread__switch_away_no_decrement:
+ STACK_SWITCH(a2,a0) /* side effect: a0 = to->pt_uc */
+ pea %a0@
+ jbsr PIC_PLT(_C_LABEL(_setcontext_u))
+ NOTREACHED
+
+
+/*
+ * void pthread__locked_switch(pthread_t self, pthread_t next,
+ * pt_spin_t *lock);
+ *
+ * Switch away from a thread that is holding a lock on a queue (to
+ * prevent being removed from the queue before being switched away).
+ */
+ENTRY(pthread__locked_switch)
+ lea %sp@(-RND_CTXSIZE),%sp /* Space for ucontext_t */
+ movl %sp@(RND_CTXSIZE+8),%a0 /* `next': Thread to switch to */
+ addql #1,%a0@(PT_SPINLOCKS) /* Make sure we get continued */
+ movl %sp@(RND_CTXSIZE+4),%a0 /* `self': current thread */
+ movl %sp,%a0@(PT_UC) /* self->pt_uc = &context */
+
+ /*
+ * Save `self's context
+ */
+ movl %sp,%sp@-
+ jbsr PIC_PLT(_C_LABEL(_getcontext_u))
+ addql #4,%sp
+
+ /*
+ * Edit the context so that it continues as if returning from
+ * the _setcontext_u below.
+ */
+ lea %pc@(Llocked_return_point),%a0
+ movl %a0,%sp@(UC_PC)
+
+ /*
+ * Fetch function parameters now that callee-saved registers
+ * are available. (They're saved in the context)
+ */
+ movl %sp@(RND_CTXSIZE+4),%a2 /* a2 = self */
+ movl %sp@(RND_CTXSIZE+8),%a1 /* a1 = next */
+ movl %sp@(RND_CTXSIZE+12),%a0 /* a0 = lock */
+ movl %sp,%a4 /* a4 = &context */
+
+ STACK_SWITCH(a1,a3) /* side effect: a3 = next->pt_uc */
+
+ /* Now running on `next's stack. */
+
+ /* Check if the original thread was preempted while holding
+ * its queue lock.
+ */
+ movl %a2@(PT_NEXT),%d0 /* self->pt_next == 0? */
+ beqs Llocked_no_old_preempt /* Yup */
+
+ /* Yes, it was. Stash the thread we were going to
+ * switch to, the lock the original thread was holding,
+ * and go to the next thread in the chain.
+ * Mark the fact that this was a locked switch, and so the
+ * thread does not need to be put on a run queue.
+ * Don't release the lock. It's possible that if we do so,
+ * PT_SWITCHTO will be stomped by another switch_lock and
+ * preemption.
+ */
+ movl %a1,%a2@(PT_SWITCHTO)
+ movl %a3,%a2@(PT_SWITCHTOUC)
+ movl %a0,%a2@(PT_HELDLOCK)
+ subql #1,%a2@(PT_SPINLOCKS)
+
+ /* Save the context we previously stored in self->pt_uc
+ * that was overwritten when we were preempted and continued,
+ * so we need to put it somewhere.
+ */
+ movl %a4,%a2@(PT_SLEEPUC)
+
+ /* a1 already == from */
+ movl %d0,%a2 /* to = self->pt_next */
+ jbra Lpthread__switch_away_decrement
+ /* NOTREACHED */
+
+Llocked_no_old_preempt:
+ /* a0 = lock, a1 = next, a2 = self, a3 = next->pt_uc */
+
+ /*
+ * We've moved to the new stack, and the old context has been
+ * saved. The queue lock can be released.
+ */
+ subql #1,%a2@(PT_SPINLOCKS) /* self->pt_spinlocks-- */
+
+ /* We happen to know that this is the right way to release a lock. */
+ clrb %a0@ /* *lock = 0 */
+
+ /* Remove the fake lock */
+ subql #1,%a1@(PT_SPINLOCKS) /* next->pt_spinlocks-- */
+
+ /* Check if we were preempted while holding the fake lock. */
+ movl %a1@(PT_NEXT),%d0 /* (%d0 = next->pt_next) == NULL? */
+ beqs Llocked_no_new_preempt /* Yup */
+
+ /* Yes, we were. Bummer. Go to the next element in the chain. */
+ movl %a1,%a1@(PT_SWITCHTO) /* next->pt_switchto = next */
+ movl %a3,%a1@(PT_SWITCHTOUC)
+ movl %d0,%a2
+ jbra Lpthread__switch_away_no_decrement
+
+Llocked_no_new_preempt:
+ pea %a3@ /* _setcontext_u(next->pt_uc) */
+ jbsr PIC_PLT(_C_LABEL(_setcontext_u))
+ NOTREACHED
+
+Llocked_return_point:
+ /* We're back on the original stack. */
+ lea %sp@(RND_CTXSIZE+4),%sp /* Ditch the ucontext_t */
+ rts
+
+
+
+/*
+ * void pthread__upcall_switch(pthread_t self, pthread_t next);
+ *
+ * Quit an upcall, recycle it, and jump to the selected thread.
+ * Since this code never returns, we are free to use callee-saved
+ * registers.
+ */
+ENTRY(pthread__upcall_switch)
+ /* Save args into registers so we can stop using the old stack. */
+ movl %sp@(4),%a2 /* a2 = self (the upcall thread) */
+ movl %sp@(8),%a3 /* a3 = next */
+
+ /* Create a `fake' lock count so we are `continued' */
+ addql #1,%a3@(PT_SPINLOCKS)
+
+ STACK_SWITCH(a3,a4) /* side effect: a4 = next->pt_uc */
+
+ /* Check if the upcall was preempted and continued. */
+ movl %a2@(PT_NEXT),%d0
+ beqs Lupcall_no_old_preempt
+
+ /* Yes, it was. Stash the thread we were going to
+ * switch to, and go to the next thread in the chain.
+ */
+ movl %a3,%a2@(PT_SWITCHTO)
+ movl %a4,%a2@(PT_SWITCHTOUC)
+ moveql #PT_STATE_RECYCLABLE,%d1
+ movl %d1,%a2@(PT_STATE)
+ movl %a3,%a1 /* from(a1) = next */
+ movl %d0,%a2 /* to(a2) = self->pt_next */
+ jbra Lpthread__switch_away_decrement
+ /* NOTREACHED */
+
+Lupcall_no_old_preempt:
+ /* pthread__sa_recycle(old, new) */
+ pea %a3@ /* new = next(a3) */
+ pea %a2@ /* old = self(a2) */
+ jbsr PIC_PLT(_C_LABEL(pthread__sa_recycle))
+ addql #8,%sp
+
+ /* Release the fake lock */
+ subql #1,%a3@(PT_SPINLOCKS)
+
+ /* Check if we were preempted while holding the fake lock. */
+ movl %a3@(PT_NEXT),%d0
+ beqs Lupcall_no_new_preempt
+
+ /* Yes, we were. Bummer. Go to the next element in the chain. */
+ movl %a3,%a3@(PT_SWITCHTO)
+ movl %a4,%a3@(PT_SWITCHTOUC)
+ movl %d0,%a2 /* to(a2) = self->pt_next */
+ jbra Lpthread__switch_away_no_decrement
+ /* NOTREACHED */
+
+Lupcall_no_new_preempt:
+ pea %a4@
+ jbsr PIC_PLT(_C_LABEL(_setcontext_u))
+ NOTREACHED
+
diff --git a/lib/libpthread/arch/mips/SYS.h b/lib/libpthread/arch/mips/SYS.h
new file mode 100644
index 00000000000..9077f95a65f
--- /dev/null
+++ b/lib/libpthread/arch/mips/SYS.h
@@ -0,0 +1,131 @@
+/* $NetBSD: SYS.h,v 1.2 2003/01/18 10:34:19 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 1996 Jonathan Stone
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jonathan Stone for
+ * the NetBSD Project.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ralph Campbell.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)SYS.h 8.1 (Berkeley) 6/4/93
+ */
+
+#include <sys/syscall.h>
+#include <mips/asm.h>
+
+
+/*
+ * If compiling for shared libs, Emit sysV ABI PIC segment pseudo-ops.
+ *
+ * i) Emit .abicalls before .LEAF entrypoint, and .cpload/.cprestore after.
+ * ii) Do interprocedure jumps indirectly via t9, with the side-effect of
+ * preserving the callee's entry address in t9.
+ */
+#ifdef __ABICALLS__
+ .abicalls
+# define PIC_PROLOGUE(x,sr) .set noreorder; .cpload sr; .set reorder
+#else
+# define PIC_PROLOGUE(x,sr)
+#endif
+#define PIC_CALL(l,sr) \
+ la sr, _C_LABEL(l) ; \
+ jalr sr ; \
+
+#ifdef __STDC__
+# define SYSTRAP(x) li v0,SYS_ ## x; syscall;
+#else
+# define SYSTRAP(x) li v0,SYS_/**/x; syscall;
+#endif
+
+
+/*
+ * Do a syscall that cannot fail (sync, get{p,u,g,eu,eg)id)
+ */
+#define RSYSCALL_NOERROR(x) \
+ PSEUDO_NOERROR(x,x)
+
+/*
+ * Do a normal syscall.
+ */
+#define RSYSCALL(x) \
+ PSEUDO(x,x)
+
+
+/*
+ * Do a renamed or pseudo syscall (e.g., _exit()), where the entrypoint
+ * and syscall name are not the same.
+ */
+#define PSEUDO_NOERROR(x,y) \
+LEAF(x); \
+ SYSTRAP(y); \
+ j ra; \
+ END(x)
+
+#define PSEUDO(x,y) \
+LEAF(x); \
+ PIC_PROLOGUE(x,t9); \
+ SYSTRAP(y); \
+ bne a3,zero,err; \
+ j ra; \
+err: \
+ PIC_CALL(__cerror,t9); \
+ END(x)
diff --git a/lib/libpthread/arch/mips/_context_u.S b/lib/libpthread/arch/mips/_context_u.S
new file mode 100644
index 00000000000..f247d6491e6
--- /dev/null
+++ b/lib/libpthread/arch/mips/_context_u.S
@@ -0,0 +1,128 @@
+/* $NetBSD: _context_u.S,v 1.2 2003/01/18 10:34:19 thorpej Exp $ */
+
+/*
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Wayne Knowles
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "assym.h"
+#include "SYS.h"
+#include <machine/asm.h>
+#include <machine/mcontext.h>
+
+/*
+ * Define:
+ * int _getcontext_u(ucontext_t *ctx)
+ * Store the current context in the provided ctx structure.
+ * [only store the callee-saved registers]
+ * int _setcontext_u(const ucontext_t *ctx)
+ * Restore the current context from the provided ctx structure.
+ * int _swapcontext_u(ucontext_t *from_ctx, const ucontext_t *to_ctx)
+ * First, store the current context into from_ctx and then
+ * restore the current context from the to_ctx.
+ */
+
+/* Macro to Save the callee-save register set */
+#define GETC(reg) ; \
+ REG_PROLOGUE ; \
+ REG_S s0, (UC_REGS + _REG_S0 * SZREG)(reg) ; \
+ REG_S s1, (UC_REGS + _REG_S1 * SZREG)(reg) ; \
+ REG_S s2, (UC_REGS + _REG_S2 * SZREG)(reg) ; \
+ REG_S s3, (UC_REGS + _REG_S3 * SZREG)(reg) ; \
+ REG_S s4, (UC_REGS + _REG_S4 * SZREG)(reg) ; \
+ REG_S s5, (UC_REGS + _REG_S5 * SZREG)(reg) ; \
+ REG_S s6, (UC_REGS + _REG_S6 * SZREG)(reg) ; \
+ REG_S s7, (UC_REGS + _REG_S7 * SZREG)(reg) ; \
+ REG_S gp, (UC_REGS + _REG_GP * SZREG)(reg) ; \
+ REG_S sp, (UC_REGS + _REG_SP * SZREG)(reg) ; \
+ REG_S s8, (UC_REGS + _REG_S8 * SZREG)(reg) ; \
+ REG_S ra, (UC_REGS + _REG_RA * SZREG)(reg) ; \
+ REG_S ra, (UC_REGS + _REG_EPC* SZREG)(reg) ; \
+ REG_EPILOGUE ; \
+ li t0, 1 ; \
+ sll t0, t0, _UC_USER_BIT ; \
+ ori t0, t0, _UC_CPU ; \
+ sw t0, (UC_FLAGS)(reg)
+
+#define SETC(reg) ; \
+ lw t0, (UC_FLAGS)(reg) ; \
+ li t1, 1 ; \
+ sll t1, t1, _UC_USER_BIT ; \
+ and t0, t1, t0 ; \
+ beq t0, zero, 1f ; \
+ REG_PROLOGUE ; \
+ REG_L s0, (UC_REGS + _REG_S0 * SZREG)(reg) ; \
+ REG_L s1, (UC_REGS + _REG_S1 * SZREG)(reg) ; \
+ REG_L s2, (UC_REGS + _REG_S2 * SZREG)(reg) ; \
+ REG_L s3, (UC_REGS + _REG_S3 * SZREG)(reg) ; \
+ REG_L s4, (UC_REGS + _REG_S4 * SZREG)(reg) ; \
+ REG_L s5, (UC_REGS + _REG_S5 * SZREG)(reg) ; \
+ REG_L s6, (UC_REGS + _REG_S6 * SZREG)(reg) ; \
+ REG_L s7, (UC_REGS + _REG_S7 * SZREG)(reg) ; \
+ REG_L s8, (UC_REGS + _REG_S8 * SZREG)(reg) ; \
+ REG_L t9, (UC_REGS + _REG_EPC * SZREG)(reg) ; \
+ REG_L gp, (UC_REGS + _REG_GP * SZREG)(reg) ; \
+ REG_L sp, (UC_REGS + _REG_SP * SZREG)(reg) ; \
+ REG_L ra, (UC_REGS + _REG_RA * SZREG)(reg) ; \
+ REG_EPILOGUE ; \
+ ; \
+ /* part procedure call, part RET */ ; \
+ j t9 ; \
+ nop ; \
+ /* NOTREACHED */ ; \
+ ; \
+1: ; \
+ move a0, reg ; \
+ la t9, _C_LABEL(setcontext) ; \
+ jr t9 ; \
+ nop ; \
+ /* NOTREACHED */
+
+LEAF(_getcontext_u)
+ PIC_PROLOGUE(_getcontext_u, t9)
+ GETC(a0)
+ xor v0, v0, v0
+ j ra
+END(_getcontext_u)
+
+LEAF(_setcontext_u)
+ PIC_PROLOGUE(_setcontext_u, t9)
+ SETC(a0)
+END(_setcontext_u)
+
+LEAF(_swapcontext_u)
+ PIC_PROLOGUE(_swapcontext_u, t9)
+ GETC(a0)
+ SETC(a1)
+END(_swapcontext_u)
diff --git a/lib/libpthread/arch/mips/genassym.cf b/lib/libpthread/arch/mips/genassym.cf
new file mode 100644
index 00000000000..9ae225a978b
--- /dev/null
+++ b/lib/libpthread/arch/mips/genassym.cf
@@ -0,0 +1,63 @@
+# $NetBSD: genassym.cf,v 1.2 2003/01/18 10:34:20 thorpej Exp $
+
+# Copyright (c) 2001 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# This code is derived from software contributed to The NetBSD Foundation
+# by Nathan J. Williams.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the NetBSD
+# Foundation, Inc. and its contributors.
+# 4. Neither the name of The NetBSD Foundation nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+
+include <ucontext.h>
+include <sys/queue.h>
+include "pthread.h"
+include "pthread_int.h"
+include "pthread_md.h"
+
+define PT_NEXT offsetof(struct pthread_st, pt_next)
+define PT_STATE offsetof(struct pthread_st, pt_state)
+define PT_SWITCHTO offsetof(struct pthread_st, pt_switchto)
+define PT_SWITCHTOUC offsetof(struct pthread_st, pt_switchtouc)
+define PT_SLEEPUC offsetof(struct pthread_st, pt_sleepuc)
+define PT_SPINLOCKS offsetof(struct pthread_st, pt_spinlocks)
+define PT_HELDLOCK offsetof(struct pthread_st, pt_heldlock)
+define PT_UC offsetof(struct pthread_st, pt_uc)
+define CONTEXTSIZE sizeof(ucontext_t)
+define UC_FLAGS offsetof(ucontext_t, uc_flags)
+define UC_REGS offsetof(ucontext_t, uc_mcontext.__gregs)
+define UC_EPC offsetof(ucontext_t, uc_mcontext.__gregs[_REG_EPC])
+define PT_STATE_RECYCLABLE PT_STATE_RECYCLABLE
+define STACKSPACE STACKSPACE
+
+# uc_flags
+define _UC_CPU _UC_CPU
+define _UC_FPU _UC_FPU
+define _UC_USER_BIT _UC_USER_BIT
diff --git a/lib/libpthread/arch/mips/pthread_md.h b/lib/libpthread/arch/mips/pthread_md.h
new file mode 100644
index 00000000000..27cfa04e773
--- /dev/null
+++ b/lib/libpthread/arch/mips/pthread_md.h
@@ -0,0 +1,113 @@
+/* $NetBSD: pthread_md.h,v 1.2 2003/01/18 10:34:20 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nathan J. Williams.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _LIB_PTHREAD_MIPS_MD_H
+#define _LIB_PTHREAD_MIPS_MD_H
+
+static __inline long
+pthread__sp(void)
+{
+ long ret;
+
+ __asm("move %0, $sp" : "=r" (ret));
+
+ return ret;
+}
+
+#define pthread__uc_sp(ucp) ((ucp)->uc_mcontext.__gregs[_REG_SP])
+#define pthread__uc_pc(ucp) ((ucp)->uc_mcontext.__gregs[_REG_EPC])
+
+/*
+ * Usable stack space below the ucontext_t.
+ * For a good time, see comments in pthread_switch.S and
+ * ../i386/pthread_switch.S about STACK_SWITCH.
+ */
+#define STACKSPACE 16 /* 4 integer valuses */
+
+/*
+ * Conversions between struct reg and struct mcontext. Used by
+ * libpthread_dbg. Note that in the "reg" structure, the indices
+ * are the same as are used in the "frame" structure in the kernel.
+ * These do NOT, in all cases, match the indices used in the
+ * "mcontext" structure.
+ */
+#include <mips/regnum.h>
+
+#define PTHREAD_UCONTEXT_TO_REG(reg, uc) \
+do { \
+ memcpy(&(reg)->r_regs[AST], &(uc)->uc_mcontext.__gregs[_REG_AT],\
+ sizeof(__greg_t) * 31); \
+ (reg)->r_regs[MULLO] = (uc)->uc_mcontext.__gregs[_REG_MDLO]; \
+ (reg)->r_regs[MULHI] = (uc)->uc_mcontext.__gregs[_REG_MDHI]; \
+ (reg)->r_regs[CAUSE] = (uc)->uc_mcontext.__gregs[_REG_CAUSE]; \
+ (reg)->r_regs[PC] = (uc)->uc_mcontext.__gregs[_REG_EPC]; \
+ (reg)->r_regs[SR] = (uc)->uc_mcontext.__gregs[_REG_SR]; \
+} while (/*CONSTCOND*/0)
+
+#define PTHREAD_REG_TO_UCONTEXT(uc, reg) \
+do { \
+ memcpy(&(uc)->uc_mcontext.__gregs[_REG_AT], &(reg)->r_regs[AST],\
+ sizeof(__greg_t) * 31); \
+ (uc)->uc_mcontext.__gregs[_REG_MDLO] = (reg)->r_regs[MULLO]; \
+ (uc)->uc_mcontext.__gregs[_REG_MDHI] = (reg)->r_regs[MULHI]; \
+ (uc)->uc_mcontext.__gregs[_REG_CAUSE] = (reg)->r_regs[CAUSE]; \
+ (uc)->uc_mcontext.__gregs[_REG_EPC] = (reg)->r_regs[PC]; \
+ (uc)->uc_mcontext.__gregs[_REG_SR] = (reg)->r_regs[SR]; \
+ \
+ (uc)->uc_flags = ((uc)->uc_flags | _UC_CPU) & ~_UC_USER; \
+} while (/*CONSTCOND*/0)
+
+#define PTHREAD_UCONTEXT_TO_FPREG(freg, uc) \
+do { \
+ memcpy((freg), &(uc)->uc_mcontext.__fpregs.__fp_r.__fp_regs32, \
+ sizeof((uc)->uc_mcontext.__fpregs.__fp_r.__fp_regs32)); \
+ (freg)->r_regs[FSR - FPBASE] = \
+ (uc)->uc_mcontext.__fpregs.__fp_csr; \
+} while (/*CONSTCOND*/0)
+
+#define PTHREAD_FPREG_TO_UCONTEXT(uc, freg) \
+do { \
+ memcpy(&(uc)->uc_mcontext.__fpregs.__fp_r.__fp_regs32, (freg), \
+ sizeof((uc)->uc_mcontext.__fpregs.__fp_r.__fp_regs32)); \
+ (uc)->uc_mcontext.__fpregs.__fp_csr = \
+ (freg)->r_regs[FSR - FPBASE]; \
+ \
+ (uc)->uc_flags = ((uc)->uc_flags | _UC_FPU) & ~_UC_USER; \
+} while (/*CONSTCOND*/0)
+
+#endif /* !_LIB_PTHREAD_MIPS_MD_H */
diff --git a/lib/libpthread/arch/mips/pthread_switch.S b/lib/libpthread/arch/mips/pthread_switch.S
new file mode 100644
index 00000000000..874e74a98cf
--- /dev/null
+++ b/lib/libpthread/arch/mips/pthread_switch.S
@@ -0,0 +1,302 @@
+/* $NetBSD: pthread_switch.S,v 1.2 2003/01/18 10:34:20 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Wayne Knowles.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This file implements low-level routines that are exported to
+ * the machine-independent parts of the thread library. The routines are:
+ *
+ * void pthread__switch(pthread_t self, pthread_t next);
+ * void pthread__upcall_switch(pthread_t self, pthread_t next);
+ * void pthread__locked_switch(pthread_t self, pthread_t next,
+ * pt_spin_t *lock);
+ *
+ * as well as some utility code used by these routines.
+ */
+
+#include <machine/asm.h>
+#include "assym.h"
+
+#ifdef __ABICALLS__
+ .abicalls
+# define PIC_PROLOGUE(x,sr) \
+ .set noreorder ; \
+ .cpload sr ; \
+ .set reorder
+
+# define PIC_CALL(l,sr) \
+ la sr, _C_LABEL(l) ; \
+ jalr sr
+
+#else
+# define PIC_PROLOGUE(x,sr)
+# define PIC_CALL(l,sr) \
+ jalr _C_LABEL(l)
+
+#endif
+
+/* Force an error when "notreached" code is reached. */
+#define NOTREACHED \
+ break 1 ; \
+ /* NOTREACHED */
+
+NESTED(pthread__switch, CALLFRAME_SIZ+CONTEXTSIZE, ra)
+ .mask 0x80000000, (CALLFRAME_RA - CALLFRAME_SIZ)
+ PIC_PROLOGUE(pthread__switch, t9)
+ subu sp, sp, (CALLFRAME_SIZ+CONTEXTSIZE)
+ sw ra, CALLFRAME_RA(sp)
+ la t0, CALLFRAME_SIZ(sp)
+ sw t0, PT_UC(a0)
+ move a0, t0
+ lw a1, PT_UC(a1)
+ PIC_CALL(_swapcontext_u, t9)
+ lw ra, CALLFRAME_RA(sp)
+ addiu sp, (CALLFRAME_SIZ+CONTEXTSIZE)
+ j ra
+ END(pthread__switch)
+
+/* *** WARNING ***
+ * STACK_SWITCH is more subtle than it looks. Please go read the extended
+ * comment in the i386 pthread_switch.S file.
+ */
+
+#define STACK_SWITCH(pt,tmp) \
+ lw tmp, PT_UC(pt) ; \
+ la sp, -STACKSPACE(tmp)
+
+/*
+ * Helper switch code used by pthread__locked_switch() and
+ * pthread__upcall_switch() when they discover spinlock preemption.
+ */
+
+NESTED(pthread__switch_away, CALLFRAME_SIZ, ra)
+ .mask 0x00000000, (CALLFRAME_RA - CALLFRAME_SIZ)
+ STACK_SWITCH(a1, s2)
+
+ /* If we're invoked from the switch-to-next provisions of
+ * pthread__locked_switch or pthread__upcall_switch, there may
+ * be a fake spinlock-count set. If so, they will set a2 to
+ * let us know, and we decrement it now that we're no longer
+ * using the old stack.
+ */
+ beqz a2, 1f
+ lw t0, PT_SPINLOCKS(a0)
+ subu t0, t0, 1
+ sw t0, PT_SPINLOCKS(a0)
+
+1: move a0, s2
+ PIC_CALL(_setcontext_u, t9)
+ NOTREACHED
+ END(pthread__switch_away)
+
+NESTED(pthread__locked_switch, CALLFRAME_SIZ+CONTEXTSIZE, ra)
+ .mask 0x80000070, (CALLFRAME_RA - CALLFRAME_SIZ)
+ PIC_PROLOGUE(pthread__locked_switch, t9)
+ subu sp, sp, (CALLFRAME_SIZ+CONTEXTSIZE)
+ sw ra, CALLFRAME_RA(sp)
+ sw a0, 0(sp)
+ sw a1, 4(sp)
+ sw a2, 8(sp)
+
+ /* Make sure we get continuted */
+ lw t0, PT_SPINLOCKS(a1)
+ addiu t0, t0, 1
+ sw t0, PT_SPINLOCKS(a1)
+
+ /* Get the current context */
+ la t0, CALLFRAME_SIZ(sp)
+ sw t0, PT_UC(a0)
+ move a0, t0
+ PIC_CALL(_getcontext_u, t9)
+ lw a0, 0(sp)
+ lw a1, 4(sp)
+ lw a2, 8(sp)
+
+ /* Edit the context to make it continue below, rather than here */
+ la t3, CALLFRAME_SIZ(sp)
+ la t1, locked_return_point
+ sw t1, UC_EPC(t3)
+
+ STACK_SWITCH(a1, t2)
+
+ /* Check if the switcher was preempted and continued to here. */
+ lw t0, PT_NEXT(a0)
+ beqz t0, 1f
+ nop
+
+ /* Yes, it was. Stash the thread we were going to switch to,
+ * the lock the original thread was holding,
+ * and switch to the next thread in the continuation chain.
+ * Mark the fact that this was a locked switch, and so the
+ * thread does not need to be put on a run queue.
+ * Don't release the lock. It's possible that if we do so,
+ * PT_SWITCHTO will be stomped by another switch_lock and
+ * preemption.
+ */
+ sw a1, PT_SWITCHTO(a0)
+ sw t2, PT_SWITCHTOUC(a0)
+ sw a2, PT_HELDLOCK(a0)
+ lw t4, PT_SPINLOCKS(a0)
+ subu t4, t4, 1
+ sw t4, PT_SPINLOCKS(a0)
+
+ /* Save the context we previously stored in PT_UC(a0);
+ * that was overwritten when we were preempted and continued,
+ * so we need to put it somewhere.
+ */
+ sw t3, PT_SLEEPUC(a0)
+
+ move a0, a1
+ move a1, t0
+ li a2, 1
+ j pthread__switch_away
+ NOTREACHED
+
+ /* No locked old-preemption */
+1: /* We've moved to the new stack, and the old context has been
+ * saved. The queue lock can be released.
+ */
+ /* Reduce the lock count... */
+ lw t0, PT_SPINLOCKS(a0)
+ subu t0, t0, 1
+ sw t0, PT_SPINLOCKS(a0)
+ /* ... actually release the lock.. */
+ nop # sync
+ sw zero, 0(a2)
+ /* .. and remove the fake lock */
+ lw t0, PT_SPINLOCKS(a1)
+ subu t0, t0, 1
+ sw t0, PT_SPINLOCKS(a1)
+
+ /* Check if we were preempted while holding the fake lock. */
+ lw t0, PT_NEXT(a1)
+ beqz t0, 2f
+ nop
+
+ /* Yes, we were. Go to the next element in the chain. */
+ sw a1, PT_SWITCHTO(a1)
+ sw t2, PT_SWITCHTOUC(a1)
+ move a0, a1
+ move a1, t0
+ li a2, 0
+ j pthread__switch_away
+ NOTREACHED
+
+2: move a0, t2
+ PIC_CALL(_setcontext_u, t9)
+ NOTREACHED
+
+locked_return_point:
+ lw ra, CALLFRAME_RA(sp)
+ addiu sp, sp, (CALLFRAME_SIZ+CONTEXTSIZE)
+ j ra
+ END(pthread__locked_switch)
+
+/*
+ * void pthread__upcall_switch(pthread_t self, pthread_t next);
+ *
+ * Quit an upcall, recycle it, and jump to the selected thread.
+ */
+
+NESTED(pthread__upcall_switch, 0, ra)
+ .mask 0x00000000, (CALLFRAME_RA - CALLFRAME_SIZ)
+ PIC_PROLOGUE(pthread__upcall_switch, t9)
+
+ /* Loading the global pointer is unnecessary; this routine
+ * is only ever called from the pthreads module, and the C
+ * code will have gp set up.
+ *
+ * Also, this code never returns, so we can treat s0-s6 as
+ * convenient registers that will be saved for us by callees,
+ * but that we do not have to save.
+ */
+
+ /* Create a "fake" lock count so that this code will be continued */
+ lw t0, PT_SPINLOCKS(a1)
+ addiu t0, t0, 1
+ sw t0, PT_SPINLOCKS(a1)
+
+ STACK_SWITCH(a1,s2)
+
+ /* Check if the upcall code was preempted and continued to here. */
+ lw t0, PT_NEXT(a0)
+ beqz t0, 1f
+
+ /* Yes, it was. Stash the thread we were going to switch to,
+ * and switch to the next thread in the continuation chain.
+ */
+ sw a1, PT_SWITCHTO(a0)
+ sw s2, PT_SWITCHTOUC(a0)
+ li t1, PT_STATE_RECYCLABLE
+ sw t1, PT_STATE(a0)
+ move a0, a1
+ move a1, t0
+ li a2, 1
+ j pthread__switch_away
+ NOTREACHED
+
+ /* No old-upcall-preemption */
+1: move s0, a0
+ move s1, a1
+ PIC_CALL(pthread__sa_recycle, t9)
+ move a0, s0
+ move a1, s1
+
+ /* We can now release the fake lock. */
+ lw t0, PT_SPINLOCKS(a1)
+ subu t0, t0, 1
+ sw t0, PT_SPINLOCKS(a1)
+
+ /* Check if we were preempted and continued while faking the lock */
+ lw t0, PT_NEXT(a1)
+ beqz t0, 2f
+
+ /* Yes, we were. Stash the to-be-switched-to context in our thread
+ * structure and go to the next link in the chain.
+ */
+ sw a1, PT_SWITCHTO(a1)
+ sw s2, PT_SWITCHTOUC(a1)
+ move a0, a1
+ move a1, t0
+ li a2, 0
+ j pthread__switch_away
+
+ /* No new-upcall-preemption */
+2: move a0, s2
+ PIC_CALL(_setcontext_u, t9)
+ NOTREACHED
+ END(pthread__upcall_switch)
diff --git a/lib/libpthread/arch/powerpc/_context_u.S b/lib/libpthread/arch/powerpc/_context_u.S
new file mode 100644
index 00000000000..4ed64e26d68
--- /dev/null
+++ b/lib/libpthread/arch/powerpc/_context_u.S
@@ -0,0 +1,167 @@
+/* $NetBSD: _context_u.S,v 1.2 2003/01/18 10:34:20 thorpej Exp $ */
+
+/*
+ * Copyright (c) 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Allen Briggs for Wasabi Systems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the NetBSD Project by
+ * Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <machine/asm.h>
+#include "assym.h"
+
+/*
+ * Define:
+ * int _getcontext_u(ucontext_t *ctx)
+ * Store the current context in the provided ctx structure.
+ * [only store the callee-saved registers]
+ * int _setcontext_u(const ucontext_t *ctx)
+ * Restore the current context from the provided ctx structure.
+ * int _swapcontext_u(ucontext_t *from_ctx, const ucontext_t *to_ctx)
+ * First, store the current context into from_ctx and then
+ * restore the current context from the to_ctx.
+ */
+
+/*
+ * According to the SVR4 ABI for PPC, r1, r14-r31, LR, and portions of the
+ * CR are non-volatile. I.e., they need to be preserved across the function
+ * call.
+ *
+ * If _getcontext_u is called, set the UC_USER_BIT in the UC_FLAGS. If
+ * _setcontext_u sees that bit, then it will just restore this part of the
+ * context, otherwise, it will call through to setcontext(2).
+ *
+ * Note: 0xd032 (put in MSR) is a magic constant that keeps the kernel
+ * setcontext(2) happy as it compares this value to srr1 to see if it
+ * should return EINVAL.
+ */
+#define GETC(reg) \
+ stw %r1, (_REG_R0 + 1*4)(reg) ; \
+ stmw %r14, (_REG_R0 + 14*4)(reg) ; \
+ mfcr %r0 ; \
+ stw %r0, _REG_CR(reg) ; \
+ mflr %r0 ; \
+ stw %r0, _REG_LR(reg) ; \
+ stw %r0, _REG_PC(reg) ; \
+ xor %r0, %r0, %r0 ; \
+ ori %r0, %r0, 0xf032 ; \
+ stw %r0, _REG_MSR(reg) ; \
+ ; \
+ mffs %r0 ; \
+ stfd %r0, (_REG_FPSCR - 4)(reg) ; \
+ stfd %r14, (_REG_FP0 + 14*8)(reg) ; \
+ stfd %r15, (_REG_FP0 + 15*8)(reg) ; \
+ stfd %r16, (_REG_FP0 + 16*8)(reg) ; \
+ stfd %r17, (_REG_FP0 + 17*8)(reg) ; \
+ stfd %r18, (_REG_FP0 + 18*8)(reg) ; \
+ stfd %r19, (_REG_FP0 + 19*8)(reg) ; \
+ stfd %r20, (_REG_FP0 + 20*8)(reg) ; \
+ stfd %r21, (_REG_FP0 + 21*8)(reg) ; \
+ stfd %r22, (_REG_FP0 + 22*8)(reg) ; \
+ stfd %r23, (_REG_FP0 + 23*8)(reg) ; \
+ stfd %r24, (_REG_FP0 + 24*8)(reg) ; \
+ stfd %r25, (_REG_FP0 + 25*8)(reg) ; \
+ stfd %r26, (_REG_FP0 + 26*8)(reg) ; \
+ stfd %r27, (_REG_FP0 + 27*8)(reg) ; \
+ stfd %r28, (_REG_FP0 + 28*8)(reg) ; \
+ stfd %r29, (_REG_FP0 + 29*8)(reg) ; \
+ stfd %r30, (_REG_FP0 + 30*8)(reg) ; \
+ stfd %r31, (_REG_FP0 + 31*8)(reg) ; \
+ ; \
+ li %r8, 1 ; \
+ stw %r8, (_UC_FPVALID)(reg) ; \
+ slwi %r9, %r8, _UC_USER_BIT ; \
+ ori %r9, %r9, (_UC_CPU | _UC_FPU) ; \
+ stw %r9, (UC_FLAGS)(reg)
+
+#define SETC \
+ li %r9, 1 ; \
+ slwi %r9, %r9, _UC_USER_BIT ; \
+ lwz %r3, (UC_FLAGS)(%r4) ; \
+ and. %r9, %r3, %r9 ; \
+ beq 2f ; \
+ ; \
+ lwz %r1, (_REG_R0 + 1*4)(%r4) ; \
+ lmw %r14, (_REG_R0 + 14*4)(%r4) ; \
+ lwz %r0, _REG_LR(%r4) ; \
+ mtlr %r0 ; \
+ lwz %r0, _REG_PC(%r4) ; \
+ mtctr %r0 ; \
+ ; \
+ andi. %r5, %r3, _UC_FPU ; \
+ beq 1f ; \
+ ; \
+ lwz %r5, (_UC_FPVALID)(%r4) ; \
+ or. %r5, %r5, %r5 ; \
+ beq 1f ; \
+ ; \
+ lfd %r14, (_REG_FP0 + 14*8)(%r4) ; \
+ lfd %r15, (_REG_FP0 + 15*8)(%r4) ; \
+ lfd %r16, (_REG_FP0 + 16*8)(%r4) ; \
+ lfd %r17, (_REG_FP0 + 17*8)(%r4) ; \
+ lfd %r18, (_REG_FP0 + 18*8)(%r4) ; \
+ lfd %r19, (_REG_FP0 + 19*8)(%r4) ; \
+ lfd %r20, (_REG_FP0 + 20*8)(%r4) ; \
+ lfd %r21, (_REG_FP0 + 21*8)(%r4) ; \
+ lfd %r22, (_REG_FP0 + 22*8)(%r4) ; \
+ lfd %r23, (_REG_FP0 + 23*8)(%r4) ; \
+ lfd %r24, (_REG_FP0 + 24*8)(%r4) ; \
+ lfd %r25, (_REG_FP0 + 25*8)(%r4) ; \
+ lfd %r26, (_REG_FP0 + 26*8)(%r4) ; \
+ lfd %r27, (_REG_FP0 + 27*8)(%r4) ; \
+ lfd %r28, (_REG_FP0 + 28*8)(%r4) ; \
+ lfd %r29, (_REG_FP0 + 29*8)(%r4) ; \
+ lfd %r30, (_REG_FP0 + 30*8)(%r4) ; \
+ lfd %r31, (_REG_FP0 + 31*8)(%r4) ; \
+ lfd %r0, (_REG_FPSCR - 4)(%r4) ; \
+ mtfsf 0xff, %r0 ; \
+ ; \
+1: ; \
+ lwz %r0, _REG_CR(%r4) ; \
+ mtcr %r0 ; \
+ bctr ; \
+2: ; \
+ mr %r3, %r4 ; \
+ b PIC_PLT(_C_LABEL(setcontext)) ; \
+ /* NOTREACHED */
+
+ENTRY(_getcontext_u)
+ GETC(%r3)
+ xor %r3,%r3,%r3
+ blr
+
+ENTRY(_setcontext_u)
+ mr %r4,%r3
+ SETC
+
+ENTRY(_swapcontext_u)
+ GETC(%r3)
+ SETC
diff --git a/lib/libpthread/arch/powerpc/genassym.cf b/lib/libpthread/arch/powerpc/genassym.cf
new file mode 100644
index 00000000000..f8384dcd6fe
--- /dev/null
+++ b/lib/libpthread/arch/powerpc/genassym.cf
@@ -0,0 +1,74 @@
+# $NetBSD: genassym.cf,v 1.2 2003/01/18 10:34:20 thorpej Exp $
+
+# Copyright (c) 2001 Wasabi Systems, Inc.
+# All rights reserved.
+#
+# Written by Allen Briggs for Wasabi Systems, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed for the NetBSD Project by
+# Wasabi Systems, Inc.
+# 4. The name of Wasabi Systems, Inc. may not be used to endorse
+# or promote products derived from this software without specific prior
+# written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+include <ucontext.h>
+include <sys/queue.h>
+include "pthread.h"
+include "pthread_int.h"
+include "pthread_md.h"
+
+# for _context_u.S
+define UC_FLAGS offsetof(ucontext_t, uc_flags)
+define _UC_FPVALID offsetof(ucontext_t, uc_mcontext.__fpregs.__fpu_valid)
+define _REG_R0 offsetof(ucontext_t, uc_mcontext.__gregs[0])
+define _REG_CR offsetof(ucontext_t, uc_mcontext.__gregs[32])
+define _REG_LR offsetof(ucontext_t, uc_mcontext.__gregs[33])
+define _REG_PC offsetof(ucontext_t, uc_mcontext.__gregs[34])
+define _REG_MSR offsetof(ucontext_t, uc_mcontext.__gregs[35])
+define _REG_CTR offsetof(ucontext_t, uc_mcontext.__gregs[36])
+define _REG_XER offsetof(ucontext_t, uc_mcontext.__gregs[37])
+define _REG_FP0 offsetof(ucontext_t, uc_mcontext.__fpregs.__fpu_regs[0])
+define _REG_FPSCR offsetof(ucontext_t, uc_mcontext.__fpregs.__fpu_fpscr)
+
+# uc_flags
+define _UC_CPU _UC_CPU
+define _UC_FPU _UC_FPU
+define _UC_USER_BIT _UC_USER_BIT
+
+#========================================================================
+# for pthread_switch.S
+define PT_UC offsetof(struct pthread_st, pt_uc)
+define PT_NEXT offsetof(struct pthread_st, pt_next)
+define PT_SPINLOCKS offsetof(struct pthread_st, pt_spinlocks)
+define PT_SWITCHTO offsetof(struct pthread_st, pt_switchto)
+define PT_SWITCHTOUC offsetof(struct pthread_st, pt_switchtouc)
+define PT_STATE offsetof(struct pthread_st, pt_state)
+define PT_HELDLOCK offsetof(struct pthread_st, pt_heldlock)
+define PT_SLEEPUC offsetof(struct pthread_st, pt_sleepuc)
+
+define PT_STATE_RECYCLABLE PT_STATE_RECYCLABLE
+define CONTEXTSIZE sizeof(ucontext_t)
+define STACKSPACE STACKSPACE
diff --git a/lib/libpthread/arch/powerpc/pthread_md.h b/lib/libpthread/arch/powerpc/pthread_md.h
new file mode 100644
index 00000000000..917bf1b14be
--- /dev/null
+++ b/lib/libpthread/arch/powerpc/pthread_md.h
@@ -0,0 +1,106 @@
+/* $NetBSD: pthread_md.h,v 1.2 2003/01/18 10:34:21 thorpej Exp $ */
+
+/*
+ * Copyright (c) 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Allen Briggs for Wasabi Systems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the NetBSD Project by
+ * Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _LIB_PTHREAD_POWERPC_MD_H
+#define _LIB_PTHREAD_POWERPC_MD_H
+
+static __inline long
+pthread__sp(void)
+{
+ long ret;
+
+ __asm("mr %0,1" : "=r" (ret));
+
+ return ret;
+}
+
+#define pthread__uc_sp(ucp) ((ucp)->uc_mcontext.__gregs[1])
+#define pthread__uc_pc(ucp) ((ucp)->uc_mcontext.__gregs[34])
+
+/*
+ * Set initial, sane values for registers whose values aren't just
+ * "don't care".
+ * 0xd032 is PSL_USERSET from arch/powerpc/include/psl.h
+ */
+#define _INITCONTEXT_U_MD(ucp) \
+ (ucp)->uc_mcontext.__gregs[35] = 0xd032;
+
+/*
+ * Usable stack space below the ucontext_t.
+ * For a good time, see comments in pthread_switch.S and
+ * ../i386/pthread_switch.S about STACK_SWITCH.
+ */
+#define STACKSPACE 16 /* room for 4 integer values */
+
+/*
+ * Conversions between struct reg and struct mcontext. Used by
+ * libpthread_dbg.
+ */
+
+#define PTHREAD_UCONTEXT_TO_REG(reg, uc) do { \
+ memcpy((reg)->fixreg, (uc)->uc_mcontext.__gregs, 32 * 4); \
+ (reg)->cr = (uc)->uc_mcontext.__gregs[32]; \
+ (reg)->lr = (uc)->uc_mcontext.__gregs[33]; \
+ (reg)->pc = (uc)->uc_mcontext.__gregs[34]; \
+ (reg)->ctr = (uc)->uc_mcontext.__gregs[36]; \
+ (reg)->xer = (uc)->uc_mcontext.__gregs[37]; \
+ } while (/*CONSTCOND*/0)
+
+#define PTHREAD_REG_TO_UCONTEXT(uc, reg) do { \
+ memcpy((uc)->uc_mcontext.__gregs, (reg)->fixreg, 32 * 4); \
+ (uc)->uc_mcontext.__gregs[32] = (reg)->cr; \
+ (uc)->uc_mcontext.__gregs[33] = (reg)->lr; \
+ (uc)->uc_mcontext.__gregs[34] = (reg)->pc; \
+ (uc)->uc_mcontext.__gregs[36] = (reg)->ctr; \
+ (uc)->uc_mcontext.__gregs[37] = (reg)->xer; \
+ (uc)->uc_flags = ((uc)->uc_flags | _UC_CPU) & ~_UC_USER; \
+ } while (/*CONSTCOND*/0)
+
+#define PTHREAD_UCONTEXT_TO_FPREG(freg, uc) do { \
+ memcpy((freg)->fpreg, (uc)->uc_mcontext.__fpregs.__fpu_regs, \
+ 32 * 4); \
+ (freg)->fpscr = (uc)->uc_mcontext.__fpregs.__fpu_fpscr; \
+ } while (/*CONSTCOND*/0)
+
+#define PTHREAD_FPREG_TO_UCONTEXT(uc, freg) do { \
+ memcpy((uc)->uc_mcontext.__fpregs.__fpu_regs, (freg)->fpreg, \
+ 32 * 4); \
+ (uc)->uc_mcontext.__fpregs.__fpu_fpscr = (freg)->fpscr; \
+ (uc)->uc_flags = ((uc)->uc_flags | _UC_FPU) & ~_UC_USER; \
+ } while (/*CONSTCOND*/0)
+
+#endif /* _LIB_PTHREAD_POWERPC_MD_H */
diff --git a/lib/libpthread/arch/powerpc/pthread_switch.S b/lib/libpthread/arch/powerpc/pthread_switch.S
new file mode 100644
index 00000000000..da983d9f87c
--- /dev/null
+++ b/lib/libpthread/arch/powerpc/pthread_switch.S
@@ -0,0 +1,353 @@
+/* $NetBSD: pthread_switch.S,v 1.2 2003/01/18 10:34:21 thorpej Exp $ */
+
+/*
+ * Copyright (c) 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Allen Briggs for Wasabi Systems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the NetBSD Project by
+ * Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <machine/asm.h>
+#include "assym.h"
+
+#define NOTREACHED
+
+/*
+ * PowerPC ABI says stack pointer must be 16-byte aligned.
+ */
+#define RND_CTXSIZE ((CONTEXTSIZE + 15) & 0xfffffff0)
+
+/*
+ * Define:
+ * void pthread__switch(pthread_t self, pthread_t next)
+ * void pthread__upcall_switch(pthread_t self, pthread_t next)
+ * void pthread__locked_switch(pthread_t self, pthread_t next,
+ * pt_spin_t *lock)
+ */
+
+
+/*
+ * Evil STACK_SWITCH()
+ * See comments in ../i386/pthread_switch.S.
+ */
+#define STACK_SWITCH(pt,tmp) \
+ lwz tmp, PT_UC(pt) ; \
+ la %r1, -STACKSPACE(tmp)
+
+
+/*
+ * void
+ * pthread__switch(pthread_t self (%r3), pthread_t next (%r4))
+ *
+ * Plain switch that doesn't do any special checking or handle spin-
+ * preemption. It isn't used much by normal code, actually; its main
+ * purpose is to be a basic switch engine when the MI code is already
+ * dealing with spin-preemption or other gunk.
+ *
+ * What we do here is allocate the ucontext for the 'self' thread on its
+ * stack, saving the pointer in self's pthread structure, then swapping
+ * context to 'next', effectively deallocating next's context on the way
+ * out.
+ */
+ENTRY(pthread__switch)
+#ifdef PIC
+ mflr %r10
+ bl _GLOBAL_OFFSET_TABLE_@local-4
+ mflr %r9
+ mtlr %r10
+#endif
+ stwu %r1, -(RND_CTXSIZE + 48)(%r1) /* alloc stack space */
+ mflr %r0
+ stw %r31,(RND_CTXSIZE + 44)(%r1) /* saved %r31 */
+#ifdef PIC
+ stw %r9, (RND_CTXSIZE + 40)(%r1)
+#endif
+ stw %r4, (RND_CTXSIZE + 32)(%r1) /* next */
+ stw %r3, (RND_CTXSIZE + 28)(%r1) /* self */
+ stw %r0, (RND_CTXSIZE + 52)(%r1) /* Save return address */
+ addi %r31, %r1, 16 /* %r31 = ucontext */
+
+ /* Get the current context */
+ mr %r3, %r31
+ bl PIC_PLT(_C_LABEL(_getcontext_u))
+ lwz %r3, (RND_CTXSIZE + 28)(%r1)
+ lwz %r4, (RND_CTXSIZE + 32)(%r1)
+#ifdef PIC
+ lwz %r9, (RND_CTXSIZE + 40)(%r1)
+#endif
+
+ /*
+ * Exit the context to make it continue at switch_return instead of
+ * here.
+ */
+#ifdef PIC
+ lwz %r6, switch_return@got(%r9)
+#else
+ lis %r6, switch_return@ha
+ addi %r6, %r6, switch_return@l
+#endif
+ stw %r6, (_REG_PC + 16)(%r1)
+
+ STACK_SWITCH(%r4, %r7)
+
+ stw %r31, PT_UC(%r3)
+
+2: mr %r3, %r7
+ b PIC_PLT(_C_LABEL(_setcontext_u))
+ NOTREACHED
+
+switch_return:
+ lwz %r31, (RND_CTXSIZE + 44)(%r1)
+ addi %r1, %r1, (RND_CTXSIZE + 48)
+ lwz %r0, 4(%r1)
+ mtlr %r0
+ blr
+
+
+
+pthread__switch_away:
+ STACK_SWITCH(%r4,%r6)
+
+ or. %r5, %r5, %r5
+ beq 1f
+ lwz %r7, PT_SPINLOCKS(%r3)
+ addi %r7, %r7, -1
+ stw %r7, PT_SPINLOCKS(%r3)
+
+1: mr %r3, %r6
+ b PIC_PLT(_C_LABEL(_setcontext_u))
+ NOTREACHED
+
+/*
+ * void
+ * pthread__upcall_switch(pthread_t self (%r3), pthread_t next (%r4))
+ */
+ENTRY(pthread__upcall_switch)
+ lwz %r6, PT_SPINLOCKS(%r4)
+ addi %r6, %r6, 1
+ stw %r6, PT_SPINLOCKS(%r4)
+
+ STACK_SWITCH(%r4,%r5)
+
+ lwz %r7, PT_NEXT(%r3)
+ or. %r7, %r7, %r7
+ beq 1f
+
+ stw %r4, PT_SWITCHTO(%r3)
+ stw %r5, PT_SWITCHTOUC(%r3)
+ li %r5, PT_STATE_RECYCLABLE
+ stw %r5, PT_STATE(%r3)
+ mr %r3, %r4
+ mr %r4, %r7
+ li %r5, 1
+ b pthread__switch_away
+ NOTREACHED
+
+1: mr %r14, %r3
+ mr %r15, %r4
+ mr %r16, %r5
+ bl PIC_PLT(_C_LABEL(pthread__sa_recycle))
+ mr %r3, %r14
+ mr %r4, %r15
+ mr %r5, %r16
+
+ lwz %r6, PT_SPINLOCKS(%r4)
+ addi %r6, %r6, -1
+ stw %r6, PT_SPINLOCKS(%r4)
+
+ lwz %r7, PT_NEXT(%r4)
+ or. %r7, %r7, %r7
+ beq 2f
+
+ stw %r4, PT_SWITCHTO(%r4)
+ stw %r5, PT_SWITCHTOUC(%r4)
+ mr %r3, %r4
+ mr %r4, %r7
+ li %r5, 0
+ b pthread__switch_away
+ NOTREACHED
+
+2: mr %r3, %r16
+ b PIC_PLT(_C_LABEL(_setcontext_u))
+ NOTREACHED
+
+/*
+ * void
+ * pthread__locked_switch(pthread_t self (%r3), pthread_t next (%r4),
+ * pt_spin_t *lock (%r5))
+ *
+ * Stack is:
+ * high addr -- return addr ( 4 bytes)
+ * %r1@call caller saved %r1 ( 4 bytes)
+ * any padding to make %r1 a multiple of 16 ... *
+ * saved %r31 ( 4 bytes) *
+ * saved %r9 ( 4 bytes) *
+ * saved %r5 ( 4 bytes) *
+ * saved %r4 ( 4 bytes) *
+ * saved %r3 ( 4 bytes) *
+ * context (RND_CTXSIZE bytes) *
+ * padding to 16 bytes ( 20 bytes) *
+ * space for callee ra ( 4 bytes) *
+ * low addr p__l_s saved %r1 ( 4 bytes) *
+ *
+ * STACKSPACE is the space between the bottom of the stack and
+ * the ucontext on the stack. i.e., 8, but we want to keep the stack
+ * rounded to a multiple of 16, so it's really 16.
+ */
+
+ .type locked_return,@function
+
+ENTRY(pthread__locked_switch)
+#ifdef PIC
+ mflr %r10
+ bl _GLOBAL_OFFSET_TABLE_@local-4
+ mflr %r9
+ mtlr %r10
+#endif
+ stwu %r1, -(RND_CTXSIZE + 48)(%r1) /* alloc stack space */
+ mflr %r0
+ stw %r31,(RND_CTXSIZE + 44)(%r1) /* saved %r31 */
+#ifdef PIC
+ stw %r9, (RND_CTXSIZE + 40)(%r1)
+#endif
+ stw %r5, (RND_CTXSIZE + 36)(%r1) /* lock */
+ stw %r4, (RND_CTXSIZE + 32)(%r1) /* next */
+ stw %r3, (RND_CTXSIZE + 28)(%r1) /* self */
+ stw %r0, (RND_CTXSIZE + 52)(%r1) /* Save return address */
+ addi %r31, %r1, 16 /* %r31 = ucontext */
+
+ /* increment spinlock to make sure that next gets continued */
+ lwz %r6, PT_SPINLOCKS(%r4)
+ addi %r6, %r6, 1
+ stw %r6, PT_SPINLOCKS(%r4)
+
+ /* Get the current context */
+ mr %r3, %r31
+ bl PIC_PLT(_C_LABEL(_getcontext_u))
+ lwz %r3, (RND_CTXSIZE + 28)(%r1)
+ lwz %r4, (RND_CTXSIZE + 32)(%r1)
+ lwz %r5, (RND_CTXSIZE + 36)(%r1)
+#ifdef PIC
+ lwz %r9, (RND_CTXSIZE + 40)(%r1)
+#endif
+
+ /*
+ * Exit the context to make it continue at locked_return instead of
+ * here.
+ */
+#ifdef PIC
+ lwz %r6, locked_return@got(%r9)
+#else
+ lis %r6, locked_return@ha
+ addi %r6, %r6, locked_return@l
+#endif
+ stw %r6, (_REG_PC + 16)(%r1)
+
+ STACK_SWITCH(%r4, %r7)
+
+ stw %r31, PT_UC(%r3)
+
+ /* Check if the switcher was preempted and continued to here. */
+ lwz %r8, PT_NEXT(%r3)
+ or. %r8, %r8, %r8
+ beq 1f
+
+ /*
+ * Yes, it was. Stash the thread we were going to switch to,
+ * the lock the original thread was holding, and switch to the
+ * next thread in the continuation chain. Mark the fact that
+ * this was a locked switch, and so the thread does not need to
+ * be put on a run queue.
+ * Don't release the lock. It's possible that if we do so,
+ * PT_SWITCHTO will be stomped by another switch_lock and preemption.
+ */
+ stw %r4, PT_SWITCHTO(%r3)
+ stw %r7, PT_SWITCHTOUC(%r3)
+ stw %r5, PT_HELDLOCK(%r3)
+ lwz %r6, PT_SPINLOCKS(%r3)
+ addi %r6, %r6, -1
+ stw %r6, PT_SPINLOCKS(%r3)
+
+ /*
+ * Save the context we previously stored in PT_UC(%r3);
+ * that was overwritten when we were preempted and continued,
+ * so we need to put it somewhere.
+ */
+ stw %r31, PT_SLEEPUC(%r3)
+
+ mr %r3, %r4
+ mr %r4, %r8
+ li %r5, 1
+ b pthread__switch_away
+ NOTREACHED
+
+ /* No locked old-preemption */
+1: /*
+ * We've moved to the new stack, and the old context has been
+ * saved. The queue lock can be released.
+ */
+ /* Reduce the lock count... */
+ lwz %r6, PT_SPINLOCKS(%r3)
+ addi %r6, %r6, -1
+ stw %r6, PT_SPINLOCKS(%r3)
+ /* ... actually release the lock ... */
+ sync
+ xor %r9,%r9,%r9
+ stw %r9, 0(%r5)
+ /* ... and remove the fake lock */
+ lwz %r6, PT_SPINLOCKS(%r4)
+ addi %r6, %r6, -1
+ stw %r6, PT_SPINLOCKS(%r4)
+
+ /* Check to see if we were preempted while holding the fake lock */
+ lwz %r8, PT_NEXT(%r4)
+ or. %r8, %r8, %r8
+ beq 2f
+
+ /* Yes, we were. Go to the next element in the chain. */
+ stw %r4, PT_SWITCHTO(%r4)
+ stw %r7, PT_SWITCHTOUC(%r4)
+ mr %r3, %r4
+ mr %r4, %r8
+ li %r5, 0
+ b pthread__switch_away
+ NOTREACHED
+
+2: mr %r3, %r7
+ b PIC_PLT(_C_LABEL(_setcontext_u))
+ NOTREACHED
+
+locked_return:
+ lwz %r31, (RND_CTXSIZE + 44)(%r1)
+ addi %r1, %r1, (RND_CTXSIZE + 48)
+ lwz %r0, 4(%r1)
+ mtlr %r0
+ blr
diff --git a/lib/libpthread/arch/sparc/_context_u.S b/lib/libpthread/arch/sparc/_context_u.S
new file mode 100644
index 00000000000..e0af4588924
--- /dev/null
+++ b/lib/libpthread/arch/sparc/_context_u.S
@@ -0,0 +1,99 @@
+/* $NetBSD: _context_u.S,v 1.2 2003/01/18 10:34:21 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <machine/asm.h>
+#include <machine/trap.h>
+#include "assym.h"
+
+#define FLUSHW t T_FLUSHWIN
+#define NOTREACHED t T_DIV0
+
+ .global _C_LABEL(setcontext)
+
+ /*
+ * For voluntary context switches save/restore take care
+ * of caller's window, the only registers which should be
+ * saved are frame pointer and return address
+ */
+
+#define GETC(uc) \
+ st %i6, [uc + UC_GREGS + _REG_O6 * 4] ; \
+ add %i7, 8, %l0 ; \
+ st %l0, [uc + UC_GREGS + _REG_PC * 4] ; \
+ /* save nPC too, so that setcontext(2) works */ \
+ add %i7, 12, %l0 ; \
+ st %l0, [uc + UC_GREGS + _REG_nPC * 4] ; \
+ \
+ set _UC_CPU | _UC_USER, %l1 ; \
+ st %l1, [uc + UC_FLAGS]
+
+#define SETC(uc) \
+ ld [uc + UC_FLAGS], %l0 ; \
+ set _UC_USER, %l1 ; \
+ btst %l1, %l0 ; \
+ bz 1f ; \
+ nop ; \
+ \
+ ld [uc + UC_GREGS + _REG_PC * 4], %i7 ; \
+ ld [uc + UC_GREGS + _REG_O6 * 4], %i6 ; \
+ ; \
+ jmp %i7 ; \
+ restore ; \
+ NOTREACHED ; \
+ \
+1: call _C_LABEL(setcontext) ; \
+ mov uc, %o0
+
+
+ENTRY(_getcontext_u)
+ save %sp, -CCFSZ, %sp
+ FLUSHW ! XXX
+ GETC(%i0)
+ ret
+ restore %g0, 0, %o0 ! return (0);
+
+ENTRY(_setcontext_u)
+ save %sp, -CCFSZ, %sp
+ FLUSHW
+ SETC(%i0)
+ NOTREACHED
+
+ENTRY(_swapcontext_u)
+ save %sp, -CCFSZ, %sp
+ FLUSHW
+ GETC(%i0)
+ SETC(%i1)
+ NOTREACHED
+
diff --git a/lib/libpthread/arch/sparc/genassym.cf b/lib/libpthread/arch/sparc/genassym.cf
new file mode 100644
index 00000000000..c2ede1b8a8f
--- /dev/null
+++ b/lib/libpthread/arch/sparc/genassym.cf
@@ -0,0 +1,56 @@
+# $NetBSD: genassym.cf,v 1.2 2003/01/18 10:34:21 thorpej Exp $
+
+
+include <ucontext.h>
+include <sys/queue.h>
+include <machine/reg.h>
+include <machine/mcontext.h>
+include <machine/frame.h>
+include "pthread.h"
+include "pthread_int.h"
+include "pthread_md.h"
+
+define PT_NEXT offsetof(struct pthread_st, pt_next)
+define PT_STATE offsetof(struct pthread_st, pt_state)
+define PT_SWITCHTO offsetof(struct pthread_st, pt_switchto)
+define PT_SWITCHTOUC offsetof(struct pthread_st, pt_switchtouc)
+define PT_SLEEPUC offsetof(struct pthread_st, pt_sleepuc)
+define PT_SPINLOCKS offsetof(struct pthread_st, pt_spinlocks)
+define PT_HELDLOCK offsetof(struct pthread_st, pt_heldlock)
+define PT_UC offsetof(struct pthread_st, pt_uc)
+define CONTEXTSIZE sizeof(ucontext_t)
+define UC_FLAGS offsetof(ucontext_t, uc_flags)
+define UC_GREGS offsetof(ucontext_t, uc_mcontext.__gregs)
+define UC_FPREGS offsetof(ucontext_t, uc_mcontext.__fpregs)
+define PT_STATE_RECYCLABLE PT_STATE_RECYCLABLE
+define STACKSPACE STACKSPACE
+define _UC_USER_BIT _UC_USER_BIT
+define _UC_USER _UC_USER
+define _UC_CPU _UC_CPU
+define _UC_FPU _UC_FPU
+
+define CCFSZ CCFSZ
+
+define FPRS_FEF FPRS_FEF
+
+define _REG_CCR _REG_CCR
+define _REG_PC _REG_PC
+define _REG_nPC _REG_nPC
+define _REG_Y _REG_Y
+define _REG_G1 _REG_G1
+define _REG_G2 _REG_G2
+define _REG_G3 _REG_G3
+define _REG_G4 _REG_G4
+define _REG_G5 _REG_G5
+define _REG_G6 _REG_G6
+define _REG_G7 _REG_G7
+define _REG_O0 _REG_O0
+define _REG_O1 _REG_O1
+define _REG_O2 _REG_O2
+define _REG_O3 _REG_O3
+define _REG_O4 _REG_O4
+define _REG_O5 _REG_O5
+define _REG_O6 _REG_O6
+define _REG_O7 _REG_O7
+define _REG_ASI _REG_ASI
+define _REG_FPRS _REG_FPRS
diff --git a/lib/libpthread/arch/sparc/pthread_md.h b/lib/libpthread/arch/sparc/pthread_md.h
new file mode 100644
index 00000000000..8ed8affb1df
--- /dev/null
+++ b/lib/libpthread/arch/sparc/pthread_md.h
@@ -0,0 +1,83 @@
+/* $NetBSD: pthread_md.h,v 1.2 2003/01/18 10:34:21 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _LIB_PTHREAD_SPARC_MD_H
+#define _LIB_PTHREAD_SPARC_MD_H
+
+/*
+ * pthread__sp used for identifying thread
+ */
+static __inline long
+pthread__sp(void)
+{
+ long ret;
+
+ __asm("mov %%sp, %0" : "=r" (ret));
+
+ return ret;
+}
+
+#define pthread__uc_sp(ucp) ((ucp)->uc_mcontext.__gregs[_REG_O6])
+#define pthread__uc_pc(ucp) ((ucp)->uc_mcontext.__gregs[_REG_PC])
+
+#define STACKSPACE 96 /* min stack frame XXX */
+
+/*
+ * Conversions between struct reg and struct mcontext. Used by
+ * libpthread_dbg.
+ * XXX macros
+ */
+
+#define PTHREAD_UCONTEXT_TO_REG(reg, uc) do { \
+ memcpy(&(reg)->r_global, &(uc)->uc_mcontext.__gregs, sizeof(__gregset_t));\
+ } while (/*CONSTCOND*/0)
+
+#define PTHREAD_REG_TO_UCONTEXT(uc, reg) do { \
+ memcpy(&(uc)->uc_mcontext.__gregs, &(reg)->r_global, sizeof(__gregset_t));\
+ (uc)->uc_flags = ((uc)->uc_flags | _UC_CPU) & ~_UC_USER; \
+ } while (/*CONSTCOND*/0)
+
+#define PTHREAD_UCONTEXT_TO_FPREG(freg, uc) \
+ memcpy((freg), &(uc)->uc_mcontext.__fpregs, \
+ sizeof(struct fpreg)) \
+
+#define PTHREAD_FPREG_TO_UCONTEXT(uc, freg) do { \
+ memcpy(&(uc)->uc_mcontext.__fpregs, (freg), \
+ sizeof(struct fpreg)); \
+ (uc)->uc_flags = ((uc)->uc_flags | _UC_FPU) & ~_UC_USER; \
+ } while (/*CONSTCOND*/0)
+
+#endif /* _LIB_PTHREAD_SPARC_MD_H */
+
diff --git a/lib/libpthread/arch/sparc/pthread_switch.S b/lib/libpthread/arch/sparc/pthread_switch.S
new file mode 100644
index 00000000000..e79b7ba693c
--- /dev/null
+++ b/lib/libpthread/arch/sparc/pthread_switch.S
@@ -0,0 +1,301 @@
+/* $NetBSD: pthread_switch.S,v 1.2 2003/01/18 10:34:21 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This file implements low-level routines that are exported to
+ * the machine-independent parts of the thread library. The routines are:
+ *
+ * void pthread__switch(pthread_t self, pthread_t next);
+ * void pthread__upcall_switch(pthread_t self, pthread_t next);
+ * void pthread__locked_switch(pthread_t self, pthread_t next,
+ * pt_spin_t *lock);
+ *
+ * as well as some utility code used by these routines.
+ */
+
+#include <machine/asm.h>
+#include <machine/trap.h>
+#include "assym.h"
+
+#define STACK_ALIGN(x) (((x) + 0x7) & ~0x7)
+
+#define NOTREACHED t T_DIV0
+
+
+/*
+ * void pthread__switch(pthread_t self, pthread_t next);
+ *
+ * Plain switch that doesn't do any special checking or handle
+ * spin-preemption. It isn't used much by normal code, actually; it's
+ * main purpose is to be a basic switch engine when the MI code is
+ * already dealing with spin-preemption or other gunk.
+ */
+ENTRY(pthread__switch)
+ /*
+ * XXX Get space on stack for return address and ucontext,
+ * save return address, put ucontext address into pthread
+ * then swapcontext
+ */
+ save %sp, -STACK_ALIGN(CCFSZ + CONTEXTSIZE), %sp
+
+ add %fp, -CONTEXTSIZE, %o0
+ st %o0, [%i0 + PT_UC]
+ call _C_LABEL(_swapcontext_u)
+ ld [%i1 + PT_UC], %o1
+
+ ret
+ restore
+
+
+/* *** WARNING ***
+ * STACK_SWITCH is more subtle than it looks. Please go read the extended
+ * comment in the i386 pthread_switch.S file.
+ */
+
+/*
+ * pt - register which contains *pthread, which stack will be used
+ * tmp - temporary register which will have *ucontext_t for that pthread
+ */
+#define STACK_SWITCH(pt, tmp) \
+ ld [pt + PT_UC], tmp ; \
+ add tmp, -STACK_ALIGN(STACKSPACE + CONTEXTSIZE), %sp
+
+
+/*
+ * Helper switch code used by pthread__locked_switch() and
+ * pthread__upcall_switch() when they discover spinlock preemption.
+ */
+
+/*
+ * pthread__switch_away
+ * %i0 who's lock to release
+ * %i1 thread to switch to
+ * %i2 flag to clear lock
+ */
+
+ENTRY(pthread__switch_away)
+
+ STACK_SWITCH(%i1, %l2)
+
+ /* If we're invoked from the switch-to-next provisions of
+ * pthread__locked_switch or pthread__upcall_switch, there may
+ * be a fake spinlock-count set. If so, they will set i2 to
+ * let us know, and we decrement it now that we're no longer
+ * using the old stack.
+ */
+ tst %i2
+ bz 1f
+ nop
+ ld [%i0 + PT_SPINLOCKS], %l0
+ dec %l0
+ st %l0, [%i0 + PT_SPINLOCKS]
+
+1: call _C_LABEL(_setcontext_u)
+ mov %l2, %o0
+ NOTREACHED
+
+
+/*
+ * void pthread__upcall_switch(pthread_t self, pthread_t next);
+ *
+ * Quit an upcall, recycle it, and jump to the selected thread.
+ */
+ENTRY(pthread__upcall_switch)
+ save %sp, -CCFSZ, %sp
+
+ /* Create a "fake" lock count so that this code will be continued */
+ ld [%i1 + PT_SPINLOCKS], %l0
+ inc %l0
+ st %l0, [%i1 + PT_SPINLOCKS]
+
+ /*
+ * switch to next thread stack so we could recycle self's one
+ */
+ STACK_SWITCH(%i1, %l2)
+
+ /* Check if the upcall code was preempted and continued to here. */
+ ld [%i0 + PT_NEXT], %l0
+ tst %l0
+ bz 1f
+ nop
+
+ /* Yes, it was. Stash the thread we were going to switch to,
+ * and switch to the next thread in the continuation chain.
+ */
+ st %i1, [%i0 + PT_SWITCHTO]
+ st %l2, [%i0 + PT_SWITCHTOUC]
+ mov PT_STATE_RECYCLABLE, %l1
+ st %l1, [%i0 + PT_STATE]
+ mov %i1, %i0
+ mov %l0, %i1
+ ba pthread__switch_away
+ mov 1, %i2
+
+ /* No old-upcall-preemption */
+1: mov %i0, %o0
+ call _C_LABEL(pthread__sa_recycle)
+ mov %i1, %o1
+
+ /* We can now release the fake lock. */
+ ld [%i1 + PT_SPINLOCKS], %l0
+ dec %l0
+ st %l0, [%i1 + PT_SPINLOCKS]
+
+ /* Check if we were preempted and continued while faking the lock. */
+ ld [%i1 + PT_NEXT], %l0
+ tst %l0
+ bz 2f
+ nop
+
+ /* Yes, we were. Stash the to-be-switched-to context in our thread
+ * structure and go to the next link in the chain.
+ */
+ st %i1, [%i1 + PT_SWITCHTO]
+ st %l2, [%i1 + PT_SWITCHTOUC]
+ mov %i1, %i0
+ mov %l0, %i1
+ ba pthread__switch_away
+ mov 0, %i2
+
+ /* No new-upcall-preemption */
+2: call _C_LABEL(_setcontext_u)
+ ld [%i1 + PT_UC], %o0
+ NOTREACHED
+
+
+/*
+ * void pthread__locked_switch(pthread_t self, pthread_t next,
+ * pt_spin_t *lock);
+ *
+ * Switch away from a thread that is holding a lock on a queue (to
+ * prevent being removed from the queue before being switched away).
+ */
+ENTRY(pthread__locked_switch)
+ save %sp, -STACK_ALIGN(CCFSZ + CONTEXTSIZE), %sp
+
+ /* Make sure we get continuted */
+ ld [%i1 + PT_SPINLOCKS], %l0
+ inc %l0
+ st %l0, [%i1 + PT_SPINLOCKS]
+
+ /* Get the current context */
+ add %fp, -CONTEXTSIZE, %l3
+ st %l3, [%i0 + PT_UC]
+ call _C_LABEL(_getcontext_u)
+ mov %l3, %o0
+
+ /* Edit the context to make it continue below, rather than here */
+#ifdef PIC
+ PICCY_SET(locked_return_point, %l1, %l5)
+#else
+ set locked_return_point, %l1
+#endif
+ st %l1, [%l3 + UC_GREGS + _REG_PC * 4]
+
+ STACK_SWITCH(%i1, %l2)
+
+ /* Check if the switcher was preempted and continued to here. */
+ ld [%i0 + PT_NEXT], %l4
+ tst %l4
+ bz 1f
+ nop
+
+ /* Yes, it was. Stash the thread we were going to switch to,
+ * the lock the original thread was holding,
+ * and switch to the next thread in the continuation chain.
+ * Mark the fact that this was a locked switch, and so the
+ * thread does not need to be put on a run queue.
+ * Don't release the lock. It's possible that if we do so,
+ * PT_SWITCHTO will be stomped by another switch_lock and
+ * preemption.
+ */
+ st %i1, [%i0 + PT_SWITCHTO]
+ st %l2, [%i0 + PT_SWITCHTOUC]
+ st %i2, [%i0 + PT_HELDLOCK]
+ ld [%i0 + PT_SPINLOCKS], %l0
+ dec %l0
+ st %l0, [%i0 + PT_SPINLOCKS]
+
+ /* Save the context we previously stored in PT_UC(a0);
+ * that was overwritten when we were preempted and continued,
+ * so we need to put it somewhere.
+ */
+ st %l3, [%i0 + PT_SLEEPUC]
+
+ mov %i1, %i0
+ mov %l4, %i1
+ ba pthread__switch_away
+ mov 1, %i2
+
+ /* No locked old-preemption */
+1: /* We've moved to the new stack, and the old context has been
+ * saved. The queue lock can be released.
+ */
+ /* Reduce the lock count... */
+ ld [%i0 + PT_SPINLOCKS], %l0
+ dec %l0
+ st %l0, [%i0 + PT_SPINLOCKS]
+
+ /* ... actually release the lock.. */
+! membar #Sync
+
+ clr [%i2]
+ /* .. and remove the fake lock */
+ ld [%i1 + PT_SPINLOCKS], %l0
+ dec %l0
+ st %l0, [%i1 + PT_SPINLOCKS]
+
+ /* Check if we were preempted while holding the fake lock. */
+ ld [%i1 + PT_NEXT], %l0
+ tst %l0
+ bz 2f
+ nop
+
+ /* Yes, we were. Go to the next element in the chain. */
+ st %i1, [%i1 + PT_SWITCHTO]
+ st %l2, [%i1 + PT_SWITCHTOUC]
+ mov %i1, %i0
+ mov %l0, %i1
+ ba pthread__switch_away
+ mov 0, %i2
+
+2: /* %l2 points to next thread's ucontext */
+ call _C_LABEL(_setcontext_u)
+ mov %l2, %o0
+ NOTREACHED
+
+locked_return_point:
+ ret
+ restore
diff --git a/lib/libpthread/arch/sparc64/_context_u.S b/lib/libpthread/arch/sparc64/_context_u.S
new file mode 100644
index 00000000000..fe4ff613cc6
--- /dev/null
+++ b/lib/libpthread/arch/sparc64/_context_u.S
@@ -0,0 +1,97 @@
+/* $NetBSD: _context_u.S,v 1.2 2003/01/18 10:34:22 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <machine/asm.h>
+#include "assym.h"
+
+
+#define NOTREACHED illtrap 10
+
+ .global _C_LABEL(setcontext)
+
+ /*
+ * For voluntary context switches save/restore take care
+ * of caller's window, the only registers which should be
+ * saved are frame pointer and return address
+ */
+
+#define GETC(reg) \
+ stx %i6, [ reg + UC_GREGS + _REG_O6 * 8 ] ; \
+ add %i7, 8, %l0 ; \
+ stx %l0, [ reg + UC_GREGS + _REG_PC * 8 ] ; \
+ \
+ sethi %hi(0x40000000), %l1 ; \
+ or %l1, _UC_CPU, %l1 ; \
+ \
+ st %l1, [ reg + UC_FLAGS ]
+
+#define SETC(reg) \
+ ld [ reg + UC_FLAGS ], %l0 ; \
+ sethi %hi(0x40000000), %l1 ; \
+ andcc %l0, %l1, %g0 ; \
+ bz,a,pn %icc,1f ; \
+ nop ; \
+ \
+ ldx [ reg + UC_GREGS + _REG_PC * 8 ], %i7 ; \
+ ldx [ reg + UC_GREGS + _REG_O6 * 8 ], %i6 ; \
+ ; \
+ jmpl %i7, %g0 ; \
+ restore ; \
+1: call _C_LABEL(setcontext) ; \
+ mov reg, %o0
+
+ENTRY(_getcontext_u)
+ save %sp, -CC64FSZ, %sp
+ flushw ! XXX
+ GETC(%i0)
+ clr %i0
+ ret
+ restore
+
+ENTRY(_setcontext_u)
+ save %sp, -CC64FSZ, %sp
+ flushw
+ mov %i0, %o0
+ SETC(%o0)
+ NOTREACHED
+
+ENTRY(_swapcontext_u)
+ save %sp, -CC64FSZ, %sp
+ flushw
+ GETC(%i0)
+ mov %i1, %o0
+ SETC(%o0)
+ NOTREACHED
+
diff --git a/lib/libpthread/arch/sparc64/genassym.cf b/lib/libpthread/arch/sparc64/genassym.cf
new file mode 100644
index 00000000000..f254d85ed42
--- /dev/null
+++ b/lib/libpthread/arch/sparc64/genassym.cf
@@ -0,0 +1,56 @@
+# $NetBSD: genassym.cf,v 1.2 2003/01/18 10:34:22 thorpej Exp $
+
+
+include <ucontext.h>
+include <sys/queue.h>
+include <machine/reg.h>
+include <machine/mcontext.h>
+include <machine/frame.h>
+include "pthread.h"
+include "pthread_int.h"
+include "pthread_md.h"
+
+define PT_NEXT offsetof(struct pthread_st, pt_next)
+define PT_STATE offsetof(struct pthread_st, pt_state)
+define PT_SWITCHTO offsetof(struct pthread_st, pt_switchto)
+define PT_SWITCHTOUC offsetof(struct pthread_st, pt_switchtouc)
+define PT_SLEEPUC offsetof(struct pthread_st, pt_sleepuc)
+define PT_SPINLOCKS offsetof(struct pthread_st, pt_spinlocks)
+define PT_HELDLOCK offsetof(struct pthread_st, pt_heldlock)
+define PT_UC offsetof(struct pthread_st, pt_uc)
+define CONTEXTSIZE sizeof(ucontext_t)
+define UC_FLAGS offsetof(ucontext_t, uc_flags)
+define UC_GREGS offsetof(ucontext_t, uc_mcontext.__gregs)
+define UC_FPREGS offsetof(ucontext_t, uc_mcontext.__fpregs)
+define PT_STATE_RECYCLABLE PT_STATE_RECYCLABLE
+define STACKSPACE STACKSPACE
+define _UC_USER_BIT _UC_USER_BIT
+define _UC_CPU _UC_CPU
+define _UC_FPU _UC_FPU
+
+define CC64FSZ CC64FSZ
+# define BIAS BIAS
+
+define FPRS_FEF FPRS_FEF
+
+define _REG_CCR _REG_CCR
+define _REG_PC _REG_PC
+define _REG_nPC _REG_nPC
+define _REG_Y _REG_Y
+define _REG_G1 _REG_G1
+define _REG_G2 _REG_G2
+define _REG_G3 _REG_G3
+define _REG_G4 _REG_G4
+define _REG_G5 _REG_G5
+define _REG_G6 _REG_G6
+define _REG_G7 _REG_G7
+define _REG_O0 _REG_O0
+define _REG_O1 _REG_O1
+define _REG_O2 _REG_O2
+define _REG_O3 _REG_O3
+define _REG_O4 _REG_O4
+define _REG_O5 _REG_O5
+define _REG_O6 _REG_O6
+define _REG_O7 _REG_O7
+define _REG_ASI _REG_ASI
+define _REG_FPRS _REG_FPRS
diff --git a/lib/libpthread/arch/sparc64/pthread_md.h b/lib/libpthread/arch/sparc64/pthread_md.h
new file mode 100644
index 00000000000..b847b27ef26
--- /dev/null
+++ b/lib/libpthread/arch/sparc64/pthread_md.h
@@ -0,0 +1,86 @@
+/* $NetBSD: pthread_md.h,v 1.2 2003/01/18 10:34:22 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _LIB_PTHREAD_SPARC64_MD_H
+#define _LIB_PTHREAD_SPARC64_MD_H
+
+/*
+ * pthread__sp used for identifying thread
+ */
+static __inline long
+pthread__sp(void)
+{
+ long ret;
+
+ __asm("mov %%sp, %0" : "=r" (ret));
+
+ return ret;
+}
+
+#define pthread__uc_sp(ucp) ((ucp)->uc_mcontext.__gregs[_REG_O6])
+#define pthread__uc_pc(ucp) ((ucp)->uc_mcontext.__gregs[_REG_PC])
+
+#define STACKSPACE 176 /* min stack frame XXX */
+
+/*
+ * Conversions between struct reg and struct mcontext. Used by
+ * libpthread_dbg.
+ * XXX macros
+ */
+
+#define PTHREAD_UCONTEXT_TO_REG(reg, uc) do { \
+ memcpy(&(reg)->r_tstate, &(uc)->uc_mcontext.__gregs, sizeof(__gregset_t));\
+ } while (/*CONSTCOND*/0)
+
+#define PTHREAD_REG_TO_UCONTEXT(uc, reg) do { \
+ memcpy(&(uc)->uc_mcontext.__gregs, &(reg)->r_tstate, sizeof(__gregset_t));\
+ (uc)->uc_flags = ((uc)->uc_flags | _UC_CPU) & ~_UC_USER; \
+ } while (/*CONSTCOND*/0)
+
+#define PTHREAD_UCONTEXT_TO_FPREG(freg, uc) \
+ memcpy((freg), &(uc)->uc_mcontext.__fpregs, \
+ sizeof(struct fpreg)) \
+
+#define PTHREAD_FPREG_TO_UCONTEXT(uc, freg) do { \
+ memcpy(&(uc)->uc_mcontext.__fpregs, (freg), \
+ sizeof(struct fpreg)); \
+ (uc)->uc_flags = ((uc)->uc_flags | _UC_FPU) & ~_UC_USER; \
+ } while (/*CONSTCOND*/0)
+
+#ifdef __PTHREAD_SIGNAL_PRIVATE
+#include <machine/psl.h>
+#endif
+
+#endif /* _LIB_PTHREAD_SPARC64_MD_H */
diff --git a/lib/libpthread/arch/sparc64/pthread_switch.S b/lib/libpthread/arch/sparc64/pthread_switch.S
new file mode 100644
index 00000000000..9e4883bdeb8
--- /dev/null
+++ b/lib/libpthread/arch/sparc64/pthread_switch.S
@@ -0,0 +1,290 @@
+/* $NetBSD: pthread_switch.S,v 1.2 2003/01/18 10:34:22 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This file implements low-level routines that are exported to
+ * the machine-independent parts of the thread library. The routines are:
+ *
+ * void pthread__switch(pthread_t self, pthread_t next);
+ * void pthread__upcall_switch(pthread_t self, pthread_t next);
+ * void pthread__locked_switch(pthread_t self, pthread_t next,
+ * pt_spin_t *lock);
+ *
+ * as well as some utility code used by these routines.
+ */
+
+#include <machine/asm.h>
+#include "assym.h"
+
+#define STACK_ALIGN(x) (((x) + 0xf) & ~0xf)
+
+#define NOTREACHED illtrap 10
+
+ENTRY(pthread__switch)
+ /*
+ * XXX Get space on stack for return address and ucontext,
+ * save return address, put ucontext address into pthread
+ * then swapcontext
+ */
+ save %sp, -STACK_ALIGN(CC64FSZ + CONTEXTSIZE), %sp
+
+ add %fp, BIAS - CONTEXTSIZE, %l0
+ stx %l0, [%i0 + PT_UC]
+ ldx [%i1 + PT_UC], %o1
+ call _C_LABEL(_swapcontext_u)
+ mov %l0, %o0
+
+ ret
+ restore
+
+
+/* *** WARNING ***
+ * STACK_SWITCH is more subtle than it looks. Please go read the extended
+ * comment in the i386 pthread_switch.S file.
+ */
+
+
+/*
+ * pt register which contains *pthread, which stack will be used
+ * tmp temporary register which will have *ucontext_t for that pthread
+ */
+#define STACK_SWITCH(pt,tmp) \
+ ldx [pt + PT_UC], tmp ; \
+ add tmp, - STACK_ALIGN(STACKSPACE + CONTEXTSIZE) - BIAS, %sp
+
+
+/*
+ * Helper switch code used by pthread__locked_switch() and
+ * pthread__upcall_switch() when they discover spinlock preemption.
+ */
+
+/*
+ * pthread__switch_away
+ * %i0 who's lock to release
+ * %i1 thread to switch to
+ * %i2 flag to clear lock
+ */
+
+ENTRY(pthread__switch_away)
+
+ STACK_SWITCH(%i1,%l2)
+
+ /* If we're invoked from the switch-to-next provisions of
+ * pthread__locked_switch or pthread__upcall_switch, there may
+ * be a fake spinlock-count set. If so, they will set i2 to
+ * let us know, and we decrement it now that we're no longer
+ * using the old stack.
+ */
+ brz,a %i2, 1f
+ nop
+ ld [%i0 + PT_SPINLOCKS], %l0
+ dec %l0
+ st %l0, [%i0 + PT_SPINLOCKS]
+
+1: call _C_LABEL(_setcontext_u)
+ mov %l2, %o0
+ NOTREACHED
+
+/*
+ * void pthread__upcall_switch(pthread_t self, pthread_t next);
+ *
+ * Quit an upcall, recycle it, and jump to the selected thread.
+ */
+ENTRY(pthread__upcall_switch)
+ save %sp, -CC64FSZ, %sp
+
+ /* Create a "fake" lock count so that this code will be continued */
+ ld [%i1 + PT_SPINLOCKS], %l0
+ inc %l0
+ st %l0, [%i1 + PT_SPINLOCKS]
+
+ /*
+ * switch to next thread stack so we could recycle self's one
+ */
+ STACK_SWITCH(%i1,%l2)
+
+ /* Check if the upcall code was preempted and continued to here. */
+ ldx [%i0 + PT_NEXT], %l0
+ brz,a %l0, 1f
+ nop
+
+ /* Yes, it was. Stash the thread we were going to switch to,
+ * and switch to the next thread in the continuation chain.
+ */
+ stx %i1, [%i0 + PT_SWITCHTO]
+ stx %l2, [%i0 + PT_SWITCHTOUC]
+ or %g0, PT_STATE_RECYCLABLE, %l1
+ st %l1, [%i0 + PT_STATE]
+ mov %i1, %i0
+ mov %l0, %i1
+ ba pthread__switch_away
+ mov 1, %i2
+
+ /* No old-upcall-preemption */
+1: mov %i0, %o0
+ call _C_LABEL(pthread__sa_recycle)
+ mov %i1, %o1
+
+ /* We can now release the fake lock. */
+ ld [%i1 + PT_SPINLOCKS], %l0
+ dec %l0
+ st %l0, [%i1 + PT_SPINLOCKS]
+
+ /* Check if we were preempted and continued while faking the lock */
+ ldx [%i1 + PT_NEXT], %l0
+ brz,a %l0, 2f
+ nop
+
+ /* Yes, we were. Stash the to-be-switched-to context in our thread
+ * structure and go to the next link in the chain.
+ */
+ stx %i1, [%i1 + PT_SWITCHTO]
+ stx %l2, [%i1 + PT_SWITCHTOUC]
+ mov %i1, %i0
+ mov %l0, %i1
+ ba pthread__switch_away
+ mov 0, %i2
+
+ /* No new-upcall-preemption */
+2:
+ call _C_LABEL(_setcontext_u)
+ ldx [%i1 + PT_UC], %o0
+ NOTREACHED
+
+
+/*
+ * void pthread__locked_switch(pthread_t self, pthread_t next,
+ * pt_spin_t *lock);
+ *
+ * Switch away from a thread that is holding a lock on a queue (to
+ * prevent being removed from the queue before being switched away).
+ */
+ENTRY(pthread__locked_switch)
+ save %sp, -STACK_ALIGN(CC64FSZ + CONTEXTSIZE), %sp
+
+ /* Make sure we get continuted */
+ ld [%i1 + PT_SPINLOCKS], %l0
+ inc %l0
+ st %l0, [%i1 + PT_SPINLOCKS]
+
+ /* Get the current context */
+ add %fp, BIAS - CONTEXTSIZE, %l3
+ stx %l3, [%i0 + PT_UC]
+ call _C_LABEL(_getcontext_u)
+ mov %l3, %o0
+
+ /* Edit the context to make it continue below, rather than here */
+#ifdef PIC
+ PICCY_SET(locked_return_point,%l1,%l5)
+#else
+ set locked_return_point, %l1
+#endif
+ stx %l1, [%l3 + UC_GREGS + _REG_PC * 8]
+
+ STACK_SWITCH(%i1, %l2)
+
+ /* Check if the switcher was preempted and continued to here. */
+ ldx [%i0 + PT_NEXT], %l4
+ brz,a %l4, 1f
+ nop
+
+ /* Yes, it was. Stash the thread we were going to switch to,
+ * the lock the original thread was holding,
+ * and switch to the next thread in the continuation chain.
+ * Mark the fact that this was a locked switch, and so the
+ * thread does not need to be put on a run queue.
+ * Don't release the lock. It's possible that if we do so,
+ * PT_SWITCHTO will be stomped by another switch_lock and
+ * preemption.
+ */
+ stx %i1, [%i0 + PT_SWITCHTO]
+ stx %l2, [%i0 + PT_SWITCHTOUC]
+ stx %i2, [%i0 + PT_HELDLOCK]
+ ld [%i0 + PT_SPINLOCKS], %l0
+ dec %l0
+ st %l0, [%i0 + PT_SPINLOCKS]
+
+ /* Save the context we previously stored in PT_UC(a0);
+ * that was overwritten when we were preempted and continued,
+ * so we need to put it somewhere.
+ */
+ stx %l3, [%i0 + PT_SLEEPUC]
+
+ mov %i1, %i0
+ mov %l4, %i1
+ ba pthread__switch_away
+ mov 1, %i2
+
+ /* No locked old-preemption */
+1: /* We've moved to the new stack, and the old context has been
+ * saved. The queue lock can be released.
+ */
+ /* Reduce the lock count... */
+ ld [%i0 + PT_SPINLOCKS], %l0
+ dec %l0
+ st %l0, [%i0 + PT_SPINLOCKS]
+
+ /* ... actually release the lock.. */
+! membar #Sync
+
+ st %g0, [%i2]
+ /* .. and remove the fake lock */
+ ld [%i1 + PT_SPINLOCKS], %l0
+ dec %l0
+ st %l0, [%i1 + PT_SPINLOCKS]
+
+ /* Check if we were preempted while holding the fake lock. */
+ ldx [%i1 + PT_NEXT], %l0
+ brz,a %l0, 2f
+ nop
+
+ /* Yes, we were. Go to the next element in the chain. */
+ stx %i1, [%i1 + PT_SWITCHTO]
+ stx %l2, [%i1 + PT_SWITCHTOUC]
+ mov %i1, %i0
+ mov %l0, %i1
+ ba pthread__switch_away
+ mov %g0, %i2
+
+2: /* %l2 points to next thread's ucontext */
+ call _C_LABEL(_setcontext_u)
+ mov %l2, %o0
+ NOTREACHED
+
+locked_return_point:
+ ret
+ restore
+
+
diff --git a/lib/libpthread/debuglog.c b/lib/libpthread/debuglog.c
new file mode 100644
index 00000000000..91165f23e28
--- /dev/null
+++ b/lib/libpthread/debuglog.c
@@ -0,0 +1,157 @@
+/* $NetBSD: debuglog.c,v 1.2 2003/01/18 10:34:14 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nathan J. Williams.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <err.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/shm.h>
+
+#include "pthread.h"
+#include "pthread_int.h"
+
+
+void pthread__debuglog_read(int, int);
+void pthread__debuglog_rawdump(void);
+
+void usage(void);
+
+
+static struct pthread_msgbuf* debugbuf;
+
+int
+main(int argc, char *argv[])
+{
+ int ch;
+ extern int optind;
+ extern char *optarg;
+
+ int follow, initialize, raw, keep;
+
+ follow = 0;
+ initialize = 0;
+ raw = 0;
+ keep = 0;
+
+ while ((ch = getopt(argc, argv, "rfik")) != -1)
+ switch (ch) {
+ case 'k':
+ keep = 1;
+ break;
+ case 'r':
+ raw = 1;
+ break;
+ case 'f':
+ follow = 1;
+ break;
+ case 'i':
+ initialize = 1;
+ break;
+
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 0)
+ usage();
+
+ debugbuf = pthread__debuglog_init(initialize);
+
+ if (raw)
+ pthread__debuglog_rawdump();
+ else
+ pthread__debuglog_read(follow, !keep);
+
+ return 0;
+}
+
+void
+pthread__debuglog_rawdump(void)
+{
+
+ printf("Debug log raw dump\n");
+ printf("Magic: %06x\n", debugbuf->msg_magic);
+ printf("Size: %06lx\n", debugbuf->msg_bufs);
+ printf("Read start: %ld Write start: %ld\n\n",
+ debugbuf->msg_bufr, debugbuf->msg_bufw);
+
+ fwrite(&debugbuf->msg_bufc[0], debugbuf->msg_bufs, 1, stdout);
+}
+
+void
+pthread__debuglog_read(int follow, int trunc)
+{
+
+ int readp, writep;
+
+ do {
+
+ readp = debugbuf->msg_bufr;
+ writep = debugbuf->msg_bufw;
+
+ if (readp < writep) {
+ fwrite(&debugbuf->msg_bufc[readp], writep - readp,
+ 1, stdout);
+ } else if (readp > writep) {
+ fwrite(&debugbuf->msg_bufc[readp],
+ debugbuf->msg_bufs - readp, 1, stdout);
+ fwrite(&debugbuf->msg_bufc[0], writep, 1, stdout);
+ }
+
+ if (trunc)
+ debugbuf->msg_bufr = writep;
+
+ if (follow)
+ sleep(1);
+ } while (follow);
+}
+
+
+
+
+void usage()
+{
+
+ fprintf(stderr,"usage: debuglog [-fi]\n");
+ exit(1);
+}
diff --git a/lib/libpthread/genassym.sh b/lib/libpthread/genassym.sh
new file mode 100644
index 00000000000..b21616b0ad2
--- /dev/null
+++ b/lib/libpthread/genassym.sh
@@ -0,0 +1,155 @@
+#!/bin/sh
+# $NetBSD: genassym.sh,v 1.2 2003/01/18 10:34:14 thorpej Exp $
+
+#
+# Copyright (c) 1997 Matthias Pfaller.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by Matthias Pfaller.
+# 4. The name of the author may not be used to endorse or promote products
+# derived from this software without specific prior written permission
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# If first argument is -c, create a temporary C file,
+# compile it and execute the result.
+
+
+awk=${AWK:-awk}
+
+if [ $1 = '-c' ] ; then
+ shift
+ ccode=1
+else
+ ccode=0
+fi
+
+#trap "rm -f /tmp/$$.c /tmp/genassym.$$" 0 1 2 3 15
+
+$awk '
+BEGIN {
+ printf("#define offsetof(type, member) ((size_t)(&((type *)0)->member))\n");
+ defining = 0;
+ type = "long";
+ asmtype = "n";
+ asmprint = "";
+}
+
+$0 ~ /^[ \t]*#.*/ || $0 ~ /^[ \t]*$/ {
+ # Just ignore comments and empty lines
+ next;
+}
+
+$0 ~ /^config[ \t]/ {
+ type = $2;
+ asmtype = $3;
+ asmprint = $4;
+ next;
+}
+
+/^include[ \t]/ {
+ if (defining != 0) {
+ defining = 0;
+ printf("}\n");
+ }
+ printf("#%s\n", $0);
+ next;
+}
+
+$0 ~ /^if[ \t]/ ||
+$0 ~ /^ifdef[ \t]/ ||
+$0 ~ /^ifndef[ \t]/ ||
+$0 ~ /^else/ ||
+$0 ~ /^elif[ \t]/ ||
+$0 ~ /^endif/ {
+ printf("#%s\n", $0);
+ next;
+}
+
+/^struct[ \t]/ {
+ structname = $2;
+ $0 = "define " structname "_SIZEOF sizeof(struct " structname ")";
+ # fall through
+}
+
+/^member[ \t]/ {
+ $0 = "define " $2 " offsetof(struct " structname ", " $2 ")";
+ # fall through
+}
+
+/^export[ \t]/ {
+ $0 = "define " $2 " " $2;
+ # fall through
+}
+
+/^define[ \t]/ {
+ if (defining == 0) {
+ defining = 1;
+ printf("void f" FNR "(void);\n");
+ printf("void f" FNR "() {\n");
+ if (ccode)
+ call[FNR] = "f" FNR;
+ defining = 1;
+ }
+ value = $0
+ gsub("^define[ \t]+[A-Za-z_][A-Za-z_0-9]*[ \t]+", "", value)
+ if (ccode)
+ printf("printf(\"#define " $2 " %%ld\\n\", (%s)" value ");\n", type);
+ else
+ printf("__asm(\"XYZZY %s %%%s0\" : : \"%s\" (%s));\n", $2, asmprint, asmtype, value);
+ next;
+}
+
+/^quote[ \t]/ {
+ gsub("^quote[ \t]+", "");
+ print;
+ next;
+}
+
+{
+ printf("syntax error in line %d\n", FNR) >"/dev/stderr";
+ exit(1);
+}
+
+END {
+ if (defining != 0) {
+ defining = 0;
+ printf("}\n");
+ }
+ if (ccode) {
+ printf("int main(int argc, char **argv) {");
+ for (i in call)
+ printf(call[i] "();");
+ printf("return(0); }\n");
+ }
+}
+' ccode=$ccode > /tmp/$$.c || exit 1
+
+if [ $ccode = 1 ] ; then
+ "$@" /tmp/$$.c -o /tmp/genassym.$$ && /tmp/genassym.$$
+else
+ # Kill all of the "#" and "$" modifiers; locore.s already
+ # prepends the correct "constant" modifier.
+ "$@" -S /tmp/$$.c -o -| sed -e 's/#//g' -e 's/\$//g' | \
+ sed -n 's/.*XYZZY/#define/gp'
+fi
diff --git a/lib/libpthread/pthread.c b/lib/libpthread/pthread.c
new file mode 100644
index 00000000000..f69e7b60081
--- /dev/null
+++ b/lib/libpthread/pthread.c
@@ -0,0 +1,835 @@
+/* $NetBSD: pthread.c,v 1.2 2003/01/18 10:34:15 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nathan J. Williams.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <lwp.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ucontext.h>
+#include <sys/cdefs.h>
+
+#include <sched.h>
+#include "pthread.h"
+#include "pthread_int.h"
+
+
+#undef PTHREAD_MAIN_DEBUG
+
+#ifdef PTHREAD_MAIN_DEBUG
+#define SDPRINTF(x) DPRINTF(x)
+#else
+#define SDPRINTF(x)
+#endif
+
+static void pthread__create_tramp(void *(*start)(void *), void *arg);
+
+int pthread__started;
+
+pthread_spin_t pthread__allqueue_lock;
+struct pthread_queue_t pthread__allqueue;
+
+pthread_spin_t pthread__deadqueue_lock;
+struct pthread_queue_t pthread__deadqueue;
+struct pthread_queue_t pthread__reidlequeue;
+
+static int nthreads;
+static int nextthread;
+static pthread_spin_t nextthread_lock;
+static pthread_attr_t pthread_default_attr;
+
+pthread_spin_t pthread__runqueue_lock;
+struct pthread_queue_t pthread__runqueue;
+struct pthread_queue_t pthread__idlequeue;
+
+__strong_alias(__libc_thr_self,pthread_self)
+__strong_alias(__libc_thr_errno,pthread__errno)
+
+/*
+ * Static library kludge. Place a reference to a symbol any library
+ * file which does not already have a reference here.
+ */
+extern int pthread__cancel_stub_binder;
+extern int pthread__sched_binder;
+
+void *pthread__static_lib_binder[] = {
+ &pthread__cancel_stub_binder,
+ pthread_cond_init,
+ pthread_mutex_init,
+ pthread_rwlock_init,
+ pthread_barrier_init,
+ pthread_key_create,
+ &pthread__sched_binder,
+};
+
+/*
+ * This needs to be started by the library loading code, before main()
+ * gets to run, for various things that use the state of the initial thread
+ * to work properly (thread-specific data is an application-visible example;
+ * spinlock counts for mutexes is an internal example).
+ */
+void
+pthread_init(void)
+{
+ pthread_t first;
+ extern int __isthreaded;
+
+ /* Initialize locks first; they're needed elsewhere. */
+ pthread__lockprim_init();
+
+ /* Basic data structure setup */
+ pthread_attr_init(&pthread_default_attr);
+ PTQ_INIT(&pthread__allqueue);
+ PTQ_INIT(&pthread__deadqueue);
+ PTQ_INIT(&pthread__reidlequeue);
+ PTQ_INIT(&pthread__runqueue);
+ PTQ_INIT(&pthread__idlequeue);
+
+ /* Create the thread structure corresponding to main() */
+ pthread__initmain(&first);
+ pthread__initthread(first, first);
+ first->pt_state = PT_STATE_RUNNING;
+ sigprocmask(0, NULL, &first->pt_sigmask);
+ PTQ_INSERT_HEAD(&pthread__allqueue, first, pt_allq);
+
+ /* Start subsystems */
+ pthread__alarm_init();
+ pthread__signal_init();
+ PTHREAD_MD_INIT
+#ifdef PTHREAD__DEBUG
+ pthread__debug_init();
+#endif
+
+ /* Tell libc that we're here and it should role-play accordingly. */
+ __isthreaded = 1;
+}
+
+
+static void
+pthread__start(void)
+{
+ pthread_t self, idle;
+ int i, ret;
+
+ self = pthread__self(); /* should be the "main()" thread */
+
+
+ /* Create idle threads */
+ for (i = 0; i < NIDLETHREADS; i++) {
+ ret = pthread__stackalloc(&idle);
+ if (ret != 0)
+ err(1, "Couldn't allocate stack for idle thread!");
+ pthread__initthread(self, idle);
+ sigfillset(&idle->pt_sigmask);
+ idle->pt_type = PT_THREAD_IDLE;
+ PTQ_INSERT_HEAD(&pthread__allqueue, idle, pt_allq);
+ pthread__sched_idle(self, idle);
+ }
+
+ nthreads = 1;
+ /* Start up the SA subsystem */
+ pthread__sa_start();
+ SDPRINTF(("(pthread__start %p) Started.\n", self));
+}
+
+
+/* General-purpose thread data structure sanitization. */
+void
+pthread__initthread(pthread_t self, pthread_t t)
+{
+ int id;
+
+ pthread_spinlock(self, &nextthread_lock);
+ id = nextthread;
+ nextthread++;
+ pthread_spinunlock(self, &nextthread_lock);
+ t->pt_num = id;
+
+ t->pt_magic = PT_MAGIC;
+ t->pt_type = PT_THREAD_NORMAL;
+ t->pt_state = PT_STATE_RUNNABLE;
+ pthread_lockinit(&t->pt_statelock);
+ t->pt_spinlocks = 0;
+ t->pt_next = NULL;
+ t->pt_exitval = NULL;
+ t->pt_flags = 0;
+ t->pt_cancel = 0;
+ t->pt_errno = 0;
+ t->pt_parent = NULL;
+ t->pt_heldlock = NULL;
+ t->pt_switchto = NULL;
+ t->pt_sleepuc = NULL;
+ sigemptyset(&t->pt_siglist);
+ sigemptyset(&t->pt_sigmask);
+ pthread_lockinit(&t->pt_siglock);
+ PTQ_INIT(&t->pt_joiners);
+ pthread_lockinit(&t->pt_join_lock);
+ PTQ_INIT(&t->pt_cleanup_stack);
+ memset(&t->pt_specific, 0, sizeof(int) * PTHREAD_KEYS_MAX);
+#ifdef PTHREAD__DEBUG
+ t->blocks = 0;
+ t->preempts = 0;
+ t->rescheds = 0;
+#endif
+}
+
+
+int
+pthread_create(pthread_t *thread, const pthread_attr_t *attr,
+ void *(*startfunc)(void *), void *arg)
+{
+ pthread_t self, newthread;
+ pthread_attr_t nattr;
+ int ret;
+
+ PTHREADD_ADD(PTHREADD_CREATE);
+ assert(thread != NULL);
+
+ /*
+ * It's okay to check this without a lock because there can
+ * only be one thread before it becomes true.
+ */
+ if (pthread__started == 0) {
+ pthread__start();
+ pthread__started = 1;
+ }
+
+ if (attr == NULL)
+ nattr = pthread_default_attr;
+ else if (((attr != NULL) && (attr->pta_magic == PT_ATTR_MAGIC)))
+ nattr = *attr;
+ else
+ return EINVAL;
+
+
+ self = pthread__self();
+
+ pthread_spinlock(self, &pthread__deadqueue_lock);
+ if (!PTQ_EMPTY(&pthread__deadqueue)) {
+ newthread= PTQ_FIRST(&pthread__deadqueue);
+ PTQ_REMOVE(&pthread__deadqueue, newthread, pt_allq);
+ pthread_spinunlock(self, &pthread__deadqueue_lock);
+ } else {
+ pthread_spinunlock(self, &pthread__deadqueue_lock);
+ /* Set up a stack and allocate space for a pthread_st. */
+ ret = pthread__stackalloc(&newthread);
+ if (ret != 0)
+ return ret;
+ }
+
+ /* 2. Set up state. */
+ pthread__initthread(self, newthread);
+ newthread->pt_flags = nattr.pta_flags;
+ newthread->pt_sigmask = self->pt_sigmask;
+
+ /*
+ * 3. Set up context.
+ *
+ * The pt_uc pointer points to a location safely below the
+ * stack start; this is arranged by pthread__stackalloc().
+ */
+ _INITCONTEXT_U(newthread->pt_uc);
+ newthread->pt_uc->uc_stack = newthread->pt_stack;
+ newthread->pt_uc->uc_link = NULL;
+ makecontext(newthread->pt_uc, pthread__create_tramp, 2,
+ startfunc, arg);
+
+ /* 4. Add to list of all threads. */
+ pthread_spinlock(self, &pthread__allqueue_lock);
+ PTQ_INSERT_HEAD(&pthread__allqueue, newthread, pt_allq);
+ nthreads++;
+ pthread_spinunlock(self, &pthread__allqueue_lock);
+
+ SDPRINTF(("(pthread_create %p) Created new thread %p.\n", self, newthread));
+ /* 5. Put on run queue. */
+ pthread__sched(self, newthread);
+
+ *thread = newthread;
+
+ return 0;
+}
+
+
+static void
+pthread__create_tramp(void *(*start)(void *), void *arg)
+{
+ void *retval;
+
+ retval = start(arg);
+
+ pthread_exit(retval);
+
+ /* NOTREACHED */
+ assert(0);
+}
+
+
+/*
+ * Other threads will switch to the idle thread so that they
+ * can dispose of any awkward locks or recycle upcall state.
+ */
+void
+pthread__idle(void)
+{
+ pthread_t self;
+
+ PTHREADD_ADD(PTHREADD_IDLE);
+ self = pthread__self();
+ SDPRINTF(("(pthread__idle %p).\n", self));
+
+ /*
+ * The drill here is that we want to yield the processor,
+ * but for the thread itself to be recovered, we need to be on
+ * a list somewhere for the thread system to know about us.
+ */
+ pthread_spinlock(self, &pthread__deadqueue_lock);
+ PTQ_INSERT_TAIL(&pthread__reidlequeue, self, pt_runq);
+ self->pt_flags |= PT_FLAG_IDLED;
+ pthread_spinunlock(self, &pthread__deadqueue_lock);
+
+ /*
+ * If we get to run this, then no preemption has happened
+ * (because the upcall handler will not continue an idle thread with
+ * PT_FLAG_IDLED set), and so we can yield the processor safely.
+ */
+ SDPRINTF(("(pthread__idle %p) yielding.\n", self));
+ sa_yield();
+
+ /* NOTREACHED */
+ self->pt_spinlocks++; /* XXX make sure we get to finish the assert! */
+ SDPRINTF(("(pthread__idle %p) Returned! Error.\n", self));
+ assert(0);
+}
+
+
+void
+pthread_exit(void *retval)
+{
+ pthread_t self, joiner;
+ struct pt_clean_t *cleanup;
+ int nt;
+
+ self = pthread__self();
+ SDPRINTF(("(pthread_exit %p) Exiting.\n", self));
+
+ /* Disable cancellability. */
+ self->pt_flags |= PT_FLAG_CS_DISABLED;
+
+ /* Call any cancellation cleanup handlers */
+ while (!PTQ_EMPTY(&self->pt_cleanup_stack)) {
+ cleanup = PTQ_FIRST(&self->pt_cleanup_stack);
+ PTQ_REMOVE(&self->pt_cleanup_stack, cleanup, ptc_next);
+ (*cleanup->ptc_cleanup)(cleanup->ptc_arg);
+ }
+
+ /* Perform cleanup of thread-specific data */
+ pthread__destroy_tsd(self);
+
+ self->pt_exitval = retval;
+
+ pthread_spinlock(self, &self->pt_join_lock);
+ if (self->pt_flags & PT_FLAG_DETACHED) {
+ pthread_spinunlock(self, &self->pt_join_lock);
+
+ pthread_spinlock(self, &pthread__allqueue_lock);
+ PTQ_REMOVE(&pthread__allqueue, self, pt_allq);
+ nthreads--;
+ nt = nthreads;
+ pthread_spinunlock(self, &pthread__allqueue_lock);
+
+ self->pt_state = PT_STATE_DEAD;
+ if (nt == 0) {
+ /* Whoah, we're the last one. Time to go. */
+ exit(0);
+ }
+
+ /* Yeah, yeah, doing work while we're dead is tacky. */
+ pthread_spinlock(self, &pthread__deadqueue_lock);
+ PTQ_INSERT_HEAD(&pthread__deadqueue, self, pt_allq);
+ pthread__block(self, &pthread__deadqueue_lock);
+ } else {
+ pthread_spinlock(self, &pthread__allqueue_lock);
+ nthreads--;
+ nt = nthreads;
+ self->pt_state = PT_STATE_ZOMBIE;
+ pthread_spinunlock(self, &pthread__allqueue_lock);
+ if (nt == 0) {
+ /* Whoah, we're the last one. Time to go. */
+ exit(0);
+ }
+ /* Wake up all the potential joiners. Only one can win.
+ * (Can you say "Thundering Herd"? I knew you could.)
+ */
+ PTQ_FOREACH(joiner, &self->pt_joiners, pt_sleep)
+ pthread__sched(self, joiner);
+ pthread__block(self, &self->pt_join_lock);
+ }
+
+ /* NOTREACHED */
+ assert(0);
+ exit(1);
+}
+
+
+int
+pthread_join(pthread_t thread, void **valptr)
+{
+ pthread_t self;
+
+ self = pthread__self();
+ SDPRINTF(("(pthread_join %p) Joining %p.\n", self, thread));
+
+ if (pthread__find(self, thread) != 0)
+ return ESRCH;
+
+ if (thread->pt_magic != PT_MAGIC)
+ return EINVAL;
+
+ if (thread == self)
+ return EDEADLK;
+
+ pthread_spinlock(self, &thread->pt_join_lock);
+
+ if (thread->pt_flags & PT_FLAG_DETACHED) {
+ pthread_spinunlock(self, &thread->pt_join_lock);
+ return EINVAL;
+ }
+
+ if ((thread->pt_state != PT_STATE_ZOMBIE) &&
+ (thread->pt_state != PT_STATE_DEAD)) {
+ /*
+ * "I'm not dead yet!"
+ * "You will be soon enough."
+ */
+ pthread_spinlock(self, &self->pt_statelock);
+ if (self->pt_cancel) {
+ pthread_spinunlock(self, &self->pt_statelock);
+ pthread_spinunlock(self, &thread->pt_join_lock);
+ pthread_exit(PTHREAD_CANCELED);
+ }
+ self->pt_state = PT_STATE_BLOCKED_QUEUE;
+ self->pt_sleepobj = thread;
+ self->pt_sleepq = &thread->pt_joiners;
+ self->pt_sleeplock = &thread->pt_join_lock;
+ pthread_spinunlock(self, &self->pt_statelock);
+
+ PTQ_INSERT_TAIL(&thread->pt_joiners, self, pt_sleep);
+ pthread__block(self, &thread->pt_join_lock);
+ pthread_spinlock(self, &thread->pt_join_lock);
+ }
+
+ if ((thread->pt_state == PT_STATE_DEAD) ||
+ (thread->pt_flags & PT_FLAG_DETACHED)) {
+ /* Someone beat us to the join, or called pthread_detach(). */
+ pthread_spinunlock(self, &thread->pt_join_lock);
+ return ESRCH;
+ }
+
+ /* All ours. */
+ thread->pt_state = PT_STATE_DEAD;
+ pthread_spinunlock(self, &thread->pt_join_lock);
+
+ if (valptr != NULL)
+ *valptr = thread->pt_exitval;
+
+ SDPRINTF(("(pthread_join %p) Joined %p.\n", self, thread));
+
+ /* Cleanup time. Move the dead thread from allqueue to the deadqueue */
+ pthread_spinlock(self, &pthread__allqueue_lock);
+ PTQ_REMOVE(&pthread__allqueue, thread, pt_allq);
+ pthread_spinunlock(self, &pthread__allqueue_lock);
+
+ pthread_spinlock(self, &pthread__deadqueue_lock);
+ PTQ_INSERT_HEAD(&pthread__deadqueue, thread, pt_allq);
+ pthread_spinunlock(self, &pthread__deadqueue_lock);
+
+ return 0;
+}
+
+
+int
+pthread_equal(pthread_t t1, pthread_t t2)
+{
+
+ /* Nothing special here. */
+ return (t1 == t2);
+}
+
+
+int
+pthread_detach(pthread_t thread)
+{
+ pthread_t self, joiner;
+
+ self = pthread__self();
+
+ if (pthread__find(self, thread) != 0)
+ return ESRCH;
+
+ if (thread->pt_magic != PT_MAGIC)
+ return EINVAL;
+
+ pthread_spinlock(self, &thread->pt_join_lock);
+
+ if (thread->pt_flags & PT_FLAG_DETACHED) {
+ pthread_spinunlock(self, &thread->pt_join_lock);
+ return EINVAL;
+ }
+
+ thread->pt_flags |= PT_FLAG_DETACHED;
+
+ /* Any joiners have to be punted now. */
+ PTQ_FOREACH(joiner, &thread->pt_joiners, pt_sleep)
+ pthread__sched(self, joiner);
+
+ pthread_spinunlock(self, &thread->pt_join_lock);
+
+ return 0;
+}
+
+
+int
+pthread_attr_init(pthread_attr_t *attr)
+{
+
+ attr->pta_magic = PT_ATTR_MAGIC;
+ attr->pta_flags = 0;
+
+ return 0;
+}
+
+
+int
+pthread_attr_destroy(pthread_attr_t *attr)
+{
+
+ return 0;
+}
+
+
+int
+pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate)
+{
+
+ if ((attr == NULL) || (attr->pta_magic != PT_ATTR_MAGIC))
+ return EINVAL;
+
+ *detachstate = (attr->pta_flags & PT_FLAG_DETACHED);
+
+ return 0;
+}
+
+
+int
+pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)
+{
+ if ((attr == NULL) || (attr->pta_magic != PT_ATTR_MAGIC))
+ return EINVAL;
+
+ switch (detachstate) {
+ case PTHREAD_CREATE_JOINABLE:
+ attr->pta_flags &= ~PT_FLAG_DETACHED;
+ break;
+ case PTHREAD_CREATE_DETACHED:
+ attr->pta_flags |= PT_FLAG_DETACHED;
+ break;
+ default:
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+
+int
+pthread_attr_setschedparam(pthread_attr_t *attr,
+ const struct sched_param *param)
+{
+
+ if ((attr == NULL) || (attr->pta_magic != PT_ATTR_MAGIC))
+ return EINVAL;
+
+ if (param == NULL)
+ return EINVAL;
+
+ if (param->sched_priority != 0)
+ return EINVAL;
+
+ return 0;
+}
+
+
+int
+pthread_attr_getschedparam(pthread_attr_t *attr, struct sched_param *param)
+{
+
+ if ((attr == NULL) || (attr->pta_magic != PT_ATTR_MAGIC))
+ return EINVAL;
+
+ if (param == NULL)
+ return EINVAL;
+
+ param->sched_priority = 0;
+
+ return 0;
+}
+
+
+/*
+ * XXX There should be a way for applications to use the efficent
+ * inline version, but there are opacity/namespace issues.
+ */
+pthread_t
+pthread_self(void)
+{
+
+ return pthread__self();
+}
+
+
+int
+pthread_cancel(pthread_t thread)
+{
+ pthread_t self;
+ int flags;
+
+ if (!(thread->pt_state == PT_STATE_RUNNING ||
+ thread->pt_state == PT_STATE_RUNNABLE ||
+ thread->pt_state == PT_STATE_BLOCKED_QUEUE ||
+ thread->pt_state == PT_STATE_BLOCKED_SYS))
+ return ESRCH;
+
+ self = pthread__self();
+ flags = thread->pt_flags;
+
+ flags |= PT_FLAG_CS_PENDING;
+ if ((flags & PT_FLAG_CS_DISABLED) == 0) {
+ thread->pt_cancel = 1;
+ pthread_spinlock(self, &thread->pt_statelock);
+ if (thread->pt_state == PT_STATE_BLOCKED_SYS) {
+ /*
+ * It's sleeping in the kernel. If we can wake
+ * it up, it will notice the cancellation when
+ * it returns. If it doesn't wake up when we
+ * make this call, then it's blocked
+ * uninterruptably in the kernel, and there's
+ * not much to be done about it.
+ */
+ _lwp_wakeup(thread->pt_blockedlwp);
+ } else if (thread->pt_state == PT_STATE_BLOCKED_QUEUE) {
+ /*
+ * We're blocked somewhere (pthread__block()
+ * was called. Cause it to wake up and the
+ * caller will check for the cancellation.
+ */
+ pthread_spinlock(self, thread->pt_sleeplock);
+ PTQ_REMOVE(thread->pt_sleepq, thread,
+ pt_sleep);
+ pthread_spinunlock(self, thread->pt_sleeplock);
+ pthread__sched(self, thread);
+ } else {
+ /*
+ * Nothing. The target thread is running and will
+ * notice at the next deferred cancellation point.
+ */
+ }
+ pthread_spinunlock(self, &thread->pt_statelock);
+ }
+
+ thread->pt_flags = flags;
+
+ return 0;
+}
+
+
+int
+pthread_setcancelstate(int state, int *oldstate)
+{
+ pthread_t self;
+ int flags;
+
+ self = pthread__self();
+ flags = self->pt_flags;
+
+ if (oldstate != NULL) {
+ if (flags & PT_FLAG_CS_DISABLED)
+ *oldstate = PTHREAD_CANCEL_DISABLE;
+ else
+ *oldstate = PTHREAD_CANCEL_ENABLE;
+ }
+
+ if (state == PTHREAD_CANCEL_DISABLE)
+ flags |= PT_FLAG_CS_DISABLED;
+ else if (state == PTHREAD_CANCEL_ENABLE) {
+ flags &= ~PT_FLAG_CS_DISABLED;
+ /*
+ * If a cancellation was requested while cancellation
+ * was disabled, note that fact for future
+ * cancellation tests.
+ */
+ if (flags & PT_FLAG_CS_PENDING) {
+ self->pt_cancel = 1;
+ /* This is not a deferred cancellation point. */
+ if (flags & PT_FLAG_CS_ASYNC)
+ pthread_exit(PTHREAD_CANCELED);
+ }
+ } else
+ return EINVAL;
+
+ self->pt_flags = flags;
+
+ return 0;
+}
+
+
+int
+pthread_setcanceltype(int type, int *oldtype)
+{
+ pthread_t self;
+ int flags;
+
+ self = pthread__self();
+ flags = self->pt_flags;
+
+ if (oldtype != NULL) {
+ if (flags & PT_FLAG_CS_ASYNC)
+ *oldtype = PTHREAD_CANCEL_ASYNCHRONOUS;
+ else
+ *oldtype = PTHREAD_CANCEL_DEFERRED;
+ }
+
+ if (type == PTHREAD_CANCEL_ASYNCHRONOUS) {
+ flags |= PT_FLAG_CS_ASYNC;
+ if (self->pt_cancel)
+ pthread_exit(PTHREAD_CANCELED);
+ } else if (type == PTHREAD_CANCEL_DEFERRED)
+ flags &= ~PT_FLAG_CS_ASYNC;
+ else
+ return EINVAL;
+
+ self->pt_flags = flags;
+
+ return 0;
+}
+
+
+void
+pthread_testcancel()
+{
+ pthread_t self;
+
+ self = pthread__self();
+ if (self->pt_cancel)
+ pthread_exit(PTHREAD_CANCELED);
+}
+
+
+/*
+ * POSIX requires that certain functions return an error rather than
+ * invoking undefined behavior even when handed completely bogus
+ * pthread_t values, e.g. stack garbage or (pthread_t)666. This
+ * utility routine searches the list of threads for the pthread_t
+ * value without dereferencing it.
+ */
+int
+pthread__find(pthread_t self, pthread_t id)
+{
+ pthread_t target;
+
+ pthread_spinlock(self, &pthread__allqueue_lock);
+ PTQ_FOREACH(target, &pthread__allqueue, pt_allq)
+ if (target == id)
+ break;
+ pthread_spinunlock(self, &pthread__allqueue_lock);
+
+ if (target == NULL)
+ return ESRCH;
+
+ return 0;
+}
+
+
+void
+pthread__testcancel(pthread_t self)
+{
+
+ if (self->pt_cancel)
+ pthread_exit(PTHREAD_CANCELED);
+}
+
+
+void
+pthread__cleanup_push(void (*cleanup)(void *), void *arg, void *store)
+{
+ pthread_t self;
+ struct pt_clean_t *entry;
+
+ self = pthread__self();
+ entry = store;
+ entry->ptc_cleanup = cleanup;
+ entry->ptc_arg = arg;
+ PTQ_INSERT_HEAD(&self->pt_cleanup_stack, entry, ptc_next);
+}
+
+
+void
+pthread__cleanup_pop(int ex, void *store)
+{
+ pthread_t self;
+ struct pt_clean_t *entry;
+
+ self = pthread__self();
+ entry = store;
+
+ PTQ_REMOVE(&self->pt_cleanup_stack, entry, ptc_next);
+ if (ex)
+ (*entry->ptc_cleanup)(entry->ptc_arg);
+}
+
+
+int *
+pthread__errno(void)
+{
+ pthread_t self;
+
+ self = pthread__self();
+
+ return &(self->pt_errno);
+}
diff --git a/lib/libpthread/pthread.h b/lib/libpthread/pthread.h
new file mode 100644
index 00000000000..01f900a682a
--- /dev/null
+++ b/lib/libpthread/pthread.h
@@ -0,0 +1,190 @@
+/* $NetBSD: pthread.h,v 1.2 2003/01/18 10:34:15 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nathan J. Williams.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _LIB_PTHREAD_H
+#define _LIB_PTHREAD_H
+
+#include <sys/cdefs.h>
+
+#include <time.h> /* For timespec */
+#include <sched.h>
+
+#include "pthread_types.h"
+
+__BEGIN_DECLS
+int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
+ void *(*startfunc)(void *), void *arg);
+void pthread_exit(void *retval) __attribute__((__noreturn__));
+int pthread_join(pthread_t thread, void **valptr);
+int pthread_equal(pthread_t t1, pthread_t t2);
+pthread_t pthread_self(void);
+int pthread_detach(pthread_t thread);
+
+int pthread_kill(pthread_t thread, int sig);
+
+int pthread_getrrtimer_np(void);
+int pthread_setrrtimer_np(int);
+
+int pthread_attr_destroy(pthread_attr_t *attr);
+int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate);
+int pthread_attr_init(pthread_attr_t *attr);
+int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
+int pthread_attr_setschedparam(pthread_attr_t *attr,
+ const struct sched_param *param);
+int pthread_attr_getschedparam(pthread_attr_t *attr,
+ struct sched_param *param);
+
+int pthread_mutex_init(pthread_mutex_t *mutex,
+ const pthread_mutexattr_t *attr);
+int pthread_mutex_destroy(pthread_mutex_t *mutex);
+int pthread_mutex_lock(pthread_mutex_t *mutex);
+int pthread_mutex_trylock(pthread_mutex_t *mutex);
+int pthread_mutex_unlock(pthread_mutex_t *mutex);
+int pthread_mutexattr_init(pthread_mutexattr_t *attr);
+int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
+int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type);
+int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type);
+
+int pthread_cond_init(pthread_cond_t *cond,
+ const pthread_condattr_t *attr);
+int pthread_cond_destroy(pthread_cond_t *cond);
+int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
+int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
+ const struct timespec *abstime);
+int pthread_cond_signal(pthread_cond_t *cond);
+int pthread_cond_broadcast(pthread_cond_t *cond);
+int pthread_condattr_init(pthread_condattr_t *attr);
+int pthread_condattr_destroy(pthread_condattr_t *attr);
+
+int pthread_once(pthread_once_t *once_control, void (*routine)(void));
+
+int pthread_key_create(pthread_key_t *key, void (*destructor)(void *));
+int pthread_key_delete(pthread_key_t key);
+int pthread_setspecific(pthread_key_t key, const void *value);
+void* pthread_getspecific(pthread_key_t key);
+
+int pthread_cancel(pthread_t thread);
+int pthread_setcancelstate(int state, int *oldstate);
+int pthread_setcanceltype(int type, int *oldtype);
+void pthread_testcancel(void);
+
+struct pthread_cleanup_store {
+ void *pad[4];
+};
+
+#define pthread_cleanup_push(routine,arg) \
+ { \
+ struct pthread_cleanup_store __store; \
+ pthread__cleanup_push((routine),(arg), &__store);
+
+#define pthread_cleanup_pop(execute) \
+ pthread__cleanup_pop((execute), &__store); \
+ }
+
+void pthread__cleanup_push(void (*routine)(void *), void *arg, void *store);
+void pthread__cleanup_pop(int execute, void *store);
+
+int pthread_spin_init(pthread_spinlock_t *lock, int pshared);
+int pthread_spin_destroy(pthread_spinlock_t *lock);
+int pthread_spin_lock(pthread_spinlock_t *lock);
+int pthread_spin_trylock(pthread_spinlock_t *lock);
+int pthread_spin_unlock(pthread_spinlock_t *lock);
+
+int pthread_rwlock_init(pthread_rwlock_t *rwlock,
+ const pthread_rwlockattr_t *attr);
+int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
+int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
+int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
+int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
+int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
+int pthread_rwlock_timedrdlock(pthread_rwlock_t *rwlock,
+ const struct timespec *abs_timeout);
+int pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlock,
+ const struct timespec *abs_timeout);
+int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
+int pthread_rwlockattr_init(pthread_rwlockattr_t *attr);
+int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr);
+
+int pthread_barrier_init(pthread_barrier_t *barrier,
+ const pthread_barrierattr_t *attr, unsigned int count);
+int pthread_barrier_wait(pthread_barrier_t *barrier);
+int pthread_barrier_destroy(pthread_barrier_t *barrier);
+int pthread_barrierattr_init(pthread_barrierattr_t *attr);
+int pthread_barrierattr_destroy(pthread_barrierattr_t *attr);
+
+int *pthread__errno(void);
+__END_DECLS
+
+#define PTHREAD_CREATE_JOINABLE 0
+#define PTHREAD_CREATE_DETACHED 1
+
+#define PTHREAD_PROCESS_PRIVATE 0
+#define PTHREAD_PROCESS_SHARED 1
+
+#define PTHREAD_CANCEL_DEFERRED 0
+#define PTHREAD_CANCEL_ASYNCHRONOUS 1
+
+#define PTHREAD_CANCEL_ENABLE 0
+#define PTHREAD_CANCEL_DISABLE 1
+
+#define PTHREAD_BARRIER_SERIAL_THREAD 1234567
+
+/*
+ * POSIX 1003.1-2001, section 2.5.9.3: "The symbolic constant
+ * PTHREAD_CANCELED expands to a constant expression of type (void *)
+ * whose value matches no pointer to an object in memory nor the value
+ * NULL."
+ */
+#define PTHREAD_CANCELED ((void *) 1)
+
+#define _POSIX_THREADS
+
+#define PTHREAD_DESTRUCTOR_ITERATIONS 4 /* Min. required */
+#define PTHREAD_KEYS_MAX 256
+#define PTHREAD_STACK_MIN 4096 /* XXX Pulled out of my butt */
+#define PTHREAD_THREADS_MAX 64 /* Min. required */
+
+/*
+ * Mutex attributes.
+ */
+#define PTHREAD_MUTEX_NORMAL 0
+#define PTHREAD_MUTEX_ERRORCHECK 1
+#define PTHREAD_MUTEX_RECURSIVE 2
+#define PTHREAD_MUTEX_DEFAULT PTHREAD_MUTEX_NORMAL
+
+#endif /* _LIB_PTHREAD_H */
diff --git a/lib/libpthread/pthread_alarms.c b/lib/libpthread/pthread_alarms.c
new file mode 100644
index 00000000000..77fdf68ef69
--- /dev/null
+++ b/lib/libpthread/pthread_alarms.c
@@ -0,0 +1,211 @@
+/* $NetBSD: pthread_alarms.c,v 1.2 2003/01/18 10:34:15 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nathan J. Williams.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include <err.h>
+#include <sys/time.h>
+#include <stdlib.h>
+
+#include "pthread.h"
+#include "pthread_int.h"
+
+timer_t pthread_alarmtimer;
+PTQ_HEAD(, pt_alarm_t) pthread_alarmqueue = PTQ_HEAD_INITIALIZER;
+pthread_spin_t pthread_alarmqlock;
+
+#define PTHREAD_ALARM_DEBUG
+
+#ifdef PTHREAD_ALARM_DEBUG
+#define SDPRINTF(x) DPRINTF(x)
+#else
+#define SDPRINTF(x)
+#endif
+
+void
+pthread__alarm_init(void)
+{
+ struct sigevent ev;
+ int retval;
+
+ ev.sigev_notify = SIGEV_SA;
+ ev.sigev_signo = 0;
+ ev.sigev_value.sival_int = PT_ALARMTIMER_MAGIC;
+ retval = timer_create(CLOCK_REALTIME, &ev, &pthread_alarmtimer);
+ if (retval)
+ err(1, "timer_create");
+
+}
+
+void
+pthread__alarm_add(pthread_t self, struct pt_alarm_t *alarm,
+ const struct timespec *ts, void (*func)(void *), void *arg)
+{
+ struct pt_alarm_t *iterator, *prev;
+ struct itimerspec it;
+ int retval;
+
+ pthread_lockinit(&alarm->pta_lock);
+ alarm->pta_time = ts;
+ alarm->pta_func = func;
+ alarm->pta_arg = arg;
+ alarm->pta_fired = 0;
+
+ SDPRINTF(("(alarm_add %p) alarm %p for %d.%06ld\n",
+ self, alarm, ts->tv_sec, ts->tv_nsec/1000));
+ prev = NULL;
+ pthread_spinlock(self, &pthread_alarmqlock);
+ iterator = PTQ_FIRST(&pthread_alarmqueue);
+ PTQ_FOREACH(iterator, &pthread_alarmqueue, pta_next) {
+ if (timespeccmp(ts, iterator->pta_time, <))
+ break;
+ prev = iterator;
+ }
+
+ if (iterator == PTQ_FIRST(&pthread_alarmqueue)) {
+ PTQ_INSERT_HEAD(&pthread_alarmqueue, alarm, pta_next);
+ timespecclear(&it.it_interval);
+ it.it_value = *ts;
+ SDPRINTF(("(add %p) resetting alarm timer to %d.%06d\n",
+ self, it.it_value.tv_sec, it.it_value.tv_nsec/1000));
+ retval = timer_settime(pthread_alarmtimer, TIMER_ABSTIME,
+ &it, NULL);
+ if (retval)
+ err(1, "timer_settime");
+
+ } else {
+ PTQ_INSERT_AFTER(&pthread_alarmqueue, prev, alarm, pta_next);
+ }
+ pthread_spinunlock(self, &pthread_alarmqlock);
+
+}
+
+void
+pthread__alarm_del(pthread_t self, struct pt_alarm_t *alarm)
+{
+ struct pt_alarm_t *next;
+ struct itimerspec it;
+ int retval;
+
+ next = NULL;
+ pthread_spinlock(self, &pthread_alarmqlock);
+ pthread_spinlock(self, &alarm->pta_lock);
+ if (alarm->pta_fired == 0) {
+ if (alarm == PTQ_FIRST(&pthread_alarmqueue)) {
+ next = PTQ_NEXT(alarm, pta_next);
+ timespecclear(&it.it_interval);
+ if (next != NULL)
+ it.it_value = *next->pta_time;
+ else
+ timespecclear(&it.it_value);
+ SDPRINTF(("(del %p) resetting alarm timer to %d.%06d\n",
+ self, it.it_value.tv_sec, it.it_value.tv_nsec/1000));
+ retval = timer_settime(pthread_alarmtimer, TIMER_ABSTIME, &it,
+ NULL);
+ assert(retval == 0);
+ if (retval)
+ err(1, "timer_settime");
+ }
+ PTQ_REMOVE(&pthread_alarmqueue, alarm, pta_next);
+ }
+ pthread_spinunlock(self, &alarm->pta_lock);
+ pthread_spinunlock(self, &pthread_alarmqlock);
+}
+
+int
+pthread__alarm_fired(struct pt_alarm_t *alarm)
+{
+
+ return alarm->pta_fired;
+}
+
+void
+pthread__alarm_process(pthread_t self, void *arg)
+{
+ struct timeval tv;
+ struct timespec ts;
+ struct itimerspec it;
+ struct pt_alarm_t *iterator, *next;
+ PTQ_HEAD(, pt_alarm_t) runq;
+ int retval;
+
+ gettimeofday(&tv, NULL);
+ TIMEVAL_TO_TIMESPEC(&tv, &ts);
+
+ SDPRINTF(("(pro %p) alarm time %d.%09ld\n",
+ self, ts.tv_sec, ts.tv_nsec));
+
+ PTQ_INIT(&runq);
+ pthread_spinlock(self, &pthread_alarmqlock);
+
+ /* 1. Collect a list of all alarms whose time has passed. */
+ for (iterator = next = PTQ_FIRST(&pthread_alarmqueue);
+ iterator;
+ iterator = next) {
+ if (timespeccmp(&ts, iterator->pta_time, <))
+ break;
+ pthread_spinlock(self, &iterator->pta_lock);
+ next = PTQ_NEXT(iterator, pta_next);
+ PTQ_REMOVE(&pthread_alarmqueue, iterator, pta_next);
+ PTQ_INSERT_TAIL(&runq, iterator, pta_next);
+ iterator = next;
+
+ }
+
+ /* 2. Reset the timer for the next element in the queue. */
+ if (next) {
+ timespecclear(&it.it_interval);
+ it.it_value = *next->pta_time;
+ SDPRINTF(("(pro %p) resetting alarm timer to %d.%09d\n", self,
+ it.it_value.tv_sec, it.it_value.tv_nsec));
+ retval = timer_settime(pthread_alarmtimer, TIMER_ABSTIME, &it, NULL);
+ assert(retval == 0);
+ if (retval)
+ err(1, "timer_settime");
+ }
+ pthread_spinunlock(self, &pthread_alarmqlock);
+
+ /* 3. Call the functions for all passed alarms. */
+ PTQ_FOREACH(iterator, &runq, pta_next) {
+ SDPRINTF(("(pro %p) calling function for alarm %p\n", self, iterator));
+ (*iterator->pta_func)(iterator->pta_arg);
+ iterator->pta_fired = 1;
+ pthread_spinunlock(self, &iterator->pta_lock);
+ }
+ SDPRINTF(("(pro %p) done\n", self, iterator));
+
+}
diff --git a/lib/libpthread/pthread_barrier.c b/lib/libpthread/pthread_barrier.c
new file mode 100644
index 00000000000..5ebf6fb28b8
--- /dev/null
+++ b/lib/libpthread/pthread_barrier.c
@@ -0,0 +1,232 @@
+/* $NetBSD: pthread_barrier.c,v 1.2 2003/01/18 10:34:15 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2001, 2003 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nathan J. Williams, and by Jason R. Thorpe.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <sys/cdefs.h>
+
+#include "pthread.h"
+#include "pthread_int.h"
+
+#undef PTHREAD_BARRIER_DEBUG
+
+#ifdef PTHREAD_BARRIER_DEBUG
+#define SDPRINTF(x) DPRINTF(x)
+#else
+#define SDPRINTF(x)
+#endif
+
+int
+pthread_barrier_init(pthread_barrier_t *barrier,
+ const pthread_barrierattr_t *attr, unsigned int count)
+{
+ pthread_t self;
+
+#ifdef ERRORCHECK
+ if ((barrier == NULL) ||
+ (attr && (attr->ptba_magic != _PT_BARRIERATTR_MAGIC)))
+ return EINVAL;
+#endif
+
+ if (count == 0)
+ return EINVAL;
+
+ self = pthread__self();
+
+ if (barrier->ptb_magic == _PT_BARRIER_MAGIC) {
+ /*
+ * We're simply reinitializing the barrier to a
+ * new count.
+ */
+ pthread_spinlock(self, &barrier->ptb_lock);
+
+ if (barrier->ptb_magic != _PT_BARRIER_MAGIC) {
+ pthread_spinunlock(self, &barrier->ptb_lock);
+ return EINVAL;
+ }
+
+ if (!PTQ_EMPTY(&barrier->ptb_waiters)) {
+ pthread_spinunlock(self, &barrier->ptb_lock);
+ return EBUSY;
+ }
+
+ barrier->ptb_initcount = count;
+ barrier->ptb_curcount = 0;
+
+ pthread_spinunlock(self, &barrier->ptb_lock);
+
+ return 0;
+ }
+
+ barrier->ptb_magic = _PT_BARRIER_MAGIC;
+ pthread_lockinit(&barrier->ptb_lock);
+ PTQ_INIT(&barrier->ptb_waiters);
+ barrier->ptb_initcount = count;
+ barrier->ptb_curcount = 0;
+
+ return 0;
+}
+
+
+int
+pthread_barrier_destroy(pthread_barrier_t *barrier)
+{
+ pthread_t self;
+
+#ifdef ERRORCHECK
+ if ((barrier == NULL) || (barrier->ptb_magic != _PT_BARRIER_MAGIC))
+ return EINVAL;
+#endif
+
+ self = pthread__self();
+
+ pthread_spinlock(self, &barrier->ptb_lock);
+
+ if (barrier->ptb_magic != _PT_BARRIER_MAGIC) {
+ pthread_spinunlock(self, &barrier->ptb_lock);
+ return EINVAL;
+ }
+
+ if (!PTQ_EMPTY(&barrier->ptb_waiters)) {
+ pthread_spinunlock(self, &barrier->ptb_lock);
+ return EBUSY;
+ }
+
+ barrier->ptb_magic = _PT_BARRIER_DEAD;
+
+ pthread_spinunlock(self, &barrier->ptb_lock);
+
+ return 0;
+}
+
+
+int
+pthread_barrier_wait(pthread_barrier_t *barrier)
+{
+ pthread_t self;
+
+#ifdef ERRORCHECK
+ if ((barrier == NULL) || (barrier->ptb_magic != _PT_BARRIER_MAGIC))
+ return EINVAL;
+#endif
+ self = pthread__self();
+
+ pthread_spinlock(self, &barrier->ptb_lock);
+
+ /*
+ * A single arbitrary thread is supposed to return
+ * PTHREAD_BARRIER_SERIAL_THREAD, and everone else
+ * is supposed to return 0. Since pthread_barrier_wait()
+ * is not a cancellation point, this is trivial; we
+ * simply elect that the thread that causes the barrier
+ * to be satisfied gets the special return value. Note
+ * that this final thread does not actually need to block,
+ * but instead is responsible for waking everyone else up.
+ */
+ if (barrier->ptb_curcount + 1 == barrier->ptb_initcount) {
+ struct pthread_queue_t blockedq;
+ pthread_t signaled;
+
+ SDPRINTF(("(barrier wait %p) Satisfied %p\n",
+ self, barrier));
+
+ blockedq = barrier->ptb_waiters;
+ PTQ_INIT(&barrier->ptb_waiters);
+ barrier->ptb_curcount = 0;
+
+ PTQ_FOREACH(signaled, &blockedq, pt_sleep)
+ pthread__sched(self, signaled);
+
+ pthread_spinunlock(self, &barrier->ptb_lock);
+
+ return PTHREAD_BARRIER_SERIAL_THREAD;
+ }
+
+ SDPRINTF(("(barrier wait %p) Waiting on %p\n", self, barrier));
+
+ pthread_spinlock(self, &self->pt_statelock);
+
+ self->pt_state = PT_STATE_BLOCKED_QUEUE;
+ self->pt_sleepobj = barrier;
+ self->pt_sleepq = &barrier->ptb_waiters;
+ self->pt_sleeplock = &barrier->ptb_lock;
+
+ pthread_spinunlock(self, &self->pt_statelock);
+
+ PTQ_INSERT_TAIL(&barrier->ptb_waiters, self, pt_sleep);
+ barrier->ptb_curcount++;
+
+ pthread__block(self, &barrier->ptb_lock);
+ /* Spinlock is unlocked on return */
+
+ SDPRINTF(("(barrier wait %p) Woke up on %p\n",
+ self, barrier));
+
+ return 0;
+}
+
+
+int
+pthread_barrierattr_init(pthread_barrierattr_t *attr)
+{
+
+#ifdef ERRORCHECK
+ if (attr == NULL)
+ return EINVAL;
+#endif
+
+ attr->ptba_magic = _PT_BARRIERATTR_MAGIC;
+
+ return 0;
+}
+
+
+int
+pthread_barrierattr_destroy(pthread_barrierattr_t *attr)
+{
+
+#ifdef ERRORCHECK
+ if ((attr == NULL) ||
+ (attr->ptba_magic != _PT_BARRIERATTR_MAGIC))
+ return EINVAL;
+#endif
+
+ attr->ptba_magic = _PT_BARRIERATTR_DEAD;
+
+ return 0;
+}
diff --git a/lib/libpthread/pthread_cancelstub.c b/lib/libpthread/pthread_cancelstub.c
new file mode 100644
index 00000000000..b829127da83
--- /dev/null
+++ b/lib/libpthread/pthread_cancelstub.c
@@ -0,0 +1,344 @@
+/* $NetBSD: pthread_cancelstub.c,v 1.2 2003/01/18 10:34:15 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nathan J. Williams.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/msg.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <stdarg.h>
+#include <unistd.h>
+
+int pthread__cancel_stub_binder;
+
+/*
+ * XXX this is necessary to get the prototypes for the __sigsuspend14
+ * XXX and __msync13 internal names, instead of the application-visible
+ * XXX sigsuspend and msync names. It's kind of gross, but we're pretty
+ * XXX intimate with libc already.
+ */
+#define __LIBC12_SOURCE__
+#include <signal.h>
+#include <sys/mman.h>
+
+#include "pthread.h"
+#include "pthread_int.h"
+
+int _sys_close(int);
+int _sys_fcntl(int, int, ...);
+int _sys_fsync(int);
+ssize_t _sys_msgrcv(int, void *, size_t, long, int);
+int _sys_msgsnd(int, const void *, size_t, int);
+int _sys___msync13(void *, size_t, int);
+int _sys_nanosleep(const struct timespec *, struct timespec *);
+int _sys_open(const char *, int, ...);
+int _sys_poll(struct pollfd *, nfds_t, int);
+ssize_t _sys_pread(int, void *, size_t, off_t);
+ssize_t _sys_pwrite(int, const void *, size_t, off_t);
+ssize_t _sys_read(int, void *, size_t);
+ssize_t _sys_readv(int, const struct iovec *, int);
+int _sys_select(int, fd_set *, fd_set *, fd_set *, struct timeval *);
+int _sys_wait4(pid_t, int *, int, struct rusage *);
+ssize_t _sys_write(int, const void *, size_t);
+ssize_t _sys_writev(int, const struct iovec *, int);
+
+
+int
+close(int d)
+{
+ int retval;
+ pthread_t self;
+
+ self = pthread__self();
+ pthread__testcancel(self);
+ retval = _sys_close(d);
+ pthread__testcancel(self);
+
+ return retval;
+}
+
+int
+fcntl(int fd, int cmd, ...)
+{
+ int retval;
+ pthread_t self;
+ va_list ap;
+
+ self = pthread__self();
+ pthread__testcancel(self);
+ va_start(ap, cmd);
+ retval = _sys_fcntl(fd, cmd, va_arg(ap, void *));
+ va_end(ap);
+ pthread__testcancel(self);
+
+ return retval;
+}
+
+int
+fsync(int d)
+{
+ int retval;
+ pthread_t self;
+
+ self = pthread__self();
+ pthread__testcancel(self);
+ retval = _sys_fsync(d);
+ pthread__testcancel(self);
+
+ return retval;
+}
+
+ssize_t
+msgrcv(int msgid, void *msgp, size_t msgsz, long msgtyp, int msgflg)
+{
+ ssize_t retval;
+ pthread_t self;
+
+ self = pthread__self();
+ pthread__testcancel(self);
+ retval = _sys_msgrcv(msgid, msgp, msgsz, msgtyp, msgflg);
+ pthread__testcancel(self);
+
+ return retval;
+}
+
+int
+msgsnd(int msgid, const void *msgp, size_t msgsz, int msgflg)
+{
+ int retval;
+ pthread_t self;
+
+ self = pthread__self();
+ pthread__testcancel(self);
+ retval = _sys_msgsnd(msgid, msgp, msgsz, msgflg);
+ pthread__testcancel(self);
+
+ return retval;
+}
+
+int
+__msync13(void *addr, size_t len, int flags)
+{
+ int retval;
+ pthread_t self;
+
+ self = pthread__self();
+ pthread__testcancel(self);
+ retval = _sys___msync13(addr, len, flags);
+ pthread__testcancel(self);
+
+ return retval;
+}
+
+int
+nanosleep(const struct timespec *rqtp, struct timespec *rmtp)
+{
+ int retval;
+ pthread_t self;
+
+ self = pthread__self();
+ pthread__testcancel(self);
+ retval = _sys_nanosleep(rqtp, rmtp);
+ pthread__testcancel(self);
+
+ return retval;
+}
+
+int
+open(const char *path, int flags, ...)
+{
+ int retval;
+ pthread_t self;
+ va_list ap;
+
+ self = pthread__self();
+ pthread__testcancel(self);
+ va_start(ap, flags);
+ retval = _sys_open(path, flags, va_arg(ap, mode_t));
+ va_end(ap);
+ pthread__testcancel(self);
+
+ return retval;
+}
+
+int
+poll(struct pollfd *fds, nfds_t nfds, int timeout)
+{
+ int retval;
+ pthread_t self;
+
+ self = pthread__self();
+ pthread__testcancel(self);
+ retval = _sys_poll(fds, nfds, timeout);
+ pthread__testcancel(self);
+
+ return retval;
+}
+
+ssize_t
+pread(int d, void *buf, size_t nbytes, off_t offset)
+{
+ ssize_t retval;
+ pthread_t self;
+
+ self = pthread__self();
+ pthread__testcancel(self);
+ retval = _sys_pread(d, buf, nbytes, offset);
+ pthread__testcancel(self);
+
+ return retval;
+}
+
+ssize_t
+pwrite(int d, const void *buf, size_t nbytes, off_t offset)
+{
+ ssize_t retval;
+ pthread_t self;
+
+ self = pthread__self();
+ pthread__testcancel(self);
+ retval = _sys_pwrite(d, buf, nbytes, offset);
+ pthread__testcancel(self);
+
+ return retval;
+}
+
+ssize_t
+read(int d, void *buf, size_t nbytes)
+{
+ ssize_t retval;
+ pthread_t self;
+
+ self = pthread__self();
+ pthread__testcancel(self);
+ retval = _sys_read(d, buf, nbytes);
+ pthread__testcancel(self);
+
+ return retval;
+}
+
+ssize_t
+readv(int d, const struct iovec *iov, int iovcnt)
+{
+ ssize_t retval;
+ pthread_t self;
+
+ self = pthread__self();
+ pthread__testcancel(self);
+ retval = _sys_readv(d, iov, iovcnt);
+ pthread__testcancel(self);
+
+ return retval;
+}
+
+int
+select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
+ struct timeval *timeout)
+{
+ int retval;
+ pthread_t self;
+
+ self = pthread__self();
+ pthread__testcancel(self);
+ retval = _sys_select(nfds, readfds, writefds, exceptfds, timeout);
+ pthread__testcancel(self);
+
+ return retval;
+}
+
+pid_t
+wait4(pid_t wpid, int *status, int options, struct rusage *rusage)
+{
+ pid_t retval;
+ pthread_t self;
+
+ self = pthread__self();
+ pthread__testcancel(self);
+ retval = _sys_wait4(wpid, status, options, rusage);
+ pthread__testcancel(self);
+
+ return retval;
+}
+
+ssize_t
+write(int d, const void *buf, size_t nbytes)
+{
+ ssize_t retval;
+ pthread_t self;
+
+ self = pthread__self();
+ pthread__testcancel(self);
+ retval = _sys_write(d, buf, nbytes);
+ pthread__testcancel(self);
+
+ return retval;
+}
+
+ssize_t
+writev(int d, const struct iovec *iov, int iovcnt)
+{
+ ssize_t retval;
+ pthread_t self;
+
+ self = pthread__self();
+ pthread__testcancel(self);
+ retval = _sys_writev(d, iov, iovcnt);
+ pthread__testcancel(self);
+
+ return retval;
+}
+
+
+__strong_alias(_close, close)
+__strong_alias(_fcntl, fcntl)
+__strong_alias(_fsync, fsync)
+__strong_alias(_msgrcv, msgrcv)
+__strong_alias(_msgsnd, msgsnd)
+__strong_alias(___msync13, __msync13)
+__strong_alias(_nanosleep, nanosleep)
+__strong_alias(_open, open)
+__strong_alias(_poll, poll)
+__strong_alias(_pread, pread)
+__strong_alias(_pwrite, pwrite)
+__strong_alias(_read, read)
+__strong_alias(_readv, readv)
+__strong_alias(_select, select)
+__strong_alias(_wait4, wait4)
+__strong_alias(_write, write)
+__strong_alias(_writev, writev)
diff --git a/lib/libpthread/pthread_cond.c b/lib/libpthread/pthread_cond.c
new file mode 100644
index 00000000000..b75e8648a75
--- /dev/null
+++ b/lib/libpthread/pthread_cond.c
@@ -0,0 +1,344 @@
+/* $NetBSD: pthread_cond.c,v 1.2 2003/01/18 10:34:15 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nathan J. Williams.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <sys/cdefs.h>
+
+#include "pthread.h"
+#include "pthread_int.h"
+
+#undef PTHREAD_COND_DEBUG
+
+#ifdef PTHREAD_COND_DEBUG
+#define SDPRINTF(x) DPRINTF(x)
+#else
+#define SDPRINTF(x)
+#endif
+
+static void pthread_cond_wait__callback(void *);
+
+__strong_alias(__libc_cond_init,pthread_cond_init)
+__strong_alias(__libc_cond_signal,pthread_cond_signal)
+__strong_alias(__libc_cond_broadcast,pthread_cond_broadcast)
+__strong_alias(__libc_cond_wait,pthread_cond_wait)
+__strong_alias(__libc_cond_timedwait,pthread_cond_timedwait)
+__strong_alias(__libc_cond_destroy,pthread_cond_destroy)
+
+int
+pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr)
+{
+
+#ifdef ERRORCHECK
+ if ((cond == NULL) ||
+ (attr && (attr->ptca_magic != _PT_CONDATTR_MAGIC)))
+ return EINVAL;
+#endif
+
+ cond->ptc_magic = _PT_COND_MAGIC;
+ pthread_lockinit(&cond->ptc_lock);
+ PTQ_INIT(&cond->ptc_waiters);
+ cond->ptc_mutex = NULL;
+
+ return 0;
+}
+
+
+int
+pthread_cond_destroy(pthread_cond_t *cond)
+{
+
+#ifdef ERRORCHECK
+ if ((cond == NULL) || (cond->ptc_magic != _PT_COND_MAGIC) ||
+ (cond->ptc_mutex != NULL) ||
+ (cond->ptc_lock != __SIMPLELOCK_UNLOCKED))
+ return EINVAL;
+#endif
+
+ cond->ptc_magic = _PT_COND_DEAD;
+
+ return 0;
+}
+
+
+int
+pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
+{
+ pthread_t self;
+#ifdef ERRORCHECK
+ if ((cond == NULL) || (cond->ptc_magic != _PT_COND_MAGIC) ||
+ (mutex == NULL) || (mutex->ptm_magic != _PT_MUTEX_MAGIC))
+ return EINVAL;
+#endif
+ self = pthread__self();
+ pthread_spinlock(self, &cond->ptc_lock);
+#ifdef ERRORCHECK
+ if (cond->ptc_mutex == NULL)
+ cond->ptc_mutex = mutex;
+ else {
+ if (cond->ptc_mutex != mutex) {
+ pthread_spinunlock(self, &cond->ptc_lock);
+ return EINVAL;
+ }
+ /* Check the mutex is actually locked */
+ if (mutex->ptm_lock != __SIMPLELOCK_LOCKED) {
+ pthread_spinunlock(self, &cond->ptc_lock);
+ return EPERM;
+ }
+ }
+#endif
+ SDPRINTF(("(cond wait %p) Waiting on %p, mutex %p\n",
+ self, cond, mutex));
+ pthread_spinlock(self, &self->pt_statelock);
+ if (self->pt_cancel) {
+ pthread_spinunlock(self, &self->pt_statelock);
+ pthread_spinunlock(self, &cond->ptc_lock);
+ pthread_exit(PTHREAD_CANCELED);
+ }
+ self->pt_state = PT_STATE_BLOCKED_QUEUE;
+ self->pt_sleepobj = cond;
+ self->pt_sleepq = &cond->ptc_waiters;
+ self->pt_sleeplock = &cond->ptc_lock;
+ pthread_spinunlock(self, &self->pt_statelock);
+
+ PTQ_INSERT_TAIL(&cond->ptc_waiters, self, pt_sleep);
+ pthread_mutex_unlock(mutex);
+
+ pthread__block(self, &cond->ptc_lock);
+ /* Spinlock is unlocked on return */
+ pthread_mutex_lock(mutex);
+ pthread__testcancel(self);
+ SDPRINTF(("(cond wait %p) Woke up on %p, mutex %p\n",
+ self, cond, mutex));
+
+ return 0;
+}
+
+
+struct pthread_cond__waitarg {
+ pthread_t ptw_thread;
+ pthread_cond_t *ptw_cond;
+};
+
+int
+pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
+ const struct timespec *abstime)
+{
+ pthread_t self;
+ struct pthread_cond__waitarg wait;
+ struct pt_alarm_t alarm;
+ int retval;
+
+#ifdef ERRORCHECK
+ if ((cond == NULL) || (cond->ptc_magic != _PT_COND_MAGIC) ||
+ (mutex == NULL) || (mutex->ptm_magic != _PT_MUTEX_MAGIC))
+ return EINVAL;
+ if ((abstime == NULL) || (abstime->tv_sec < 0 ||
+ abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000))
+ return EINVAL;
+#endif
+ self = pthread__self();
+ pthread_spinlock(self, &cond->ptc_lock);
+#ifdef ERRORCHECK
+ if (cond->ptc_mutex == NULL)
+ cond->ptc_mutex = mutex;
+ else {
+ if (cond->ptc_mutex != mutex) {
+ pthread_spinunlock(self, &cond->ptc_lock);
+ return EINVAL;
+ }
+ /* Check the mutex is actually locked */
+ if (mutex->ptm_lock != __SIMPLELOCK_LOCKED) {
+ pthread_spinunlock(self, &cond->ptc_lock);
+ return EPERM;
+ }
+ }
+#endif
+ wait.ptw_thread = self;
+ wait.ptw_cond = cond;
+ retval = 0;
+ SDPRINTF(("(cond timed wait %p) Waiting on %p until %d.%06ld\n",
+ self, cond, abstime->tv_sec, abstime->tv_nsec/1000));
+
+ pthread_spinlock(self, &self->pt_statelock);
+ if (self->pt_cancel) {
+ pthread_spinunlock(self, &self->pt_statelock);
+ pthread_spinunlock(self, &cond->ptc_lock);
+ pthread_exit(PTHREAD_CANCELED);
+ }
+ pthread__alarm_add(self, &alarm, abstime, pthread_cond_wait__callback,
+ &wait);
+ self->pt_state = PT_STATE_BLOCKED_QUEUE;
+ self->pt_sleepobj = cond;
+ self->pt_sleepq = &cond->ptc_waiters;
+ self->pt_sleeplock = &cond->ptc_lock;
+ pthread_spinunlock(self, &self->pt_statelock);
+
+ PTQ_INSERT_TAIL(&cond->ptc_waiters, self, pt_sleep);
+ pthread_mutex_unlock(mutex);
+
+ pthread__block(self, &cond->ptc_lock);
+ /* Spinlock is unlocked on return */
+ SDPRINTF(("(cond timed wait %p) Woke up on %p, mutex %p\n",
+ self, cond));
+ pthread__alarm_del(self, &alarm);
+ if (pthread__alarm_fired(&alarm))
+ retval = ETIMEDOUT;
+ SDPRINTF(("(cond timed wait %p) %s\n",
+ self, (retval == ETIMEDOUT) ? "(timed out)" : ""));
+ pthread_mutex_lock(mutex);
+ pthread__testcancel(self);
+
+
+ return retval;
+}
+
+static void
+pthread_cond_wait__callback(void *arg)
+{
+ struct pthread_cond__waitarg *a;
+ pthread_t self;
+
+ a = arg;
+ self = pthread__self();
+
+ /*
+ * Don't dequeue and schedule the thread if it's already been
+ * queued up by a signal or broadcast (but hasn't yet run as far
+ * as pthread__alarm_del(), or we wouldn't be here, and hence can't
+ * have become blocked on some *other* queue).
+ */
+ pthread_spinlock(self, &a->ptw_cond->ptc_lock);
+ if (a->ptw_thread->pt_state == PT_STATE_BLOCKED_QUEUE) {
+ PTQ_REMOVE(&a->ptw_cond->ptc_waiters, a->ptw_thread, pt_sleep);
+#ifdef ERRORCHECK
+ if (PTQ_EMPTY(&a->ptw_cond->ptc_waiters))
+ a->ptw_cond->ptc_mutex = NULL;
+#endif
+ pthread__sched(self, a->ptw_thread);
+ }
+ pthread_spinunlock(self, &a->ptw_cond->ptc_lock);
+}
+
+int
+pthread_cond_signal(pthread_cond_t *cond)
+{
+ pthread_t self, signaled;
+#ifdef ERRORCHECK
+ if ((cond == NULL) || (cond->ptc_magic != _PT_COND_MAGIC))
+ return EINVAL;
+#endif
+
+ self = pthread__self();
+ SDPRINTF(("(cond signal %p) Signaling %p\n",
+ self, cond));
+
+ pthread_spinlock(self, &cond->ptc_lock);
+ signaled = PTQ_FIRST(&cond->ptc_waiters);
+ if (signaled != NULL)
+ PTQ_REMOVE(&cond->ptc_waiters, signaled, pt_sleep);
+#ifdef ERRORCHECK
+ if (PTQ_EMPTY(&cond->ptc_waiters))
+ cond->ptc_mutex = NULL;
+#endif
+ if (signaled != NULL)
+ pthread__sched(self, signaled);
+ pthread_spinunlock(self, &cond->ptc_lock);
+
+ return 0;
+}
+
+
+int
+pthread_cond_broadcast(pthread_cond_t *cond)
+{
+ pthread_t self, signaled;
+ struct pthread_queue_t blockedq;
+#ifdef ERRORCHECK
+ if ((cond == NULL) || (cond->ptc_magic != _PT_COND_MAGIC))
+ return EINVAL;
+#endif
+
+ self = pthread__self();
+ SDPRINTF(("(cond signal %p) Broadcasting %p\n",
+ self, cond));
+
+ pthread_spinlock(self, &cond->ptc_lock);
+ blockedq = cond->ptc_waiters;
+ PTQ_INIT(&cond->ptc_waiters);
+#ifdef ERRORCHECK
+ cond->ptc_mutex = NULL;
+#endif
+ PTQ_FOREACH(signaled, &blockedq, pt_sleep)
+ pthread__sched(self, signaled);
+ pthread_spinunlock(self, &cond->ptc_lock);
+
+ return 0;
+
+}
+
+
+int
+pthread_condattr_init(pthread_condattr_t *attr)
+{
+
+#ifdef ERRORCHECK
+ if (attr == NULL)
+ return EINVAL;
+#endif
+
+ attr->ptca_magic = _PT_CONDATTR_MAGIC;
+
+ return 0;
+}
+
+
+int
+pthread_condattr_destroy(pthread_condattr_t *attr)
+{
+
+#ifdef ERRORCHECK
+ if ((attr == NULL) ||
+ (attr->ptca_magic != _PT_CONDATTR_MAGIC))
+ return EINVAL;
+#endif
+
+ attr->ptca_magic = _PT_CONDATTR_DEAD;
+
+ return 0;
+}
diff --git a/lib/libpthread/pthread_debug.c b/lib/libpthread/pthread_debug.c
new file mode 100644
index 00000000000..0618e4972e5
--- /dev/null
+++ b/lib/libpthread/pthread_debug.c
@@ -0,0 +1,168 @@
+/* $NetBSD: pthread_debug.c,v 1.2 2003/01/18 10:34:15 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nathan J. Williams.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/shm.h>
+#include <sys/types.h>
+
+#include "pthread.h"
+#include "pthread_int.h"
+
+int pthread__dbg;
+
+#ifdef PTHREAD__DEBUG
+
+int pthread__debug_counters[PTHREADD_NCOUNTERS];
+static struct pthread_msgbuf* debugbuf;
+
+static void pthread__debug_printcounters(void);
+static const char *pthread__counternames[] = PTHREADD_INITCOUNTERNAMES;
+
+void pthread__debug_init(void)
+{
+ time_t t;
+
+ if (getenv("PTHREAD_DEBUGCOUNTERS") != NULL)
+ atexit(pthread__debug_printcounters);
+
+ if (getenv("PTHREAD_DEBUGLOG") != NULL) {
+ t = time(NULL);
+ debugbuf = pthread__debuglog_init(0);
+ DPRINTF(("Started debugging %s (pid %d) at %s\n",
+ getprogname(), getpid(), ctime(&t)));
+ }
+}
+
+static void
+pthread__debug_printcounters(void)
+{
+ int i;
+ int counts[PTHREADD_NCOUNTERS];
+
+ /*
+ * Copy the counters before printing anything to so that we don't see
+ * the effect of printing the counters.
+ * There will still be one more mutex lock than unlock, because
+ * atexit() handling itself will call us back with a mutex locked.
+ */
+ for (i = 0; i < PTHREADD_NCOUNTERS; i++)
+ counts[i] = pthread__debug_counters[i];
+
+ printf("Pthread event counters\n");
+ for (i = 0; i < PTHREADD_NCOUNTERS; i++)
+ printf("%2d %-20s %9d\n", i, pthread__counternames[i],
+ counts[i]);
+ printf("\n");
+}
+
+struct pthread_msgbuf *
+pthread__debuglog_init(int force)
+{
+ int debugshmid;
+ void *debuglog;
+ struct pthread_msgbuf* buf;
+
+ debugshmid = shmget(PTHREAD__DEBUG_SHMKEY, PTHREAD__DEBUG_SHMSIZE,
+ IPC_CREAT | S_IRWXU | S_IRWXG | S_IRWXO);
+
+ if (debugshmid == -1)
+ err(1, "Couldn't get shared debug log");
+
+ debuglog = shmat(debugshmid, 0, 0);
+
+ if (debuglog == (void *)-1)
+ err(1, "Couldn't map shared debug log (ID %d)", debugshmid);
+
+ buf = (struct pthread_msgbuf *)debuglog;
+
+ if (force || buf->msg_magic != BUF_MAGIC) {
+ /* Initialize */
+ buf->msg_magic = BUF_MAGIC;
+ buf->msg_bufw = 0;
+ buf->msg_bufr = 0;
+ buf->msg_bufs = PTHREAD__DEBUG_SHMSIZE -
+ sizeof (struct pthread_msgbuf);
+ }
+
+ return buf;
+}
+
+void
+pthread__debuglog_printf(const char *fmt, ...)
+{
+ char tmpbuf[200];
+ long len, cplen, diff1, diff2;
+ va_list ap;
+
+ if (debugbuf == NULL)
+ return;
+
+ va_start(ap, fmt);
+ len = vsnprintf(tmpbuf, 200, fmt, ap);
+ va_end(ap);
+
+ diff1 = debugbuf->msg_bufw - debugbuf->msg_bufr;
+
+ if (debugbuf->msg_bufw + len > debugbuf->msg_bufs) {
+ cplen = debugbuf->msg_bufs - debugbuf->msg_bufw;
+ memcpy(&debugbuf->msg_bufc[debugbuf->msg_bufw],
+ tmpbuf, cplen);
+ memcpy(&debugbuf->msg_bufc[0], tmpbuf + cplen, len - cplen);
+ debugbuf->msg_bufw = len - cplen;
+ } else {
+ memcpy(&debugbuf->msg_bufc[debugbuf->msg_bufw],
+ tmpbuf, len);
+ debugbuf->msg_bufw += len;
+ }
+
+ diff2 = debugbuf->msg_bufw - debugbuf->msg_bufr;
+
+ /* Check if we've lapped the read pointer and if so advance it. */
+ if (((diff1 < 0) && (diff2 >= 0)) || ((diff1 >= 0) && (diff2 < 0)))
+ debugbuf->msg_bufr = debugbuf->msg_bufw;
+
+}
+#endif
diff --git a/lib/libpthread/pthread_debug.h b/lib/libpthread/pthread_debug.h
new file mode 100644
index 00000000000..ebd7a7be595
--- /dev/null
+++ b/lib/libpthread/pthread_debug.h
@@ -0,0 +1,107 @@
+/* $NetBSD: pthread_debug.h,v 1.2 2003/01/18 10:34:15 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nathan J. Williams.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _LIB_PTHREAD_DEBUG_H
+#define _LIB_PTHREAD_DEBUG_H
+
+#define PTHREADD_CREATE 0
+#define PTHREADD_IDLE 1
+#define PTHREADD_UPCALLS 2
+#define PTHREADD_UP_NEW 3
+#define PTHREADD_UP_BLOCK 4
+#define PTHREADD_UP_UNBLOCK 5
+#define PTHREADD_UP_PREEMPT 6
+#define PTHREADD_UP_SIGNAL 7
+#define PTHREADD_UP_SIGEV 8
+#define PTHREADD_SPINLOCKS 9
+#define PTHREADD_SPINUNLOCKS 10
+#define PTHREADD_SPINPREEMPT 11
+#define PTHREADD_RESOLVELOCKS 12
+#define PTHREADD_SWITCHTO 13
+#define PTHREADD_NCOUNTERS 14
+
+#define PTHREADD_INITCOUNTERNAMES { \
+ "pthread_create()", /* 0 CREATE */ \
+ "pthread_idle()", /* 1 IDLE */ \
+ "upcall", /* 2 UPCALLS */ \
+ "upcall: new", /* 3 UP_NEW */ \
+ "upcall: block", /* 4 UP_BLOCK */ \
+ "upcall: unblock", /* 5 UP_UNBLOCK */ \
+ "upcall: preempt", /* 6 UP_PREEMPT */ \
+ "upcall: signal", /* 7 UP_SIGNAL */ \
+ "upcall: sigev", /* 8 UP_SIGEV */ \
+ "spinlock", /* 9 SPINLOCKS */ \
+ "spinunlock", /* 10 SPINUNLOCKS */ \
+ "spin preemption", /* 11 SPINPREEMPT */ \
+ "resolvelocks", /* 12 RESOLVELOCKS */ \
+ "switchto", /* 13 SPINUNLOCKS */ \
+}
+
+#define PTHREAD__DEBUG_SHMKEY (0x000f)
+#define PTHREAD__DEBUG_SHMSIZE (1<<18)
+
+struct pthread_msgbuf {
+#define BUF_MAGIC 0x090976
+ int msg_magic;
+ long msg_bufw;
+ long msg_bufr;
+ long msg_bufs;
+ char msg_bufc[1];
+};
+
+void pthread__debug_init(void);
+struct pthread_msgbuf* pthread__debuglog_init(int force);
+void pthread__debuglog_printf(const char *fmt, ...);
+
+#ifdef PTHREAD__DEBUG
+
+extern int pthread__debug_counters[PTHREADD_NCOUNTERS];
+
+extern int pthread__dbg; /* Set by libpthread_dbg */
+
+#define PTHREADD_ADD(x) (pthread__debug_counters[(x)]++)
+
+#define DPRINTF(x) pthread__debuglog_printf x
+#else /* PTHREAD_DEBUG */
+
+#define PTHREADD_ADD(x)
+#define DPRINTF(x)
+
+#endif /* PTHREAD_DEBUG */
+
+#endif /* _LIB_PTHREAD_DEBUG_H */
diff --git a/lib/libpthread/pthread_int.h b/lib/libpthread/pthread_int.h
new file mode 100644
index 00000000000..d9ba02c8808
--- /dev/null
+++ b/lib/libpthread/pthread_int.h
@@ -0,0 +1,349 @@
+/* $NetBSD: pthread_int.h,v 1.2 2003/01/18 10:34:15 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nathan J. Williams.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _LIB_PTHREAD_INT_H
+#define _LIB_PTHREAD_INT_H
+
+#define PTHREAD__DEBUG
+#define ERRORCHECK
+
+#include "pthread_types.h"
+#include "pthread_queue.h"
+#include "pthread_debug.h"
+#include "pthread_md.h"
+
+#include <sa.h>
+#include <signal.h>
+
+/*
+ * The size of this structure needs to be no larger than struct
+ * __pthread_cleanup_store, defined in pthread.h.
+ */
+struct pt_clean_t {
+ PTQ_ENTRY(pt_clean_t) ptc_next;
+ void (*ptc_cleanup)(void *);
+ void *ptc_arg;
+};
+
+struct pt_alarm_t {
+ PTQ_ENTRY(pt_alarm_t) pta_next;
+ pthread_spin_t pta_lock;
+ const struct timespec *pta_time;
+ void (*pta_func)(void *);
+ void *pta_arg;
+ int pta_fired;
+};
+
+struct pthread_st {
+ unsigned int pt_magic;
+
+ int pt_type; /* normal, upcall, or idle */
+ int pt_state; /* running, blocked, etc. */
+ pthread_spin_t pt_statelock; /* lock on pt_state */
+ int pt_flags; /* see PT_FLAG_* below */
+ int pt_cancel; /* Deferred cancellation */
+ int pt_spinlocks; /* Number of spinlocks held. */
+ int pt_blockedlwp; /* LWP/SA number when blocked */
+
+ int pt_errno; /* Thread-specific errno. */
+
+ /* Entry on the run queue */
+ PTQ_ENTRY(pthread_st) pt_runq;
+ /* Entry on the list of all threads */
+ PTQ_ENTRY(pthread_st) pt_allq;
+ /* Entry on the sleep queue (xxx should be same as run queue?) */
+ PTQ_ENTRY(pthread_st) pt_sleep;
+ /* Object we're sleeping on */
+ void *pt_sleepobj;
+ /* Queue we're sleeping on */
+ struct pthread_queue_t *pt_sleepq;
+ /* Lock protecting that queue */
+ pthread_spin_t *pt_sleeplock;
+
+ stack_t pt_stack; /* Our stack */
+ ucontext_t *pt_uc; /* Saved context when we're stopped */
+
+ sigset_t pt_sigmask; /* Signals we won't take. */
+ sigset_t pt_siglist; /* Signals pending for us. */
+ pthread_spin_t pt_siglock; /* Lock on above */
+
+ void * pt_exitval; /* Read by pthread_join() */
+
+ /* Stack of cancellation cleanup handlers and their arguments */
+ PTQ_HEAD(, pt_clean_t) pt_cleanup_stack;
+
+ /* Other threads trying to pthread_join() us. */
+ struct pthread_queue_t pt_joiners;
+ /* Lock for above, and for changing pt_state to ZOMBIE or DEAD,
+ * and for setting the DETACHED flag
+ */
+ pthread_spin_t pt_join_lock;
+
+ /* Thread we were going to switch to before we were preempted
+ * ourselves. Will be used by the upcall that's continuing us.
+ */
+ pthread_t pt_switchto;
+ ucontext_t* pt_switchtouc;
+
+ /* The context we saved in pthread__locked_switch but which
+ * was trashed when we were preempted before switching stacks.
+ */
+ ucontext_t* pt_sleepuc;
+
+ /* Threads that are preempted with spinlocks held will be
+ * continued until they unlock their spinlock. When they do
+ * so, they should jump ship to the thread pointed to by
+ * pt_next.
+ */
+ pthread_t pt_next;
+
+ /* The upcall that is continuing this thread */
+ pthread_t pt_parent;
+
+ /* A queue lock that this thread held while trying to
+ * context switch to another process.
+ */
+ pthread_spin_t* pt_heldlock;
+
+ /* Identifier, for debugging. */
+ int pt_num;
+
+ /* Thread-specific data */
+ void* pt_specific[PTHREAD_KEYS_MAX];
+
+#ifdef PTHREAD__DEBUG
+ int blocks;
+ int preempts;
+ int rescheds;
+#endif
+};
+
+struct pthread_lock_ops {
+ void (*plo_init)(__cpu_simple_lock_t *);
+ int (*plo_try)(__cpu_simple_lock_t *);
+ void (*plo_unlock)(__cpu_simple_lock_t *);
+};
+
+/* Thread types */
+#define PT_THREAD_NORMAL 1
+#define PT_THREAD_UPCALL 2
+#define PT_THREAD_IDLE 3
+
+/* Thread states */
+#define PT_STATE_RUNNING 1
+#define PT_STATE_RUNNABLE 2
+#define PT_STATE_BLOCKED_SYS 3
+#define PT_STATE_BLOCKED_QUEUE 4
+#define PT_STATE_ZOMBIE 5
+#define PT_STATE_DEAD 6
+#define PT_STATE_RECYCLABLE 7
+
+/* Flag values */
+
+#define PT_FLAG_DETACHED 0x0001
+#define PT_FLAG_IDLED 0x0002
+#define PT_FLAG_CS_DISABLED 0x0004 /* Cancellation disabled */
+#define PT_FLAG_CS_ASYNC 0x0008 /* Cancellation is async */
+#define PT_FLAG_CS_PENDING 0x0010
+#define PT_FLAG_SIGCATCH 0x0020 /* Interrupt sleep on a signal */
+
+#define PT_MAGIC 0x11110001
+#define PT_DEAD 0xDEAD0001
+
+#define PT_ATTR_MAGIC 0x22220002
+#define PT_ATTR_DEAD 0xDEAD0002
+
+#define PT_STACKSIZE (1<<18)
+#define PT_STACKMASK (PT_STACKSIZE-1)
+
+#define PT_UPCALLSTACKS 16
+
+#define PT_ALARMTIMER_MAGIC 0x88880010
+#define PT_RRTIMER_MAGIC 0x88880020
+#define NIDLETHREADS 4
+#define IDLESPINS 1000
+
+/* Flag to be used in a ucontext_t's uc_flags indicating that
+ * the saved register state is "user" state only, not full
+ * trap state.
+ */
+#define _UC_USER_BIT 30
+#define _UC_USER (1LU << _UC_USER_BIT)
+
+void pthread_init(void) __attribute__ ((__constructor__));
+
+/* Utility functions */
+
+/* Set up/clean up a thread's basic state. */
+void pthread__initthread(pthread_t self, pthread_t t);
+
+/* Go do something else. Don't go back on the run queue */
+void pthread__block(pthread_t self, pthread_spin_t* queuelock);
+/* Put a thread back on the run queue */
+void pthread__sched(pthread_t self, pthread_t thread);
+void pthread__sched_idle(pthread_t self, pthread_t thread);
+void pthread__sched_idle2(pthread_t self);
+
+void pthread__sched_bulk(pthread_t self, pthread_t qhead);
+
+void pthread__idle(void);
+
+/* Get the next thread */
+pthread_t pthread__next(pthread_t self);
+
+int pthread__stackalloc(pthread_t *t);
+void pthread__initmain(pthread_t *t);
+
+void pthread__sa_start(void);
+void pthread__sa_recycle(pthread_t old, pthread_t new);
+
+/* Alarm code */
+void pthread__alarm_init(void);
+void pthread__alarm_add(pthread_t, struct pt_alarm_t *,
+ const struct timespec *, void (*)(void *), void *);
+void pthread__alarm_del(pthread_t, struct pt_alarm_t *);
+int pthread__alarm_fired(struct pt_alarm_t *);
+void pthread__alarm_process(pthread_t self, void *arg);
+
+/* Internal locking primitives */
+void pthread__lockprim_init(void);
+void pthread_lockinit(pthread_spin_t *lock);
+void pthread_spinlock(pthread_t thread, pthread_spin_t *lock);
+int pthread_spintrylock(pthread_t thread, pthread_spin_t *lock);
+void pthread_spinunlock(pthread_t thread, pthread_spin_t *lock);
+
+extern const struct pthread_lock_ops *pthread__lock_ops;
+
+#define pthread__simple_lock_init(alp) (*pthread__lock_ops->plo_init)(alp)
+#define pthread__simple_lock_try(alp) (*pthread__lock_ops->plo_try)(alp)
+#define pthread__simple_unlock(alp) (*pthread__lock_ops->plo_unlock)(alp)
+
+#ifndef _getcontext_u
+int _getcontext_u(ucontext_t *);
+#endif
+#ifndef _setcontext_u
+int _setcontext_u(const ucontext_t *);
+#endif
+#ifndef _swapcontext_u
+int _swapcontext_u(ucontext_t *, const ucontext_t *);
+#endif
+
+void pthread__testcancel(pthread_t self);
+int pthread__find(pthread_t self, pthread_t target);
+
+#ifndef PTHREAD_MD_INIT
+#define PTHREAD_MD_INIT
+#endif
+
+#ifndef _INITCONTEXT_U_MD
+#define _INITCONTEXT_U_MD(ucp)
+#endif
+
+#define _INITCONTEXT_U(ucp) do { \
+ (ucp)->uc_flags = _UC_CPU | _UC_STACK; \
+ _INITCONTEXT_U_MD(ucp) \
+ } while (0)
+
+#ifdef __PTHREAD_SIGNAL_PRIVATE
+
+/*
+ * Macros for converting from ucontext to sigcontext and vice-versa.
+ * Note that going from sigcontext->ucontext is only safe for a
+ * sigcontext that was first created from a ucontext.
+ *
+ * Arch-specific code can override this, if necessary. It may also
+ * be necessary for arch-specific code to include extra info along with
+ * the sigcontext.
+ */
+#ifndef PTHREAD_SIGCONTEXT_EXTRA
+#define PTHREAD_SIGCONTEXT_EXTRA
+#endif
+
+struct pthread__sigcontext {
+ struct sigcontext psc_context;
+ PTHREAD_SIGCONTEXT_EXTRA
+};
+
+#ifndef PTHREAD_UCONTEXT_TO_SIGCONTEXT
+#define PTHREAD_UCONTEXT_TO_SIGCONTEXT(mask, uc, psc) \
+do { \
+ (uc)->uc_sigmask = *(mask); \
+ /* \
+ * XXX We may want to check for _UC_USER here and do a \
+ * XXX _INITCONTEXT_U_MD() and clearing _UC_USER on such \
+ * XXX contexts before converting to a signcontext, thus \
+ * XXX allowing signal handlers to modify the non-_UC_USER \
+ * XXX registers. Hazy territory; ignore it for now. \
+ */ \
+ _UCONTEXT_TO_SIGCONTEXT((uc), &(psc)->psc_context); \
+} while (/*CONSTCOND*/0)
+
+#define PTHREAD_SIGCONTEXT_TO_UCONTEXT(psc, uc) \
+do { \
+ _SIGCONTEXT_TO_UCONTEXT(&(psc)->psc_context, (uc)); \
+ (uc)->uc_flags &= ~_UC_SIGMASK; \
+} while (/*CONSTCOND*/0)
+#else
+void pthread__ucontext_to_sigcontext(const sigset_t *, ucontext_t *,
+ struct pthread__sigcontext *);
+void pthread__sigcontext_to_ucontext(const struct pthread__sigcontext *,
+ ucontext_t *);
+#endif /* PTHREAD_UCONTEXT_TO_SIGCONTEXT */
+
+#endif /* __PTHREAD_SIGNAL_PRIVATE */
+
+/* Stack location of pointer to a particular thread */
+#define pthread__id(sp) \
+ ((pthread_t) (((vaddr_t)(sp)) & ~PT_STACKMASK))
+
+#define pthread__self() (pthread__id(pthread__sp()))
+
+/* These three routines are defined in processor-specific code. */
+void pthread__upcall_switch(pthread_t self, pthread_t next);
+void pthread__switch(pthread_t self, pthread_t next);
+void pthread__locked_switch(pthread_t self, pthread_t next,
+ pthread_spin_t *lock);
+
+void pthread__signal_init(void);
+
+void pthread__signal(pthread_t t, int sig, int code);
+
+void pthread__destroy_tsd(pthread_t self);
+
+
+#endif /* _LIB_PTHREAD_INT_H */
diff --git a/lib/libpthread/pthread_lock.c b/lib/libpthread/pthread_lock.c
new file mode 100644
index 00000000000..9e56c905ee7
--- /dev/null
+++ b/lib/libpthread/pthread_lock.c
@@ -0,0 +1,368 @@
+/* $NetBSD: pthread_lock.c,v 1.2 2003/01/18 10:34:15 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nathan J. Williams.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/ras.h>
+#include <sys/sysctl.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "pthread.h"
+#include "pthread_int.h"
+
+#undef PTHREAD_SPIN_DEBUG
+
+#ifdef PTHREAD_SPIN_DEBUG
+#define SDPRINTF(x) DPRINTF(x)
+#else
+#define SDPRINTF(x)
+#endif
+
+/* How many times to try before checking whether we've been continued. */
+#define NSPINS 1 /* no point in actually spinning until MP works */
+
+static int nspins = NSPINS;
+
+extern char pthread__lock_ras_start[], pthread__lock_ras_end[];
+
+static void
+pthread__ras_simple_lock_init(__cpu_simple_lock_t *alp)
+{
+
+ *alp = __SIMPLELOCK_UNLOCKED;
+}
+
+static int
+pthread__ras_simple_lock_try(__cpu_simple_lock_t *alp)
+{
+ __cpu_simple_lock_t old;
+
+ /* This is the atomic sequence. */
+ __asm __volatile("pthread__lock_ras_start:");
+ old = *alp;
+ *alp = __SIMPLELOCK_LOCKED;
+ __asm __volatile("pthread__lock_ras_end:");
+
+ return (old == __SIMPLELOCK_UNLOCKED);
+}
+
+static void
+pthread__ras_simple_unlock(__cpu_simple_lock_t *alp)
+{
+
+ *alp = __SIMPLELOCK_UNLOCKED;
+}
+
+static const struct pthread_lock_ops pthread__lock_ops_ras = {
+ pthread__ras_simple_lock_init,
+ pthread__ras_simple_lock_try,
+ pthread__ras_simple_unlock,
+};
+
+static void
+pthread__atomic_simple_lock_init(__cpu_simple_lock_t *alp)
+{
+
+ __cpu_simple_lock_init(alp);
+}
+
+static int
+pthread__atomic_simple_lock_try(__cpu_simple_lock_t *alp)
+{
+
+ return (__cpu_simple_lock_try(alp));
+}
+
+static void
+pthread__atomic_simple_unlock(__cpu_simple_lock_t *alp)
+{
+
+ __cpu_simple_unlock(alp);
+}
+
+static const struct pthread_lock_ops pthread__lock_ops_atomic = {
+ pthread__atomic_simple_lock_init,
+ pthread__atomic_simple_lock_try,
+ pthread__atomic_simple_unlock,
+};
+
+/*
+ * We default to pointing to the RAS primitives; we might need to use
+ * locks early, but before main() starts. This is safe, since no other
+ * threads will be active for the process, so atomicity will not be
+ * required.
+ */
+const struct pthread_lock_ops *pthread__lock_ops = &pthread__lock_ops_ras;
+
+/*
+ * Initialize the locking primitives. On uniprocessors, we always
+ * use Restartable Atomic Sequences if they are available. Otherwise,
+ * we fall back onto machine-dependent atomic lock primitives.
+ */
+void
+pthread__lockprim_init(void)
+{
+ int mib[2];
+ size_t len;
+ int ncpu;
+
+ mib[0] = CTL_HW;
+ mib[1] = HW_NCPU;
+
+ len = sizeof(ncpu);
+ sysctl(mib, 2, &ncpu, &len, NULL, 0);
+
+ if (ncpu == 1 &&
+ rasctl(pthread__lock_ras_start,
+ (caddr_t)pthread__lock_ras_end -
+ (caddr_t)pthread__lock_ras_start, RAS_INSTALL) == 0) {
+ pthread__lock_ops = &pthread__lock_ops_ras;
+ return;
+ }
+
+ pthread__lock_ops = &pthread__lock_ops_atomic;
+}
+
+void
+pthread_lockinit(pthread_spin_t *lock)
+{
+
+ pthread__simple_lock_init(lock);
+}
+
+void
+pthread_spinlock(pthread_t thread, pthread_spin_t *lock)
+{
+ int count, ret;
+
+ count = nspins;
+ SDPRINTF(("(pthread_spinlock %p) incrementing spinlock %p (count %d)\n",
+ thread, lock, thread->pt_spinlocks));
+#ifdef PTHREAD_SPIN_DEBUG
+ if(!(thread->pt_spinlocks >= 0)) {
+ (void)kill(getpid(), SIGABRT);
+ _exit(1);
+ }
+#endif
+ ++thread->pt_spinlocks;
+
+ do {
+ while (((ret = pthread__simple_lock_try(lock)) == 0) && --count)
+ ;
+
+ if (ret == 1)
+ break;
+
+ SDPRINTF(("(pthread_spinlock %p) decrementing spinlock %p (count %d)\n",
+ thread, lock, thread->pt_spinlocks));
+ --thread->pt_spinlocks;
+
+ /*
+ * We may be preempted while spinning. If so, we will
+ * be restarted here if thread->pt_spinlocks is
+ * nonzero, which can happen if:
+ * a) we just got the lock
+ * b) we haven't yet decremented the lock count.
+ * If we're at this point, (b) applies. Therefore,
+ * check if we're being continued, and if so, bail.
+ * (in case (a), we should let the code finish and
+ * we will bail out in pthread_spinunlock()).
+ */
+ if (thread->pt_next != NULL) {
+ PTHREADD_ADD(PTHREADD_SPINPREEMPT);
+ pthread__switch(thread, thread->pt_next);
+ }
+ /* try again */
+ count = nspins;
+ SDPRINTF(("(pthread_spinlock %p) incrementing spinlock from %d\n",
+ thread, thread->pt_spinlocks));
+ ++thread->pt_spinlocks;
+ } while (/*CONSTCOND*/1);
+
+ PTHREADD_ADD(PTHREADD_SPINLOCKS);
+ /* Got it! We're out of here. */
+}
+
+
+int
+pthread_spintrylock(pthread_t thread, pthread_spin_t *lock)
+{
+ int ret;
+
+ SDPRINTF(("(pthread_spinlock %p) incrementing spinlock from %d\n",
+ thread, thread->pt_spinlocks));
+ ++thread->pt_spinlocks;
+
+ ret = pthread__simple_lock_try(lock);
+
+ if (ret == 0) {
+ SDPRINTF(("(pthread_spintrylock %p) decrementing spinlock from %d\n",
+ thread, thread->pt_spinlocks));
+ --thread->pt_spinlocks;
+ /* See above. */
+ if (thread->pt_next != NULL) {
+ PTHREADD_ADD(PTHREADD_SPINPREEMPT);
+ pthread__switch(thread, thread->pt_next);
+ }
+ }
+
+ return ret;
+}
+
+
+void
+pthread_spinunlock(pthread_t thread, pthread_spin_t *lock)
+{
+
+ pthread__simple_unlock(lock);
+ SDPRINTF(("(pthread_spinunlock %p) decrementing spinlock %p (count %d)\n",
+ thread, lock, thread->pt_spinlocks));
+ --thread->pt_spinlocks;
+#ifdef PTHREAD_SPIN_DEBUG
+ if (!(thread->pt_spinlocks >= 0)) {
+ (void)kill(getpid(), SIGABRT);
+ _exit(1);
+ }
+#endif
+ PTHREADD_ADD(PTHREADD_SPINUNLOCKS);
+
+ /*
+ * If we were preempted while holding a spinlock, the
+ * scheduler will notice this and continue us. To be good
+ * citzens, we must now get out of here if that was our
+ * last spinlock.
+ * XXX when will we ever have more than one?
+ */
+
+ if ((thread->pt_spinlocks == 0) && (thread->pt_next != NULL)) {
+ PTHREADD_ADD(PTHREADD_SPINPREEMPT);
+ pthread__switch(thread, thread->pt_next);
+ }
+}
+
+
+/*
+ * Public (POSIX-specified) spinlocks.
+ * These don't interact with the spin-preemption code, nor do they
+ * perform any adaptive sleeping.
+ */
+
+int
+pthread_spin_init(pthread_spinlock_t *lock, int pshared)
+{
+
+#ifdef ERRORCHECK
+ if ((lock == NULL) ||
+ ((pshared != PTHREAD_PROCESS_PRIVATE) &&
+ (pshared != PTHREAD_PROCESS_SHARED)))
+ return EINVAL;
+#endif
+ lock->pts_magic = _PT_SPINLOCK_MAGIC;
+ /*
+ * We don't actually use the pshared flag for anything;
+ * cpu simple locks have all the process-shared properties
+ * that we want anyway.
+ */
+ lock->pts_flags = pshared;
+ pthread_lockinit(&lock->pts_spin);
+
+ return 0;
+}
+
+int
+pthread_spin_destroy(pthread_spinlock_t *lock)
+{
+
+#ifdef ERRORCHECK
+ if ((lock == NULL) || (lock->pts_magic != _PT_SPINLOCK_MAGIC))
+ return EINVAL;
+
+ if (lock->pts_spin != __SIMPLELOCK_UNLOCKED)
+ return EBUSY;
+#endif
+
+ lock->pts_magic = _PT_SPINLOCK_DEAD;
+
+ return 0;
+}
+
+int
+pthread_spin_lock(pthread_spinlock_t *lock)
+{
+
+#ifdef ERRORCHECK
+ if ((lock == NULL) || (lock->pts_magic != _PT_SPINLOCK_MAGIC))
+ return EINVAL;
+#endif
+
+ while (pthread__simple_lock_try(&lock->pts_spin) == 0)
+ /* spin */ ;
+
+ return 0;
+}
+
+int
+pthread_spin_trylock(pthread_spinlock_t *lock)
+{
+
+#ifdef ERRORCHECK
+ if ((lock == NULL) || (lock->pts_magic != _PT_SPINLOCK_MAGIC))
+ return EINVAL;
+#endif
+
+ if (pthread__simple_lock_try(&lock->pts_spin) == 0)
+ return EBUSY;
+
+ return 0;
+}
+
+int
+pthread_spin_unlock(pthread_spinlock_t *lock)
+{
+
+#ifdef ERRORCHECK
+ if ((lock == NULL) || (lock->pts_magic != _PT_SPINLOCK_MAGIC))
+ return EINVAL;
+#endif
+
+ pthread__simple_unlock(&lock->pts_spin);
+
+ return 0;
+}
diff --git a/lib/libpthread/pthread_mutex.c b/lib/libpthread/pthread_mutex.c
new file mode 100644
index 00000000000..0005f61b1e4
--- /dev/null
+++ b/lib/libpthread/pthread_mutex.c
@@ -0,0 +1,458 @@
+/* $NetBSD: pthread_mutex.c,v 1.2 2003/01/18 10:34:16 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2001, 2003 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nathan J. Williams, and by Jason R. Thorpe.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#include "pthread.h"
+#include "pthread_int.h"
+
+static int pthread_mutex_lock_slow(pthread_mutex_t *);
+
+__strong_alias(__libc_mutex_init,pthread_mutex_init)
+__strong_alias(__libc_mutex_lock,pthread_mutex_lock)
+__strong_alias(__libc_mutex_trylock,pthread_mutex_trylock)
+__strong_alias(__libc_mutex_unlock,pthread_mutex_unlock)
+__strong_alias(__libc_mutex_destroy,pthread_mutex_destroy)
+
+__strong_alias(__libc_thr_once,pthread_once)
+
+struct mutex_private {
+ int type;
+ int recursecount;
+};
+
+static const struct mutex_private mutex_private_default = {
+ PTHREAD_MUTEX_DEFAULT,
+ 0,
+};
+
+struct mutexattr_private {
+ int type;
+};
+
+static const struct mutexattr_private mutexattr_private_default = {
+ PTHREAD_MUTEX_DEFAULT,
+};
+
+/*
+ * If the mutex does not already have private data (i.e. was statically
+ * initialized), then give it the default.
+ */
+#define GET_MUTEX_PRIVATE(mutex, mp) \
+do { \
+ if (__predict_false((mp = (mutex)->ptm_private) == NULL)) { \
+ /* LINTED cast away const */ \
+ mp = ((mutex)->ptm_private = \
+ (void *)&mutex_private_default); \
+ } \
+} while (/*CONSTCOND*/0)
+
+int
+pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr)
+{
+ struct mutexattr_private *map;
+ struct mutex_private *mp;
+
+#ifdef ERRORCHECK
+ if ((mutex == NULL) ||
+ (attr && (attr->ptma_magic != _PT_MUTEXATTR_MAGIC)))
+ return EINVAL;
+#endif
+
+ if (attr != NULL && (map = attr->ptma_private) != NULL &&
+ memcmp(map, &mutexattr_private_default, sizeof(*map)) != 0) {
+ mp = malloc(sizeof(*mp));
+ if (mp == NULL)
+ return ENOMEM;
+
+ mp->type = map->type;
+ mp->recursecount = 0;
+ } else {
+ /* LINTED cast away const */
+ mp = (struct mutex_private *) &mutex_private_default;
+ }
+
+ mutex->ptm_magic = _PT_MUTEX_MAGIC;
+ mutex->ptm_owner = NULL;
+ pthread_lockinit(&mutex->ptm_lock);
+ pthread_lockinit(&mutex->ptm_interlock);
+ PTQ_INIT(&mutex->ptm_blocked);
+ mutex->ptm_private = mp;
+
+ return 0;
+}
+
+
+int
+pthread_mutex_destroy(pthread_mutex_t *mutex)
+{
+
+#ifdef ERRORCHECK
+ if ((mutex == NULL) ||
+ (mutex->ptm_magic != _PT_MUTEX_MAGIC) ||
+ (mutex->ptm_lock != __SIMPLELOCK_UNLOCKED))
+ return EINVAL;
+#endif
+
+ mutex->ptm_magic = _PT_MUTEX_DEAD;
+ if (mutex->ptm_private != NULL &&
+ mutex->ptm_private != (void *)&mutex_private_default)
+ free(mutex->ptm_private);
+
+ return 0;
+}
+
+
+/*
+ * Note regarding memory visibility: Pthreads has rules about memory
+ * visibility and mutexes. Very roughly: Memory a thread can see when
+ * it unlocks a mutex can be seen by another thread that locks the
+ * same mutex.
+ *
+ * A memory barrier after a lock and before an unlock will provide
+ * this behavior. This code relies on pthread__simple_lock_try() to issue
+ * a barrier after obtaining a lock, and on pthread__simple_unlock() to
+ * issue a barrier before releasing a lock.
+ */
+
+int
+pthread_mutex_lock(pthread_mutex_t *mutex)
+{
+ int error;
+
+#ifdef ERRORCHECK
+ if ((mutex == NULL) || (mutex->ptm_magic != _PT_MUTEX_MAGIC))
+ return EINVAL;
+#endif
+
+ /*
+ * Note that if we get the lock, we don't have to deal with any
+ * non-default lock type handling.
+ */
+ if (__predict_false(pthread__simple_lock_try(&mutex->ptm_lock) == 0)) {
+ error = pthread_mutex_lock_slow(mutex);
+ if (error)
+ return error;
+ }
+
+ /* We have the lock! */
+ mutex->ptm_owner = pthread__self();
+
+ return 0;
+}
+
+
+static int
+pthread_mutex_lock_slow(pthread_mutex_t *mutex)
+{
+ pthread_t self;
+
+ self = pthread__self();
+
+ while (/*CONSTCOND*/1) {
+ if (pthread__simple_lock_try(&mutex->ptm_lock))
+ break; /* got it! */
+
+ /* Okay, didn't look free. Get the interlock... */
+ pthread_spinlock(self, &mutex->ptm_interlock);
+ /*
+ * The mutex_unlock routine will get the interlock
+ * before looking at the list of sleepers, so if the
+ * lock is held we can safely put ourselves on the
+ * sleep queue. If it's not held, we can try taking it
+ * again.
+ */
+ if (mutex->ptm_lock == __SIMPLELOCK_LOCKED) {
+ struct mutex_private *mp;
+
+ GET_MUTEX_PRIVATE(mutex, mp);
+
+ if (mutex->ptm_owner == self) {
+ switch (mp->type) {
+ case PTHREAD_MUTEX_ERRORCHECK:
+ pthread_spinunlock(self,
+ &mutex->ptm_interlock);
+ return EDEADLK;
+
+ case PTHREAD_MUTEX_RECURSIVE:
+ /*
+ * It's safe to do this without
+ * holding the interlock, because
+ * we only modify it if we know we
+ * own the mutex.
+ */
+ pthread_spinunlock(self,
+ &mutex->ptm_interlock);
+ if (mp->recursecount == INT_MAX)
+ return EAGAIN;
+ mp->recursecount++;
+ return 0;
+ }
+ }
+
+ PTQ_INSERT_TAIL(&mutex->ptm_blocked, self, pt_sleep);
+ /*
+ * Locking a mutex is not a cancellation
+ * point, so we don't need to do the
+ * test-cancellation dance. We may get woken
+ * up spuriously by pthread_cancel, though,
+ * but it's okay since we're just going to
+ * retry.
+ */
+ pthread_spinlock(self, &self->pt_statelock);
+ self->pt_state = PT_STATE_BLOCKED_QUEUE;
+ self->pt_sleepobj = mutex;
+ self->pt_sleepq = &mutex->ptm_blocked;
+ self->pt_sleeplock = &mutex->ptm_interlock;
+ pthread_spinunlock(self, &self->pt_statelock);
+
+ pthread__block(self, &mutex->ptm_interlock);
+ /* interlock is not held when we return */
+ } else {
+ pthread_spinunlock(self, &mutex->ptm_interlock);
+ }
+ /* Go around for another try. */
+ }
+
+ return 0;
+}
+
+
+int
+pthread_mutex_trylock(pthread_mutex_t *mutex)
+{
+ pthread_t self = pthread__self();
+
+#ifdef ERRORCHECK
+ if ((mutex == NULL) || (mutex->ptm_magic != _PT_MUTEX_MAGIC))
+ return EINVAL;
+#endif
+
+ if (pthread__simple_lock_try(&mutex->ptm_lock) == 0) {
+ struct mutex_private *mp;
+
+ GET_MUTEX_PRIVATE(mutex, mp);
+
+ /*
+ * These tests can be performed without holding the
+ * interlock because these fields are only modified
+ * if we know we own the mutex.
+ */
+ if (mutex->ptm_owner == self) {
+ switch (mp->type) {
+ case PTHREAD_MUTEX_ERRORCHECK:
+ return EDEADLK;
+
+ case PTHREAD_MUTEX_RECURSIVE:
+ if (mp->recursecount == INT_MAX)
+ return EAGAIN;
+ mp->recursecount++;
+ return 0;
+ }
+ }
+
+ return EBUSY;
+ }
+
+ mutex->ptm_owner = self;
+
+ return 0;
+}
+
+
+int
+pthread_mutex_unlock(pthread_mutex_t *mutex)
+{
+ struct mutex_private *mp;
+ pthread_t self, blocked;
+
+ self = pthread__self();
+
+#ifdef ERRORCHECK
+ if ((mutex == NULL) || (mutex->ptm_magic != _PT_MUTEX_MAGIC))
+ return EINVAL;
+
+ if (mutex->ptm_lock != __SIMPLELOCK_LOCKED)
+ return EPERM; /* Not exactly the right error. */
+#endif
+
+ GET_MUTEX_PRIVATE(mutex, mp);
+
+ /*
+ * These tests can be performed without holding the
+ * interlock because these fields are only modified
+ * if we know we own the mutex.
+ */
+ switch (mp->type) {
+ case PTHREAD_MUTEX_ERRORCHECK:
+ if (mutex->ptm_owner != self)
+ return EPERM;
+ break;
+
+ case PTHREAD_MUTEX_RECURSIVE:
+ if (mutex->ptm_owner != self)
+ return EPERM;
+ if (mp->recursecount != 0) {
+ mp->recursecount--;
+ return 0;
+ }
+ break;
+ }
+
+ pthread_spinlock(self, &mutex->ptm_interlock);
+ blocked = PTQ_FIRST(&mutex->ptm_blocked);
+ if (blocked)
+ PTQ_REMOVE(&mutex->ptm_blocked, blocked, pt_sleep);
+ mutex->ptm_owner = NULL;
+ pthread__simple_unlock(&mutex->ptm_lock);
+ pthread_spinunlock(self, &mutex->ptm_interlock);
+
+ /* Give the head of the blocked queue another try. */
+ if (blocked)
+ pthread__sched(self, blocked);
+
+ return 0;
+}
+
+int
+pthread_mutexattr_init(pthread_mutexattr_t *attr)
+{
+ struct mutexattr_private *map;
+
+#ifdef ERRORCHECK
+ if (attr == NULL)
+ return EINVAL;
+#endif
+
+ map = malloc(sizeof(*map));
+ if (map == NULL)
+ return ENOMEM;
+
+ *map = mutexattr_private_default;
+
+ attr->ptma_magic = _PT_MUTEXATTR_MAGIC;
+ attr->ptma_private = map;
+
+ return 0;
+}
+
+
+int
+pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
+{
+
+#ifdef ERRORCHECK
+ if ((attr == NULL) ||
+ (attr->ptma_magic != _PT_MUTEXATTR_MAGIC))
+ return EINVAL;
+#endif
+
+ attr->ptma_magic = _PT_MUTEXATTR_DEAD;
+ if (attr->ptma_private != NULL)
+ free(attr->ptma_private);
+
+ return 0;
+}
+
+
+int
+pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *typep)
+{
+ struct mutexattr_private *map;
+
+#ifdef ERRORCHECK
+ if ((attr == NULL) ||
+ (attr->ptma_magic != _PT_MUTEXATTR_MAGIC) ||
+ (typep == NULL))
+ return EINVAL;
+#endif
+
+ map = attr->ptma_private;
+
+ *typep = map->type;
+
+ return 0;
+}
+
+
+int
+pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type)
+{
+ struct mutexattr_private *map;
+
+#ifdef ERRORCHECK
+ if ((attr == NULL) ||
+ (attr->ptma_magic != _PT_MUTEXATTR_MAGIC))
+ return EINVAL;
+#endif
+ map = attr->ptma_private;
+
+ switch (type) {
+ case PTHREAD_MUTEX_NORMAL:
+ case PTHREAD_MUTEX_ERRORCHECK:
+ case PTHREAD_MUTEX_RECURSIVE:
+ map->type = type;
+ break;
+
+ default:
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+
+int
+pthread_once(pthread_once_t *once_control, void (*routine)(void))
+{
+
+ if (once_control->pto_done == 0) {
+ pthread_mutex_lock(&once_control->pto_mutex);
+ if (once_control->pto_done == 0) {
+ routine();
+ once_control->pto_done = 1;
+ }
+ pthread_mutex_unlock(&once_control->pto_mutex);
+ }
+
+ return 0;
+}
diff --git a/lib/libpthread/pthread_queue.h b/lib/libpthread/pthread_queue.h
new file mode 100644
index 00000000000..0004a208735
--- /dev/null
+++ b/lib/libpthread/pthread_queue.h
@@ -0,0 +1,140 @@
+/* $NetBSD: pthread_queue.h,v 1.2 2003/01/18 10:34:16 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nathan J. Williams.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* pthread_queue.h
+ * Definition of a queue interface for the pthread library.
+ * Style modeled on the sys/queue.h macros; implementation taken from
+ * the tail queue, with the added property of static initializability
+ * (and a corresponding extra cost in the _INSERT_TAIL() function.
+*/
+
+
+
+/*
+ * Queue definitions.
+ */
+#define PTQ_HEAD(name, type) \
+struct name { \
+ struct type *ptqh_first;/* first element */ \
+ struct type **ptqh_last;/* addr of last next element */ \
+}
+
+#define PTQ_HEAD_INITIALIZER { NULL, NULL }
+
+
+#define PTQ_ENTRY(type) \
+struct { \
+ struct type *ptqe_next; /* next element */ \
+ struct type **ptqe_prev;/* address of previous next element */ \
+}
+
+/*
+ * Queue functions.
+ */
+
+#define PTQ_INIT(head) do { \
+ (head)->ptqh_first = NULL; \
+ (head)->ptqh_last = &(head)->ptqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define PTQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.ptqe_next = (head)->ptqh_first) != NULL) \
+ (head)->ptqh_first->field.ptqe_prev = \
+ &(elm)->field.ptqe_next; \
+ else \
+ (head)->ptqh_last = &(elm)->field.ptqe_next; \
+ (head)->ptqh_first = (elm); \
+ (elm)->field.ptqe_prev = &(head)->ptqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define PTQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.ptqe_next = NULL; \
+ if ((head)->ptqh_last == NULL) \
+ (head)->ptqh_last = &(head)->ptqh_first; \
+ (elm)->field.ptqe_prev = (head)->ptqh_last; \
+ *(head)->ptqh_last = (elm); \
+ (head)->ptqh_last = &(elm)->field.ptqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define PTQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.ptqe_next = (listelm)->field.ptqe_next) != NULL)\
+ (elm)->field.ptqe_next->field.ptqe_prev = \
+ &(elm)->field.ptqe_next; \
+ else \
+ (head)->ptqh_last = &(elm)->field.ptqe_next; \
+ (listelm)->field.ptqe_next = (elm); \
+ (elm)->field.ptqe_prev = &(listelm)->field.ptqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define PTQ_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.ptqe_prev = (listelm)->field.ptqe_prev; \
+ (elm)->field.ptqe_next = (listelm); \
+ *(listelm)->field.ptqe_prev = (elm); \
+ (listelm)->field.ptqe_prev = &(elm)->field.ptqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define PTQ_REMOVE(head, elm, field) do { \
+ if (((elm)->field.ptqe_next) != NULL) \
+ (elm)->field.ptqe_next->field.ptqe_prev = \
+ (elm)->field.ptqe_prev; \
+ else \
+ (head)->ptqh_last = (elm)->field.ptqe_prev; \
+ *(elm)->field.ptqe_prev = (elm)->field.ptqe_next; \
+} while (/*CONSTCOND*/0)
+
+/*
+ * Queue access methods.
+ */
+#define PTQ_EMPTY(head) ((head)->ptqh_first == NULL)
+#define PTQ_FIRST(head) ((head)->ptqh_first)
+#define PTQ_NEXT(elm, field) ((elm)->field.ptqe_next)
+
+#define PTQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->ptqh_last))->ptqh_last))
+#define PTQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.ptqe_prev))->ptqh_last))
+
+#define PTQ_FOREACH(var, head, field) \
+ for ((var) = ((head)->ptqh_first); \
+ (var); \
+ (var) = ((var)->field.ptqe_next))
+
+#define PTQ_FOREACH_REVERSE(var, head, headname, field) \
+ for ((var) = (*(((struct headname *)((head)->ptqh_last))->ptqh_last)); \
+ (var); \
+ (var) = (*(((struct headname *)((var)->field.ptqe_prev))->ptqh_last)))
diff --git a/lib/libpthread/pthread_run.c b/lib/libpthread/pthread_run.c
new file mode 100644
index 00000000000..bffd95f6b6f
--- /dev/null
+++ b/lib/libpthread/pthread_run.c
@@ -0,0 +1,229 @@
+/* $NetBSD: pthread_run.c,v 1.2 2003/01/18 10:34:16 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nathan J. Williams.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#include <assert.h>
+#include <ucontext.h>
+
+#include "pthread.h"
+#include "pthread_int.h"
+
+#undef PTHREAD_RUN_DEBUG
+
+#ifdef PTHREAD_RUN_DEBUG
+#define SDPRINTF(x) DPRINTF(x)
+#else
+#define SDPRINTF(x)
+#endif
+
+extern pthread_spin_t pthread__runqueue_lock;
+extern struct pthread_queue_t pthread__runqueue;
+extern struct pthread_queue_t pthread__idlequeue;
+
+extern pthread_spin_t pthread__deadqueue_lock;
+extern struct pthread_queue_t pthread__reidlequeue;
+
+
+int
+sched_yield(void)
+{
+ pthread_t self, next;
+ extern int pthread__started;
+
+ if (pthread__started) {
+ self = pthread__self();
+ SDPRINTF(("(sched_yield %p) yielding\n", self));
+ pthread_spinlock(self, &pthread__runqueue_lock);
+ PTQ_INSERT_TAIL(&pthread__runqueue, self, pt_runq);
+ /*
+ * There will always be at least one thread on the runqueue,
+ * because we just put ourselves there.
+ */
+ next = PTQ_FIRST(&pthread__runqueue);
+ PTQ_REMOVE(&pthread__runqueue, next, pt_runq);
+ next->pt_state = PT_STATE_RUNNING;
+ pthread__locked_switch(self, next, &pthread__runqueue_lock);
+ }
+
+ return 0;
+}
+
+
+/* Go do another thread, without putting us on the run queue. */
+void
+pthread__block(pthread_t self, pthread_spin_t *queuelock)
+{
+ pthread_t next;
+
+ next = pthread__next(self);
+ next->pt_state = PT_STATE_RUNNING;
+ SDPRINTF(("(calling locked_switch %p, %p) spinlock %d, %d\n",
+ self, next, self->pt_spinlocks, next->pt_spinlocks));
+ pthread__locked_switch(self, next, queuelock);
+ SDPRINTF(("(back from locked_switch %p, %p) spinlock %d, %d\n",
+ self, next, self->pt_spinlocks, next->pt_spinlocks));
+}
+
+
+/* Get the next thread to switch to. Will never return NULL. */
+pthread_t
+pthread__next(pthread_t self)
+{
+ pthread_t next;
+
+ pthread_spinlock(self, &pthread__runqueue_lock);
+ next = PTQ_FIRST(&pthread__runqueue);
+ if (next) {
+ assert(next->pt_type == PT_THREAD_NORMAL);
+ PTQ_REMOVE(&pthread__runqueue, next, pt_runq);
+ } else {
+ next = PTQ_FIRST(&pthread__idlequeue);
+ assert(next != 0);
+ PTQ_REMOVE(&pthread__idlequeue, next, pt_runq);
+ assert(next->pt_type == PT_THREAD_IDLE);
+ SDPRINTF(("(next %p) returning idle thread %p\n", self, next));
+ }
+ pthread_spinunlock(self, &pthread__runqueue_lock);
+
+ return next;
+}
+
+
+/* Put a thread back on the run queue */
+void
+pthread__sched(pthread_t self, pthread_t thread)
+{
+
+ SDPRINTF(("(sched %p) scheduling %p\n", self, thread));
+ thread->pt_state = PT_STATE_RUNNABLE;
+ assert (thread->pt_type == PT_THREAD_NORMAL);
+ assert (thread->pt_spinlocks == 0);
+#ifdef PTHREAD__DEBUG
+ thread->rescheds++;
+#endif
+ pthread_spinlock(self, &pthread__runqueue_lock);
+ PTQ_INSERT_TAIL(&pthread__runqueue, thread, pt_runq);
+ pthread_spinunlock(self, &pthread__runqueue_lock);
+}
+
+
+/* Make a thread a candidate idle thread. */
+void
+pthread__sched_idle(pthread_t self, pthread_t thread)
+{
+
+ SDPRINTF(("(sched_idle %p) idling %p\n", self, thread));
+ thread->pt_state = PT_STATE_RUNNABLE;
+
+ thread->pt_flags &= ~PT_FLAG_IDLED;
+ _INITCONTEXT_U(thread->pt_uc);
+ thread->pt_uc->uc_stack = thread->pt_stack;
+ thread->pt_uc->uc_link = NULL;
+ makecontext(thread->pt_uc, pthread__idle, 0);
+
+ pthread_spinlock(self, &pthread__runqueue_lock);
+ PTQ_INSERT_TAIL(&pthread__idlequeue, thread, pt_runq);
+ pthread_spinunlock(self, &pthread__runqueue_lock);
+}
+
+
+void
+pthread__sched_idle2(pthread_t self)
+{
+ pthread_t idlethread, qhead, next;
+
+ qhead = NULL;
+ pthread_spinlock(self, &pthread__deadqueue_lock);
+ idlethread = PTQ_FIRST(&pthread__reidlequeue);
+ while (idlethread != NULL) {
+ SDPRINTF(("(sched_idle2 %p) reidling %p\n", self, idlethread));
+ next = PTQ_NEXT(idlethread, pt_runq);
+ if (idlethread->pt_next == NULL) {
+ PTQ_REMOVE(&pthread__reidlequeue, idlethread, pt_runq);
+ idlethread->pt_next = qhead;
+ qhead = idlethread;
+ } else {
+ SDPRINTF(("(sched_idle2 %p) %p is in a preemption loop\n", self, idlethread));
+ }
+ idlethread = next;
+ }
+ pthread_spinunlock(self, &pthread__deadqueue_lock);
+
+ if (qhead)
+ pthread__sched_bulk(self, qhead);
+}
+
+/*
+ * Put a series of threads, linked by their pt_next fields, onto the
+ * run queue.
+ */
+void
+pthread__sched_bulk(pthread_t self, pthread_t qhead)
+{
+ pthread_t next;
+
+ pthread_spinlock(self, &pthread__runqueue_lock);
+ for ( ; qhead && (qhead != self) ; qhead = next) {
+ next = qhead->pt_next;
+ assert (qhead->pt_spinlocks == 0);
+ if (qhead->pt_type == PT_THREAD_NORMAL) {
+ qhead->pt_state = PT_STATE_RUNNABLE;
+ qhead->pt_next = NULL;
+ qhead->pt_parent = NULL;
+#ifdef PTHREAD__DEBUG
+ qhead->rescheds++;
+#endif
+ SDPRINTF(("(bulk %p) scheduling %p\n", self, qhead));
+ assert(PTQ_LAST(&pthread__runqueue, pthread_queue_t) != qhead);
+ assert(PTQ_FIRST(&pthread__runqueue) != qhead);
+ PTQ_INSERT_TAIL(&pthread__runqueue, qhead, pt_runq);
+ } else if (qhead->pt_type == PT_THREAD_IDLE) {
+ qhead->pt_state = PT_STATE_RUNNABLE;
+ qhead->pt_flags &= ~PT_FLAG_IDLED;
+ qhead->pt_next = NULL;
+ qhead->pt_parent = NULL;
+ _INITCONTEXT_U(qhead->pt_uc);
+ qhead->pt_uc->uc_stack = qhead->pt_stack;
+ qhead->pt_uc->uc_link = NULL;
+ makecontext(qhead->pt_uc, pthread__idle, 0);
+ SDPRINTF(("(bulk %p) idling %p\n", self, qhead));
+ PTQ_INSERT_TAIL(&pthread__idlequeue, qhead, pt_runq);
+ }
+ }
+ pthread_spinunlock(self, &pthread__runqueue_lock);
+}
diff --git a/lib/libpthread/pthread_rwlock.c b/lib/libpthread/pthread_rwlock.c
new file mode 100644
index 00000000000..2911583b798
--- /dev/null
+++ b/lib/libpthread/pthread_rwlock.c
@@ -0,0 +1,455 @@
+/* $NetBSD: pthread_rwlock.c,v 1.2 2003/01/18 10:34:16 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nathan J. Williams.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <sys/cdefs.h>
+
+#include "pthread.h"
+#include "pthread_int.h"
+
+static void pthread_rwlock__callback(void *);
+
+__strong_alias(__libc_rwlock_init,pthread_rwlock_init)
+__strong_alias(__libc_rwlock_rdlock,pthread_rwlock_rdlock)
+__strong_alias(__libc_rwlock_wrlock,pthread_rwlock_wrlock)
+__strong_alias(__libc_rwlock_tryrdlock,pthread_rwlock_tryrdlock)
+__strong_alias(__libc_rwlock_trywrlock,pthread_rwlock_trywrlock)
+__strong_alias(__libc_rwlock_unlock,pthread_rwlock_unlock)
+__strong_alias(__libc_rwlock_destroy,pthread_rwlock_destroy)
+
+int
+pthread_rwlock_init(pthread_rwlock_t *rwlock,
+ const pthread_rwlockattr_t *attr)
+{
+#ifdef ERRORCHECK
+ if ((rwlock == NULL) ||
+ (attr && (attr->ptra_magic != _PT_RWLOCKATTR_MAGIC)))
+ return EINVAL;
+#endif
+ rwlock->ptr_magic = _PT_RWLOCK_MAGIC;
+ pthread_lockinit(&rwlock->ptr_interlock);
+ PTQ_INIT(&rwlock->ptr_rblocked);
+ PTQ_INIT(&rwlock->ptr_wblocked);
+ rwlock->ptr_nreaders = 0;
+ rwlock->ptr_writer = NULL;
+
+ return 0;
+}
+
+
+int
+pthread_rwlock_destroy(pthread_rwlock_t *rwlock)
+{
+#ifdef ERRORCHECK
+ if ((rwlock == NULL) ||
+ (rwlock->ptr_magic != _PT_RWLOCK_MAGIC) ||
+ (!PTQ_EMPTY(&rwlock->ptr_rblocked)) ||
+ (!PTQ_EMPTY(&rwlock->ptr_wblocked)) ||
+ (rwlock->ptr_nreaders != 0) ||
+ (rwlock->ptr_writer != NULL))
+ return EINVAL;
+#endif
+ rwlock->ptr_magic = _PT_RWLOCK_DEAD;
+
+ return 0;
+}
+
+
+int
+pthread_rwlock_rdlock(pthread_rwlock_t *rwlock)
+{
+ pthread_t self;
+#ifdef ERRORCHECK
+ if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
+ return EINVAL;
+#endif
+ self = pthread__self();
+
+ pthread_spinlock(self, &rwlock->ptr_interlock);
+#ifdef ERRORCHECK
+ if (rwlock->ptr_writer == self) {
+ pthread_spinunlock(self, &rwlock->ptr_interlock);
+ return EDEADLK;
+ }
+#endif
+ /*
+ * Don't get a readlock if there is a writer or if there are waiting
+ * writers; i.e. prefer writers to readers. This strategy is dictated
+ * by SUSv3.
+ */
+ while ((rwlock->ptr_writer != NULL) ||
+ (!PTQ_EMPTY(&rwlock->ptr_wblocked))) {
+ PTQ_INSERT_TAIL(&rwlock->ptr_rblocked, self, pt_sleep);
+ /* Locking a rwlock is not a cancellation point; don't check */
+ pthread_spinlock(self, &self->pt_statelock);
+ self->pt_state = PT_STATE_BLOCKED_QUEUE;
+ self->pt_sleepobj = rwlock;
+ self->pt_sleepq = &rwlock->ptr_rblocked;
+ self->pt_sleeplock = &rwlock->ptr_interlock;
+ pthread_spinunlock(self, &self->pt_statelock);
+ pthread__block(self, &rwlock->ptr_interlock);
+ /* interlock is not held when we return */
+ pthread_spinlock(self, &rwlock->ptr_interlock);
+ }
+
+ rwlock->ptr_nreaders++;
+ pthread_spinunlock(self, &rwlock->ptr_interlock);
+
+ return 0;
+}
+
+
+int
+pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock)
+{
+ pthread_t self;
+#ifdef ERRORCHECK
+ if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
+ return EINVAL;
+#endif
+ self = pthread__self();
+
+ pthread_spinlock(self, &rwlock->ptr_interlock);
+#ifdef ERRORCHECK
+ if (rwlock->ptr_writer == self) {
+ pthread_spinunlock(self, &rwlock->ptr_interlock);
+ return EDEADLK;
+ }
+#endif
+ /*
+ * Don't get a readlock if there is a writer or if there are waiting
+ * writers; i.e. prefer writers to readers. This strategy is dictated
+ * by SUSv3.
+ */
+ if ((rwlock->ptr_writer != NULL) ||
+ (!PTQ_EMPTY(&rwlock->ptr_wblocked))) {
+ pthread_spinunlock(self, &rwlock->ptr_interlock);
+ return EBUSY;
+ }
+
+ rwlock->ptr_nreaders++;
+ pthread_spinunlock(self, &rwlock->ptr_interlock);
+
+ return 0;
+}
+
+
+int
+pthread_rwlock_wrlock(pthread_rwlock_t *rwlock)
+{
+ pthread_t self;
+#ifdef ERRORCHECK
+ if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
+ return EINVAL;
+#endif
+ self = pthread__self();
+
+ pthread_spinlock(self, &rwlock->ptr_interlock);
+ /*
+ * Prefer writers to readers here; permit writers even if there are
+ * waiting readers.
+ */
+ while ((rwlock->ptr_nreaders > 0) || (rwlock->ptr_writer != NULL)) {
+ PTQ_INSERT_TAIL(&rwlock->ptr_wblocked, self, pt_sleep);
+ /* Locking a rwlock is not a cancellation point; don't check */
+ pthread_spinlock(self, &self->pt_statelock);
+ self->pt_state = PT_STATE_BLOCKED_QUEUE;
+ self->pt_sleepobj = rwlock;
+ self->pt_sleepq = &rwlock->ptr_wblocked;
+ self->pt_sleeplock = &rwlock->ptr_interlock;
+ pthread_spinunlock(self, &self->pt_statelock);
+ pthread__block(self, &rwlock->ptr_interlock);
+ /* interlock is not held when we return */
+ pthread_spinlock(self, &rwlock->ptr_interlock);
+ }
+
+ rwlock->ptr_writer = self;
+ pthread_spinunlock(self, &rwlock->ptr_interlock);
+
+ return 0;
+}
+
+
+int
+pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock)
+{
+ pthread_t self;
+#ifdef ERRORCHECK
+ if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
+ return EINVAL;
+#endif
+ self = pthread__self();
+
+ pthread_spinlock(self, &rwlock->ptr_interlock);
+ /*
+ * Prefer writers to readers here; permit writers even if there are
+ * waiting readers.
+ */
+ if ((rwlock->ptr_nreaders > 0) || (rwlock->ptr_writer != NULL)) {
+ pthread_spinunlock(self, &rwlock->ptr_interlock);
+ return EBUSY;
+ }
+
+ rwlock->ptr_writer = self;
+ pthread_spinunlock(self, &rwlock->ptr_interlock);
+
+ return 0;
+}
+
+
+struct pthread_rwlock__waitarg {
+ pthread_t ptw_thread;
+ pthread_rwlock_t *ptw_rwlock;
+ struct pthread_queue_t *ptw_queue;
+};
+
+int
+pthread_rwlock_timedrdlock(pthread_rwlock_t *rwlock,
+ const struct timespec *abs_timeout)
+{
+ pthread_t self;
+ struct pthread_rwlock__waitarg wait;
+ struct pt_alarm_t alarm;
+ int retval;
+#ifdef ERRORCHECK
+ if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
+ return EINVAL;
+ if ((abs_timeout == NULL) || (abs_timeout->tv_nsec >= 1000000000))
+ return EINVAL;
+#endif
+ self = pthread__self();
+
+ pthread_spinlock(self, &rwlock->ptr_interlock);
+#ifdef ERRORCHECK
+ if (rwlock->ptr_writer == self) {
+ pthread_spinlock(self, &rwlock->ptr_interlock);
+ return EDEADLK;
+ }
+#endif
+ /*
+ * Don't get a readlock if there is a writer or if there are waiting
+ * writers; i.e. prefer writers to readers. This strategy is dictated
+ * by SUSv3.
+ */
+ retval = 0;
+ while ((retval == 0) && ((rwlock->ptr_writer != NULL) ||
+ (!PTQ_EMPTY(&rwlock->ptr_wblocked)))) {
+ wait.ptw_thread = self;
+ wait.ptw_rwlock = rwlock;
+ wait.ptw_queue = &rwlock->ptr_rblocked;
+ pthread__alarm_add(self, &alarm, abs_timeout,
+ pthread_rwlock__callback, &wait);
+ PTQ_INSERT_TAIL(&rwlock->ptr_rblocked, self, pt_sleep);
+ /* Locking a rwlock is not a cancellation point; don't check */
+ pthread_spinlock(self, &self->pt_statelock);
+ self->pt_state = PT_STATE_BLOCKED_QUEUE;
+ self->pt_sleepobj = rwlock;
+ self->pt_sleepq = &rwlock->ptr_rblocked;
+ self->pt_sleeplock = &rwlock->ptr_interlock;
+ pthread_spinunlock(self, &self->pt_statelock);
+ pthread__block(self, &rwlock->ptr_interlock);
+ /* interlock is not held when we return */
+ pthread__alarm_del(self, &alarm);
+ if (pthread__alarm_fired(&alarm))
+ retval = ETIMEDOUT;
+ pthread_spinlock(self, &rwlock->ptr_interlock);
+ }
+
+ if (retval == 0)
+ rwlock->ptr_nreaders++;
+ pthread_spinunlock(self, &rwlock->ptr_interlock);
+
+ return retval;
+}
+
+
+int
+pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlock,
+ const struct timespec *abs_timeout)
+{
+ struct pthread_rwlock__waitarg wait;
+ struct pt_alarm_t alarm;
+ int retval;
+ pthread_t self;
+#ifdef ERRORCHECK
+ if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
+ return EINVAL;
+#endif
+ self = pthread__self();
+
+ pthread_spinlock(self, &rwlock->ptr_interlock);
+ /*
+ * Prefer writers to readers here; permit writers even if there are
+ * waiting readers.
+ */
+ retval = 0;
+ while (retval == 0 &&
+ ((rwlock->ptr_nreaders > 0) || (rwlock->ptr_writer != NULL))) {
+ wait.ptw_thread = self;
+ wait.ptw_rwlock = rwlock;
+ wait.ptw_queue = &rwlock->ptr_wblocked;
+ pthread__alarm_add(self, &alarm, abs_timeout,
+ pthread_rwlock__callback, &wait);
+ PTQ_INSERT_TAIL(&rwlock->ptr_wblocked, self, pt_sleep);
+ /* Locking a rwlock is not a cancellation point; don't check */
+ pthread_spinlock(self, &self->pt_statelock);
+ self->pt_state = PT_STATE_BLOCKED_QUEUE;
+ self->pt_sleepobj = rwlock;
+ self->pt_sleepq = &rwlock->ptr_wblocked;
+ self->pt_sleeplock = &rwlock->ptr_interlock;
+ pthread_spinunlock(self, &self->pt_statelock);
+ pthread__block(self, &rwlock->ptr_interlock);
+ /* interlock is not held when we return */
+ pthread__alarm_del(self, &alarm);
+ if (pthread__alarm_fired(&alarm))
+ retval = ETIMEDOUT;
+ pthread_spinlock(self, &rwlock->ptr_interlock);
+ }
+
+ if (retval == 0)
+ rwlock->ptr_writer = self;
+ pthread_spinunlock(self, &rwlock->ptr_interlock);
+
+ return 0;
+}
+
+
+static void
+pthread_rwlock__callback(void *arg)
+{
+ struct pthread_rwlock__waitarg *a;
+ pthread_t self;
+
+ a = arg;
+ self = pthread__self();
+
+ pthread_spinlock(self, &a->ptw_rwlock->ptr_interlock);
+ /*
+ * Don't dequeue and schedule the thread if it's already been
+ * queued up by a signal or broadcast (but hasn't yet run as far
+ * as pthread__alarm_del(), or we wouldn't be here, and hence can't
+ * have become blocked on some *other* queue).
+ */
+ if (a->ptw_thread->pt_state == PT_STATE_BLOCKED_QUEUE) {
+ PTQ_REMOVE(a->ptw_queue, a->ptw_thread, pt_sleep);
+ pthread__sched(self, a->ptw_thread);
+ }
+ pthread_spinunlock(self, &a->ptw_rwlock->ptr_interlock);
+
+}
+
+
+int
+pthread_rwlock_unlock(pthread_rwlock_t *rwlock)
+{
+ pthread_t self, reader, writer;
+ struct pthread_queue_t blockedq;
+#ifdef ERRORCHECK
+ if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
+ return EINVAL;
+#endif
+ writer = NULL;
+ PTQ_INIT(&blockedq);
+ self = pthread__self();
+
+ pthread_spinlock(self, &rwlock->ptr_interlock);
+ if (rwlock->ptr_writer != NULL) {
+ /* Releasing a write lock. */
+#ifdef ERRORCHECK
+ if (rwlock->ptr_writer != self) {
+ pthread_spinunlock(self, &rwlock->ptr_interlock);
+ return EPERM;
+ }
+#endif
+ rwlock->ptr_writer = NULL;
+ writer = PTQ_FIRST(&rwlock->ptr_wblocked);
+ if (writer != NULL) {
+ PTQ_REMOVE(&rwlock->ptr_wblocked, writer, pt_sleep);
+ } else {
+ blockedq = rwlock->ptr_rblocked;
+ PTQ_INIT(&rwlock->ptr_rblocked);
+ }
+ } else {
+ /* Releasing a read lock. */
+ rwlock->ptr_nreaders--;
+ if (rwlock->ptr_nreaders == 0) {
+ writer = PTQ_FIRST(&rwlock->ptr_wblocked);
+ if (writer != NULL)
+ PTQ_REMOVE(&rwlock->ptr_wblocked, writer,
+ pt_sleep);
+ }
+ }
+
+ pthread_spinunlock(self, &rwlock->ptr_interlock);
+
+ if (writer != NULL)
+ pthread__sched(self, writer);
+ else
+ PTQ_FOREACH(reader, &blockedq, pt_sleep)
+ pthread__sched(self, reader);
+
+ return 0;
+}
+
+
+int
+pthread_rwlockattr_init(pthread_rwlockattr_t *attr)
+{
+#ifdef ERRORCHECK
+ if (attr == NULL)
+ return EINVAL;
+#endif
+ attr->ptra_magic = _PT_RWLOCKATTR_MAGIC;
+
+ return 0;
+}
+
+
+int
+pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr)
+{
+#ifdef ERRORCHECK
+ if ((attr == NULL) ||
+ (attr->ptra_magic != _PT_RWLOCKATTR_MAGIC))
+ return EINVAL;
+#endif
+ attr->ptra_magic = _PT_RWLOCKATTR_DEAD;
+
+ return 0;
+}
diff --git a/lib/libpthread/pthread_sa.c b/lib/libpthread/pthread_sa.c
new file mode 100644
index 00000000000..d9dd233ea63
--- /dev/null
+++ b/lib/libpthread/pthread_sa.c
@@ -0,0 +1,734 @@
+/* $NetBSD: pthread_sa.c,v 1.2 2003/01/18 10:34:16 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nathan J. Williams.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <lwp.h>
+#include <sa.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ucontext.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#include "pthread.h"
+#include "pthread_int.h"
+
+#define PTHREAD_SA_DEBUG
+
+#ifdef PTHREAD_SA_DEBUG
+#define SDPRINTF(x) DPRINTF(x)
+#else
+#define SDPRINTF(x)
+#endif
+
+extern struct pthread_queue_t pthread__allqueue;
+
+static stack_t recyclable[2][(PT_UPCALLSTACKS/2)+1];
+static int recycle_count;
+static int recycle_threshold;
+static int recycle_side;
+static pthread_spin_t recycle_lock;
+
+#define PTHREAD_RRTIMER_INTERVAL_DEFAULT 100
+static pthread_mutex_t rrtimer_mutex = PTHREAD_MUTEX_INITIALIZER;
+static timer_t pthread_rrtimer;
+static int pthread_rrtimer_interval = PTHREAD_RRTIMER_INTERVAL_DEFAULT;
+
+int pthread__maxlwps;
+
+
+#define pthread__sa_id(sap) (pthread__id((sap)->sa_context))
+
+void pthread__upcall(int type, struct sa_t *sas[], int ev, int intr,
+ void *arg);
+int pthread__find_interrupted(struct sa_t *sas[], int nsas, pthread_t *qhead,
+ pthread_t self);
+void pthread__resolve_locks(pthread_t self, pthread_t *interrupted);
+void pthread__recycle_bulk(pthread_t self, pthread_t qhead);
+
+void
+pthread__upcall(int type, struct sa_t *sas[], int ev, int intr, void *arg)
+{
+ pthread_t t, self, next, intqueue;
+ int first = 1;
+ int deliversig = 0, runalarms = 0;
+ siginfo_t *si;
+
+ PTHREADD_ADD(PTHREADD_UPCALLS);
+
+ self = pthread__self();
+ self->pt_state = PT_STATE_RUNNING;
+
+ if (sas[0]->sa_id > pthread__maxlwps)
+ pthread__maxlwps = sas[0]->sa_id;
+ SDPRINTF(("(up %p) type %d LWP %d ev %d intr %d\n", self,
+ type, sas[0]->sa_id, ev ? sas[1]->sa_id : 0,
+ intr ? sas[ev+intr]->sa_id : 0));
+ switch (type) {
+ case SA_UPCALL_BLOCKED:
+ t = pthread__sa_id(sas[1]);
+ pthread_spinlock(self, &t->pt_statelock);
+ t->pt_state = PT_STATE_BLOCKED_SYS;
+ pthread_spinunlock(self, &t->pt_statelock);
+#ifdef PTHREAD__DEBUG
+ t->blocks++;
+#endif
+ t->pt_blockedlwp = sas[1]->sa_id;
+ if (t->pt_cancel)
+ _lwp_wakeup(t->pt_blockedlwp);
+ t->pt_uc = sas[1]->sa_context;
+ first++; /* Don't handle this SA in the usual processing. */
+ PTHREADD_ADD(PTHREADD_UP_BLOCK);
+ break;
+ case SA_UPCALL_NEWPROC:
+ PTHREADD_ADD(PTHREADD_UP_NEW);
+ break;
+ case SA_UPCALL_PREEMPTED:
+ PTHREADD_ADD(PTHREADD_UP_PREEMPT);
+ break;
+ case SA_UPCALL_UNBLOCKED:
+ PTHREADD_ADD(PTHREADD_UP_UNBLOCK);
+ break;
+ case SA_UPCALL_SIGNAL:
+ PTHREADD_ADD(PTHREADD_UP_SIGNAL);
+ deliversig = 1;
+ break;
+ case SA_UPCALL_SIGEV:
+ PTHREADD_ADD(PTHREADD_UP_SIGEV);
+ si = arg;
+ if (si->si_sigval.sival_int == PT_ALARMTIMER_MAGIC)
+ runalarms = 1;
+ /*
+ * PT_RRTIMER_MAGIC doesn't need explicit handling;
+ * the per-thread work below will put the interrupted
+ * thread on the back of the run queue, and
+ * pthread_next() will get one from the front.
+ */
+ break;
+ case SA_UPCALL_USER:
+ /* We don't send ourselves one of these. */
+ default:
+ assert(0);
+ }
+
+ /*
+ * Do per-thread work, including saving the context.
+ * Briefly run any threads that were in a critical section.
+ * This includes any upcalls that have been interupted, so
+ * they can do their own version of this dance.
+ */
+ intqueue = NULL;
+ if ((ev + intr) >= first) {
+ if (pthread__find_interrupted(sas + first, ev + intr,
+ &intqueue, self) > 0)
+ pthread__resolve_locks(self, &intqueue);
+ }
+ pthread__sched_idle2(self);
+ if (intqueue)
+ pthread__sched_bulk(self, intqueue);
+
+
+ /*
+ * Run the alarm queue (handled after lock resolution since
+ * alarm handling requires locks).
+ */
+ if (runalarms)
+ pthread__alarm_process(self, arg);
+
+ /*
+ * Note that we handle signals after handling
+ * spinlock preemption. This is because spinlocks are only
+ * used internally to the thread library and we don't want to
+ * expose the middle of them to a signal. While this means
+ * that synchronous instruction traps that occur inside
+ * critical sections in this library (SIGFPE, SIGILL, SIGBUS,
+ * SIGSEGV) won't be handled at the precise location where
+ * they occured, that's okay, because (1) we don't use any FP
+ * and (2) SIGILL/SIGBUS/SIGSEGV should really just core dump.
+ *
+ * This also means that a thread that was interrupted to take
+ * a signal will be on a run queue, and not in upcall limbo.
+ */
+ if (deliversig) {
+ si = arg;
+ if (ev)
+ pthread__signal(pthread__sa_id(sas[1]), si->si_signo,
+ si->si_code);
+ else
+ pthread__signal(NULL, si->si_signo, si->si_code);
+ }
+
+ /*
+ * At this point everything on our list should be scheduled
+ * (or was an upcall).
+ */
+ assert(self->pt_spinlocks == 0);
+ next = pthread__next(self);
+ next->pt_state = PT_STATE_RUNNING;
+ SDPRINTF(("(up %p) switching to %p (uc: %p pc: %lx)\n",
+ self, next, next->pt_uc, pthread__uc_pc(next->pt_uc)));
+ pthread__upcall_switch(self, next);
+ /* NOTREACHED */
+ assert(0);
+}
+
+/*
+ * Build a chain of the threads that were interrupted by the upcall.
+ * Determine if any of them were upcalls or lock-holders that
+ * need to be continued early.
+ */
+int
+pthread__find_interrupted(struct sa_t *sas[], int nsas, pthread_t *qhead,
+ pthread_t self)
+{
+ int i, resume;
+ pthread_t victim, next;
+
+ resume = 0;
+ next = self;
+
+ for (i = 0; i < nsas; i++) {
+ victim = pthread__sa_id(sas[i]);
+#ifdef PTHREAD__DEBUG
+ victim->preempts++;
+#endif
+ victim->pt_uc = sas[i]->sa_context;
+ victim->pt_uc->uc_flags &= ~_UC_SIGMASK;
+ SDPRINTF(("(fi %p) victim %d %p(%d)", self, i, victim,
+ victim->pt_type));
+ if (victim->pt_type == PT_THREAD_UPCALL) {
+ /* Case 1: Upcall. Must be resumed. */
+ SDPRINTF((" upcall"));
+ resume = 1;
+ if (victim->pt_next) {
+ /*
+ * Case 1A: Upcall in a chain.
+ *
+ * Already part of a chain. We want to
+ * splice this chain into our chain, so
+ * we have to find the root.
+ */
+ SDPRINTF((" chain"));
+ for ( ; victim->pt_parent != NULL;
+ victim = victim->pt_parent) {
+ SDPRINTF((" parent %p", victim->pt_parent));
+ assert(victim->pt_parent != victim);
+ }
+ }
+ } else {
+ /* Case 2: Normal or idle thread. */
+ if (victim->pt_spinlocks > 0) {
+ /* Case 2A: Lockholder. Must be resumed. */
+ SDPRINTF((" lockholder %d",
+ victim->pt_spinlocks));
+ resume = 1;
+ if (victim->pt_next) {
+ /*
+ * Case 2A1: Lockholder on a chain.
+ * Same deal as 1A.
+ */
+ SDPRINTF((" chain"));
+ for ( ; victim->pt_parent != NULL;
+ victim = victim->pt_parent) {
+ SDPRINTF((" parent %p", victim->pt_parent));
+ assert(victim->pt_parent != victim);
+ }
+
+
+ }
+ } else {
+ /* Case 2B: Non-lockholder. */
+ SDPRINTF((" nonlockholder"));
+ if (victim->pt_next) {
+ /*
+ * Case 2B1: Non-lockholder on a chain
+ * (must have just released a lock).
+ */
+ SDPRINTF((" chain"));
+ resume = 1;
+ for ( ; victim->pt_parent != NULL;
+ victim = victim->pt_parent) {
+ SDPRINTF((" parent %p", victim->pt_parent));
+ assert(victim->pt_parent != victim);
+ }
+ } else if (victim->pt_flags & PT_FLAG_IDLED) {
+ /*
+ * Idle threads that have already
+ * idled must be skipped so
+ * that we don't (a) idle-queue them
+ * twice and (b) get the pt_next
+ * queue of threads to put on the run
+ * queue mangled by
+ * pthread__sched_idle2()
+ */
+ SDPRINTF(("\n"));
+ continue;
+ }
+
+ }
+ }
+ assert (victim != self);
+ victim->pt_parent = self;
+ victim->pt_next = next;
+ next = victim;
+ SDPRINTF(("\n"));
+ }
+
+ *qhead = next;
+
+ return resume;
+}
+
+void
+pthread__resolve_locks(pthread_t self, pthread_t *intqueuep)
+{
+ pthread_t victim, prev, next, switchto, runq, recycleq, intqueue;
+ pthread_spin_t *lock;
+
+ PTHREADD_ADD(PTHREADD_RESOLVELOCKS);
+
+ recycleq = NULL;
+ runq = NULL;
+ intqueue = *intqueuep;
+ switchto = NULL;
+ victim = intqueue;
+
+ SDPRINTF(("(rl %p) entered\n", self));
+
+ while (intqueue != self) {
+ /*
+ * Make a pass over the interrupted queue, cleaning out
+ * any threads that have dropped all their locks and any
+ * upcalls that have finished.
+ */
+ SDPRINTF(("(rl %p) intqueue %p\n", self, intqueue));
+ prev = NULL;
+ for (victim = intqueue; victim != self; victim = next) {
+ next = victim->pt_next;
+ SDPRINTF(("(rl %p) victim %p (uc %p)", self,
+ victim, victim->pt_uc));
+ if (victim->pt_switchto) {
+ PTHREADD_ADD(PTHREADD_SWITCHTO);
+ switchto = victim->pt_switchto;
+ switchto->pt_uc = victim->pt_switchtouc;
+ victim->pt_switchto = NULL;
+ victim->pt_switchtouc = NULL;
+ SDPRINTF((" switchto: %p", switchto));
+ }
+
+ if (victim->pt_type == PT_THREAD_NORMAL) {
+ SDPRINTF((" normal"));
+ if (victim->pt_spinlocks == 0) {
+ /*
+ * We can remove this thread
+ * from the interrupted queue.
+ */
+ if (prev)
+ prev->pt_next = next;
+ else
+ intqueue = next;
+ /*
+ * Check whether the victim was
+ * making a locked switch.
+ */
+ if (victim->pt_heldlock) {
+ /*
+ * Yes. Therefore, it's on
+ * some sleep queue and
+ * all we have to do is
+ * release the lock and
+ * restore the real
+ * sleep context.
+ */
+ lock = victim->pt_heldlock;
+ victim->pt_heldlock = NULL;
+ pthread__simple_unlock(lock);
+ victim->pt_uc =
+ victim->pt_sleepuc;
+ victim->pt_sleepuc = NULL;
+ victim->pt_next = NULL;
+ victim->pt_parent = NULL;
+ SDPRINTF((" heldlock: %p",lock));
+ } else {
+ /*
+ * No. Queue it for the
+ * run queue.
+ */
+ victim->pt_next = runq;
+ runq = victim;
+ }
+ } else {
+ SDPRINTF((" spinlocks: %d",
+ victim->pt_spinlocks));
+ /*
+ * Still holding locks.
+ * Leave it in the interrupted queue.
+ */
+ prev = victim;
+ }
+ } else if (victim->pt_type == PT_THREAD_UPCALL) {
+ SDPRINTF((" upcall"));
+ /* Okay, an upcall. */
+ if (victim->pt_state == PT_STATE_RECYCLABLE) {
+ /* We're done with you. */
+ SDPRINTF((" recyclable"));
+ if (prev)
+ prev->pt_next = next;
+ else
+ intqueue = next;
+ victim->pt_next = recycleq;
+ recycleq = victim;
+ } else {
+ /*
+ * Not finished yet.
+ * Leave it in the interrupted queue.
+ */
+ prev = victim;
+ }
+ } else {
+ SDPRINTF((" idle"));
+ /*
+ * Idle threads should be given an opportunity
+ * to put themselves on the reidle queue.
+ * We know that they're done when they have no
+ * locks and PT_FLAG_IDLED is set.
+ */
+ if (victim->pt_spinlocks != 0) {
+ /* Still holding locks. */
+ SDPRINTF((" spinlocks: %d",
+ victim->pt_spinlocks));
+ prev = victim;
+ } else if (!(victim->pt_flags & PT_FLAG_IDLED)) {
+ /*
+ * Hasn't yet put itself on the
+ * reidle queue.
+ */
+ SDPRINTF((" not done"));
+ prev = victim;
+ } else {
+ /* Done! */
+ if (prev)
+ prev->pt_next = next;
+ else
+ intqueue = next;
+ /* Permit moving off the reidlequeue */
+ victim->pt_next = NULL;
+ }
+ }
+
+ if (switchto) {
+ assert(switchto->pt_spinlocks == 0);
+ /*
+ * Threads can have switchto set to themselves
+ * if they hit new_preempt. Don't put them
+ * on the run queue twice.
+ */
+ if (switchto != victim) {
+ switchto->pt_next = runq;
+ runq = switchto;
+ }
+ switchto = NULL;
+ }
+ SDPRINTF(("\n"));
+ }
+
+ if (intqueue != self) {
+ /*
+ * There is a chain. Run through the elements
+ * of the chain. If one of them is preempted again,
+ * the upcall that handles it will have us on its
+ * chain, and we will continue here, having
+ * returned from the switch.
+ */
+ SDPRINTF(("(rl %p) starting chain %p (pc: %lx sp: %lx)\n",
+ self, intqueue, pthread__uc_pc(intqueue->pt_uc),
+ pthread__uc_sp(intqueue->pt_uc)));
+ pthread__switch(self, intqueue);
+ SDPRINTF(("(rl %p) returned from chain\n",
+ self));
+ }
+
+ if (self->pt_next) {
+ /*
+ * We're on a chain ourselves. Let the other
+ * threads in the chain run; our parent upcall
+ * will resume us here after a pass around its
+ * interrupted queue.
+ */
+ SDPRINTF(("(rl %p) upcall chain switch to %p (pc: %lx sp: %lx)\n",
+ self, self->pt_next,
+ pthread__uc_pc(self->pt_next->pt_uc),
+ pthread__uc_sp(self->pt_next->pt_uc)));
+ pthread__switch(self, self->pt_next);
+ }
+
+ }
+
+ /* Recycle upcalls. */
+ pthread__recycle_bulk(self, recycleq);
+ SDPRINTF(("(rl %p) exiting\n", self));
+ *intqueuep = runq;
+}
+
+void
+pthread__recycle_bulk(pthread_t self, pthread_t qhead)
+{
+ int do_recycle, my_side, ret;
+ pthread_t upcall;
+
+ while(qhead != NULL) {
+ pthread_spinlock(self, &recycle_lock);
+ my_side = recycle_side;
+ do_recycle = 0;
+ while ((qhead != NULL) &&
+ (recycle_count < recycle_threshold)) {
+ upcall = qhead;
+ qhead = qhead->pt_next;
+ upcall->pt_state = PT_STATE_RUNNABLE;
+ upcall->pt_next = NULL;
+ upcall->pt_parent = NULL;
+ recyclable[my_side][recycle_count] = upcall->pt_stack;
+ recycle_count++;
+ }
+ SDPRINTF(("(recycle_bulk %p) count %d\n", self, recycle_count));
+ if (recycle_count == recycle_threshold) {
+ recycle_side = 1 - recycle_side;
+ recycle_count = 0;
+ do_recycle = 1;
+ }
+ pthread_spinunlock(self, &recycle_lock);
+ if (do_recycle) {
+ SDPRINTF(("(recycle_bulk %p) recycled %d stacks\n", self, recycle_threshold));
+
+ ret = sa_stacks(recycle_threshold,
+ recyclable[my_side]);
+ if (ret != recycle_threshold) {
+ printf("Error: recycle_threshold\n");
+ printf("ret: %d threshold: %d\n",
+ ret, recycle_threshold);
+
+ assert(0);
+ }
+ }
+ }
+
+}
+
+/*
+ * Stash away an upcall and its stack, possibly recycling it to the kernel.
+ * Must be running in the context of "new".
+ */
+void
+pthread__sa_recycle(pthread_t old, pthread_t new)
+{
+ int do_recycle, my_side, ret;
+
+ do_recycle = 0;
+
+ old->pt_next = NULL;
+ old->pt_parent = NULL;
+ old->pt_state = PT_STATE_RUNNABLE;
+
+ pthread_spinlock(new, &recycle_lock);
+
+ my_side = recycle_side;
+ recyclable[my_side][recycle_count] = old->pt_stack;
+ recycle_count++;
+ SDPRINTF(("(recycle %p) count %d\n", new, recycle_count));
+
+ if (recycle_count == recycle_threshold) {
+ /* Switch */
+ recycle_side = 1 - recycle_side;
+ recycle_count = 0;
+ do_recycle = 1;
+ }
+ pthread_spinunlock(new, &recycle_lock);
+
+ if (do_recycle) {
+ ret = sa_stacks(recycle_threshold, recyclable[my_side]);
+ SDPRINTF(("(recycle %p) recycled %d stacks\n", new, recycle_threshold));
+ if (ret != recycle_threshold)
+ assert(0);
+ }
+}
+
+/*
+ * Set the round-robin timeslice timer.
+ */
+static int
+pthread__setrrtimer(int msec, int startit)
+{
+ static int rrtimer_created;
+ struct itimerspec it;
+
+ /*
+ * This check is safe -- we will either be called before there
+ * are any threads, or with the rrtimer_mutex held.
+ */
+ if (rrtimer_created == 0) {
+ struct sigevent ev;
+
+ ev.sigev_notify = SIGEV_SA;
+ ev.sigev_signo = 0;
+ ev.sigev_value.sival_int = (int) PT_RRTIMER_MAGIC;
+ if (timer_create(CLOCK_VIRTUAL, &ev, &pthread_rrtimer) == -1)
+ return (errno);
+
+ rrtimer_created = 1;
+ }
+
+ if (startit) {
+ it.it_interval.tv_sec = 0;
+ it.it_interval.tv_nsec = (long)msec * 1000000;
+ it.it_value = it.it_interval;
+ if (timer_settime(pthread_rrtimer, 0, &it, NULL) == -1)
+ return (errno);
+ }
+
+ pthread_rrtimer_interval = msec;
+
+ return (0);
+}
+
+/* Get things rolling. */
+void
+pthread__sa_start(void)
+{
+ pthread_t self, t;
+ stack_t upcall_stacks[PT_UPCALLSTACKS];
+ int ret, i, errnosave, flags, rr;
+ char *value;
+
+ flags = 0;
+ value = getenv("PTHREAD_PREEMPT");
+ if (value && strcmp(value, "yes") == 0)
+ flags |= SA_FLAG_PREEMPT;
+
+ /*
+ * It's possible the user's program has set the round-robin
+ * interval before starting any threads.
+ *
+ * Allow the environment variable to override the default.
+ *
+ * XXX Should we just nuke the environment variable?
+ */
+ rr = pthread_rrtimer_interval;
+ value = getenv("PTHREAD_RRTIME");
+ if (value)
+ rr = atoi(value);
+
+ ret = sa_register(pthread__upcall, NULL, flags);
+ if (ret)
+ abort();
+
+ self = pthread__self();
+ for (i = 0; i < PT_UPCALLSTACKS; i++) {
+ if (0 != (ret = pthread__stackalloc(&t)))
+ abort();
+ upcall_stacks[i] = t->pt_stack;
+ pthread__initthread(self, t);
+ t->pt_type = PT_THREAD_UPCALL;
+ t->pt_flags = PT_FLAG_DETACHED;
+ sigfillset(&t->pt_sigmask); /* XXX hmmmmmm */
+ /* No locking needed, there are no threads yet. */
+ PTQ_INSERT_HEAD(&pthread__allqueue, t, pt_allq);
+ }
+
+ recycle_threshold = PT_UPCALLSTACKS/2;
+
+ ret = sa_stacks(i, upcall_stacks);
+ if (ret == -1)
+ abort();
+
+ /* XXX
+ * Calling sa_enable() can mess with errno in bizzare ways,
+ * because the kernel doesn't really return from it as a
+ * normal system call. The kernel will launch an upcall
+ * handler which will jump back to the inside of sa_enable()
+ * and permit us to continue here. However, since the kernel
+ * doesn't get a chance to set up the return-state properly,
+ * the syscall stub may interpret the unmodified register
+ * state as an error return and stuff an inappropriate value
+ * into errno.
+ *
+ * Therefore, we need to keep errno from being changed by this
+ * slightly weird control flow.
+ */
+ errnosave = errno;
+ sa_enable();
+ errno = errnosave;
+
+ /* Start the round-robin timer. */
+ if (rr != 0 && pthread__setrrtimer(rr, 1) != 0)
+ abort();
+}
+
+/*
+ * Interface routines to get/set the round-robin timer interval.
+ *
+ * XXX Sanity check the behavior for MP systems.
+ */
+
+int
+pthread_getrrtimer_np(void)
+{
+
+ return (pthread_rrtimer_interval);
+}
+
+int
+pthread_setrrtimer_np(int msec)
+{
+ extern int pthread__started;
+ int ret = 0;
+
+ if (msec < 0)
+ return (EINVAL);
+
+ pthread_mutex_lock(&rrtimer_mutex);
+
+ ret = pthread__setrrtimer(msec, pthread__started);
+
+ pthread_mutex_unlock(&rrtimer_mutex);
+
+ return (ret);
+}
diff --git a/lib/libpthread/pthread_sig.c b/lib/libpthread/pthread_sig.c
new file mode 100644
index 00000000000..9bc67dabba8
--- /dev/null
+++ b/lib/libpthread/pthread_sig.c
@@ -0,0 +1,612 @@
+/* $NetBSD: pthread_sig.c,v 1.2 2003/01/18 10:34:16 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nathan J. Williams.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* We're interposing a specific version of the signal interface. */
+#define __LIBC12_SOURCE__
+
+#define __PTHREAD_SIGNAL_PRIVATE
+
+#include <assert.h>
+#include <errno.h>
+#include <lwp.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h> /* for memcpy() */
+#include <ucontext.h>
+#include <unistd.h>
+#include <sys/cdefs.h>
+#include <sys/syscall.h>
+
+#include <sched.h>
+#include "pthread.h"
+#include "pthread_int.h"
+
+#undef PTHREAD_SIG_DEBUG
+
+#ifdef PTHREAD_SIG_DEBUG
+#define SDPRINTF(x) DPRINTF(x)
+#else
+#define SDPRINTF(x)
+#endif
+
+extern pthread_spin_t pthread__runqueue_lock;
+extern struct pthread_queue_t pthread__runqueue;
+
+extern pthread_spin_t pthread__allqueue_lock;
+extern struct pthread_queue_t pthread__allqueue;
+
+static pthread_spin_t pt_sigacts_lock;
+static struct sigaction pt_sigacts[_NSIG];
+
+static pthread_spin_t pt_process_siglock;
+static sigset_t pt_process_sigmask;
+static sigset_t pt_process_siglist;
+
+/* Queue of threads that are waiting in sigsuspend(). */
+static struct pthread_queue_t pt_sigsuspended;
+static pthread_spin_t pt_sigsuspended_lock;
+
+/*
+ * Nothing actually signals or waits on this lock, but the sleepobj
+ * needs to point to something.
+ */
+static pthread_cond_t pt_sigsuspended_cond = PTHREAD_COND_INITIALIZER;
+
+
+static void
+pthread__signal_tramp(int, int, struct sigaction *, ucontext_t *, sigset_t *,
+ int, struct pthread_queue_t *, pthread_spin_t *);
+
+__strong_alias(__libc_thr_sigsetmask,pthread_sigmask)
+
+void
+pthread__signal_init(void)
+{
+ SDPRINTF(("(signal_init) setting process sigmask\n"));
+ __sigprocmask14(0, NULL, &pt_process_sigmask);
+}
+
+int
+pthread_kill(pthread_t thread, int sig)
+{
+
+ /* We only let the thread handle this signal if the action for
+ * the signal is an explicit handler. Otherwise we feed it to
+ * the kernel so that it can affect the whole process.
+ */
+
+ /* XXX implement me */
+ return -1;
+}
+
+
+/*
+ * Interpositioning is our friend. We need to intercept sigaction() and
+ * sigsuspend().
+ */
+
+int
+__sigaction14(int sig, const struct sigaction *act, struct sigaction *oact)
+{
+ pthread_t self;
+ struct sigaction realact;
+
+ self = pthread__self();
+ if (act != NULL) {
+ /* Save the information for our internal dispatch. */
+ pthread_spinlock(self, &pt_sigacts_lock);
+ pt_sigacts[sig] = *act;
+ pthread_spinunlock(self, &pt_sigacts_lock);
+ /*
+ * We want to handle all signals ourself, and not have
+ * the kernel mask them. Therefore, we clear the
+ * sa_mask field before passing this to the kernel. We
+ * do not set SA_NODEFER, which seems like it might be
+ * appropriate, because that would permit a continuous
+ * stream of signals to exhaust the supply of upcalls.
+ */
+ realact = *act;
+ __sigemptyset14(&realact.sa_mask);
+ act = &realact;
+ }
+
+ return (__libc_sigaction14(sig, act, oact));
+}
+
+int
+__sigsuspend14(const sigset_t *sigmask)
+{
+ pthread_t self;
+ sigset_t oldmask;
+
+ self = pthread__self();
+
+ pthread_spinlock(self, &pt_sigsuspended_lock);
+ pthread_spinlock(self, &self->pt_statelock);
+ if (self->pt_cancel) {
+ pthread_spinunlock(self, &self->pt_statelock);
+ pthread_spinunlock(self, &pt_sigsuspended_lock);
+ pthread_exit(PTHREAD_CANCELED);
+ }
+ pthread_sigmask(SIG_SETMASK, sigmask, &oldmask);
+
+ self->pt_flags |= PT_FLAG_SIGCATCH;
+ self->pt_state = PT_STATE_BLOCKED_QUEUE;
+ self->pt_sleepobj = &pt_sigsuspended_cond;
+ self->pt_sleepq = &pt_sigsuspended;
+ self->pt_sleeplock = &pt_sigsuspended_lock;
+ pthread_spinunlock(self, &self->pt_statelock);
+
+ PTQ_INSERT_TAIL(&pt_sigsuspended, self, pt_sleep);
+ pthread__block(self, &pt_sigsuspended_lock);
+
+ pthread__testcancel(self);
+ self->pt_flags &= ~PT_FLAG_SIGCATCH;
+
+ pthread_sigmask(SIG_SETMASK, &oldmask, NULL);
+
+ errno = EINTR;
+ return -1;
+}
+
+/*
+ * firstsig is stolen from kern_sig.c
+ * XXX this is an abstraction violation; except for this, all of
+ * the knowledge about the composition of sigset_t's was encapsulated
+ * in signal.h.
+ * Putting this function in signal.h caused problems with other parts of the
+ * kernel that #included <signal.h> but didn't have a prototype for ffs.
+ */
+
+static int
+firstsig(const sigset_t *ss)
+{
+ int sig;
+
+ sig = ffs(ss->__bits[0]);
+ if (sig != 0)
+ return (sig);
+#if NSIG > 33
+ sig = ffs(ss->__bits[1]);
+ if (sig != 0)
+ return (sig + 32);
+#endif
+#if NSIG > 65
+ sig = ffs(ss->__bits[2]);
+ if (sig != 0)
+ return (sig + 64);
+#endif
+#if NSIG > 97
+ sig = ffs(ss->__bits[3]);
+ if (sig != 0)
+ return (sig + 96);
+#endif
+ return (0);
+}
+
+int
+pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
+{
+ pthread_t self;
+ sigset_t tmp;
+ int i, retval, unblocked, procmaskset;
+ void (*handler)(int, int, struct sigcontext *);
+ struct sigaction *act;
+ struct sigcontext xxxsc;
+ sigset_t oldmask, takelist;
+
+ self = pthread__self();
+
+ retval = 0;
+ unblocked = 0;
+ if (set != NULL) {
+ pthread_spinlock(self, &self->pt_siglock);
+ if (how == SIG_BLOCK) {
+ __sigplusset(set, &self->pt_sigmask);
+ /*
+ * Blocking of signals that are now
+ * blocked by all threads will be done
+ * lazily, at signal delivery time.
+ */
+ } else if (how == SIG_UNBLOCK) {
+ pthread_spinlock(self, &pt_process_siglock);
+ __sigminusset(set, &self->pt_sigmask);
+ /*
+ * Unblock any signals that were blocked
+ * process-wide before this.
+ */
+ tmp = pt_process_sigmask;
+ __sigminusset(set, &tmp);
+ if (!__sigsetequal(&tmp, &pt_process_sigmask))
+ pt_process_sigmask = tmp;
+ unblocked = 1;
+ } else if (how == SIG_SETMASK) {
+ pthread_spinlock(self, &pt_process_siglock);
+ self->pt_sigmask = *set;
+ SDPRINTF(("(pt_sigmask %p) (set) set thread mask to "
+ "%08x\n", self, self->pt_sigmask.__bits[0]));
+ /*
+ * Unblock any signals that were blocked
+ * process-wide before this.
+ */
+ tmp = pt_process_sigmask;
+ __sigandset(set, &tmp);
+ if (!__sigsetequal(&tmp, &pt_process_sigmask))
+ pt_process_sigmask = tmp;
+ unblocked = 1; /* Well, maybe */
+ } else
+ retval = EINVAL;
+ }
+
+ if (unblocked) {
+ SDPRINTF(("(pt_sigmask %p) Process mask was changed to %08x\n",
+ self, pt_process_sigmask.__bits[0]));
+ /* See if there are any signals to take */
+ __sigemptyset14(&takelist);
+ while ((i = firstsig(&self->pt_siglist)) != 0) {
+ if (!__sigismember14(&self->pt_sigmask, i)) {
+ __sigaddset14(&takelist, i);
+ __sigdelset14(&self->pt_siglist, i);
+ }
+ }
+ while ((i = firstsig(&pt_process_siglist)) != 0) {
+ if (!__sigismember14(&self->pt_sigmask, i)) {
+ __sigaddset14(&takelist, i);
+ __sigdelset14(&pt_process_siglist, i);
+ }
+ }
+
+ procmaskset = 0;
+ while ((i = firstsig(&takelist)) != 0) {
+ if (!__sigismember14(&self->pt_sigmask, i)) {
+ /* Take the signal */
+ act = &pt_sigacts[i];
+ oldmask = self->pt_sigmask;
+ __sigplusset(&self->pt_sigmask,
+ &act->sa_mask);
+ __sigaddset14(&self->pt_sigmask, i);
+ pthread_spinunlock(self, &pt_process_siglock);
+ pthread_spinunlock(self, &self->pt_siglock);
+ SDPRINTF(("(pt_sigmask %p) taking unblocked signal %d\n", self, i));
+ handler =
+ (void (*)(int, int, struct sigcontext *))
+ act->sa_handler;
+ handler(i, 0, &xxxsc);
+ pthread_spinlock(self, &self->pt_siglock);
+ pthread_spinlock(self, &pt_process_siglock);
+ __sigdelset14(&takelist, i);
+ /* Reset masks */
+ self->pt_sigmask = oldmask;
+ tmp = pt_process_sigmask;
+ __sigandset(&oldmask, &tmp);
+ if (!__sigsetequal(&tmp, &pt_process_sigmask)){
+ pt_process_sigmask = tmp;
+ SDPRINTF(("(pt_sigmask %p) setting proc sigmask to %08x\n", pt_process_sigmask.__bits[0]));
+ __sigprocmask14(SIG_SETMASK,
+ &pt_process_sigmask, NULL);
+ procmaskset = 1;
+ }
+ }
+ }
+ if (!procmaskset) {
+ SDPRINTF(("(pt_sigmask %p) setting proc sigmask to %08x\n", self, pt_process_sigmask.__bits[0]));
+ __sigprocmask14(SIG_SETMASK, &pt_process_sigmask, NULL);
+ }
+ }
+ if (set != NULL) {
+ if ((how == SIG_UNBLOCK) || (how == SIG_SETMASK))
+ pthread_spinunlock(self, &pt_process_siglock);
+ pthread_spinunlock(self, &self->pt_siglock);
+ }
+
+ /*
+ * While other threads may read a process's sigmask,
+ * they won't write it, so we don't need to lock our reads of it.
+ */
+ if (oset != NULL) {
+ *oset = self->pt_sigmask;
+ }
+
+ return retval;
+}
+
+
+/*
+ * Dispatch a signal to thread t, if it is non-null, and to any
+ * willing thread, if t is null.
+ */
+void
+pthread__signal(pthread_t t, int sig, int code)
+{
+ pthread_t self, target, good, okay;
+ sigset_t oldmask, *maskp;
+ ucontext_t *uc;
+
+ self = pthread__self();
+ if (t) {
+ target = t;
+ pthread_spinlock(self, &target->pt_siglock);
+ } else {
+ /*
+ * Pick a thread that doesn't have the signal blocked
+ * and can be reasonably forced to run.
+ */
+ okay = good = NULL;
+ pthread_spinlock(self, &pthread__allqueue_lock);
+ PTQ_FOREACH(target, &pthread__allqueue, pt_allq) {
+ /*
+ * Changing to PT_STATE_ZOMBIE is protected by
+ * the pthread__allqueue lock, so we can just
+ * test for it here.
+ */
+ if ((target->pt_state == PT_STATE_ZOMBIE) ||
+ (target->pt_type != PT_THREAD_NORMAL))
+ continue;
+ pthread_spinlock(self, &target->pt_siglock);
+ SDPRINTF((
+ "(pt_signal %p) target %p: state %d, mask %08x\n",
+ self, target, target->pt_state, target->pt_sigmask.__bits[0]));
+ if (!__sigismember14(&target->pt_sigmask, sig)) {
+ if (target->pt_state != PT_STATE_BLOCKED_SYS) {
+ good = target;
+ /* Leave target locked */
+ break;
+ } else if (okay == NULL) {
+ okay = target;
+ /* Leave target locked */
+ continue;
+ }
+ }
+ pthread_spinunlock(self, &target->pt_siglock);
+ }
+ pthread_spinunlock(self, &pthread__allqueue_lock);
+ if (good) {
+ target = good;
+ if (okay)
+ pthread_spinunlock(self, &okay->pt_siglock);
+ } else {
+ target = okay;
+ }
+
+ if (target == NULL) {
+ /*
+ * They all have it blocked. Note that in our
+ * process-wide signal mask, and stash the signal
+ * for later unblocking.
+ */
+ pthread_spinlock(self, &pt_process_siglock);
+ __sigaddset14(&pt_process_sigmask, sig);
+ SDPRINTF(("(pt_signal %p) lazily setting proc sigmask to "
+ "%08x\n", self, pt_process_sigmask.__bits[0]));
+ __sigprocmask14(SIG_SETMASK, &pt_process_sigmask, NULL);
+ __sigaddset14(&pt_process_siglist, sig);
+ pthread_spinunlock(self, &pt_process_siglock);
+ return;
+ }
+ }
+
+ /*
+ * We now have a signal and a victim.
+ * The victim's pt_siglock is locked.
+ */
+
+ /*
+ * Reset the process signal mask so we can take another
+ * signal. We will not exhaust our supply of upcalls; if
+ * another signal is delivered after this, the resolve_locks
+ * dance will permit us to finish and recycle before the next
+ * upcall reaches this point.
+ */
+ pthread_spinlock(self, &pt_process_siglock);
+ SDPRINTF(("(pt_signal %p) setting proc sigmask to "
+ "%08x\n", self, pt_process_sigmask.__bits[0]));
+ __sigprocmask14(SIG_SETMASK, &pt_process_sigmask, NULL);
+ pthread_spinunlock(self, &pt_process_siglock);
+
+ if (t && __sigismember14(&target->pt_sigmask, sig)) {
+ /* Record the signal for later delivery. */
+ __sigaddset14(&target->pt_siglist, sig);
+ pthread_spinunlock(self, &target->pt_siglock);
+ return;
+ }
+
+ /*
+ * Ensure the victim is not running.
+ * In a MP world, it could be on another processor somewhere.
+ *
+ * XXX As long as this is uniprocessor, encountering a running
+ * target process is a bug.
+ */
+ assert(target->pt_state != PT_STATE_RUNNING);
+ /*
+ * Prevent anyone else from considering this thread for handling
+ * more instances of this signal.
+ */
+ oldmask = target->pt_sigmask;
+ __sigplusset(&target->pt_sigmask, &pt_sigacts[sig].sa_mask);
+ __sigaddset14(&target->pt_sigmask, sig);
+ pthread_spinunlock(self, &target->pt_siglock);
+
+ /*
+ * Holding the state lock blocks out cancellation and any other
+ * attempts to set this thread up to take a signal.
+ */
+ pthread_spinlock(self, &target->pt_statelock);
+ switch (target->pt_state) {
+ case PT_STATE_RUNNABLE:
+ pthread_spinlock(self, &pthread__runqueue_lock);
+ PTQ_REMOVE(&pthread__runqueue, target, pt_runq);
+ pthread_spinunlock(self, &pthread__runqueue_lock);
+ break;
+ case PT_STATE_BLOCKED_QUEUE:
+ pthread_spinlock(self, target->pt_sleeplock);
+ PTQ_REMOVE(target->pt_sleepq, target, pt_sleep);
+ pthread_spinunlock(self, target->pt_sleeplock);
+ break;
+ case PT_STATE_BLOCKED_SYS:
+ /*
+ * The target is not on a queue at all, and won't run
+ * again for a while. Try to wake it from its torpor.
+ */
+ _lwp_wakeup(target->pt_blockedlwp);
+ break;
+ default:
+ ;
+ }
+
+ /*
+ * We'd like to just pass oldmask to the
+ * pthread__signal_tramp(), but makecontext() can't reasonably
+ * pass structures, just word-size things or smaller. We also
+ * don't want to involve malloc() here, inside the upcall
+ * handler. So we borrow a bit of space from the target's
+ * stack, which we were adjusting anyway.
+ */
+ maskp = (sigset_t *)((char *)target->pt_uc - STACKSPACE -
+ sizeof(sigset_t));
+ *maskp = oldmask;
+
+ /*
+ * XXX We are blatantly ignoring SIGALTSTACK. It would screw
+ * with our notion of stack->thread mappings.
+ */
+ uc = (ucontext_t *)((char *)maskp - sizeof(ucontext_t));
+#ifdef _UC_UCONTEXT_ALIGN
+ uc = (ucontext_t *)((uintptr_t)uc & _UC_UCONTEXT_ALIGN);
+#endif
+
+ _INITCONTEXT_U(uc);
+ uc->uc_stack.ss_sp = maskp;
+ uc->uc_stack.ss_size = 0;
+ uc->uc_link = NULL;
+
+ SDPRINTF(("(makecontext %p): target %p: sig: %d %d uc: %p oldmask: %08x\n",
+ self, target, sig, code, target->pt_uc, maskp->__bits[0]));
+ makecontext(uc, pthread__signal_tramp, 8,
+ sig, code, &pt_sigacts[sig], target->pt_uc, maskp,
+ target->pt_state, target->pt_sleepq, target->pt_sleeplock);
+ target->pt_uc = uc;
+
+ if (target->pt_state != PT_STATE_BLOCKED_SYS)
+ pthread__sched(self, target);
+ pthread_spinunlock(self, &target->pt_statelock);
+}
+
+
+static void
+pthread__signal_tramp(int sig, int code, struct sigaction *act,
+ ucontext_t *uc, sigset_t *oldmask, int oldstate,
+ struct pthread_queue_t *oldsleepq, pthread_spin_t *oldsleeplock)
+{
+ void (*handler)(int, int, struct sigcontext *);
+ struct pthread__sigcontext psc;
+ pthread_t self, next;
+
+ self = pthread__self();
+
+ SDPRINTF(("(tramp %p) sig %d uc %p oldmask %08x oldstate %d q %p\n",
+ self, sig, uc, oldmask->__bits[0], oldstate, oldsleepq));
+
+ /*
+ * We should only ever get here if a handler is set. Signal
+ * actions are process-global; a signal set to SIG_DFL or
+ * SIG_IGN should be handled in the kernel (by being ignored
+ * or killing the process) and never get this far.
+ */
+ handler = (void (*)(int, int, struct sigcontext *)) act->sa_handler;
+
+ /*
+ * XXX we don't support siginfo here yet.
+ */
+ PTHREAD_UCONTEXT_TO_SIGCONTEXT(oldmask, uc, &psc);
+ handler(sig, code, &psc.psc_context);
+ PTHREAD_SIGCONTEXT_TO_UCONTEXT(&psc, uc);
+
+ /*
+ * We've finished the handler, so this thread can restore the
+ * original signal mask. Note that traditional BSD behavior
+ * allows for the handler to change the signal mask; we handle
+ * that here.
+ */
+ pthread_sigmask(SIG_SETMASK, &uc->uc_sigmask, NULL);
+
+ /*
+ * Go back to whatever queue we were found on, unless SIGCATCH
+ * is set. When we are continued, the first thing we do will
+ * be to jump back to the previous context.
+ */
+ if (self->pt_flags & PT_FLAG_SIGCATCH)
+ _setcontext_u(uc);
+
+ next = pthread__next(self);
+ next->pt_state = PT_STATE_RUNNING;
+ pthread_spinlock(self, &self->pt_statelock);
+ if (oldstate == PT_STATE_RUNNABLE) {
+ pthread_spinlock(self, &pthread__runqueue_lock);
+ pthread_spinunlock(self, &self->pt_statelock);
+ self->pt_state = PT_STATE_RUNNABLE;
+ PTQ_INSERT_TAIL(&pthread__runqueue, self, pt_runq);
+ pthread__locked_switch(self, next, &pthread__runqueue_lock);
+ } else if (oldstate == PT_STATE_BLOCKED_QUEUE) {
+ pthread_spinlock(self, oldsleeplock);
+ self->pt_state = PT_STATE_BLOCKED_QUEUE;
+ self->pt_sleepq = oldsleepq;
+ self->pt_sleeplock = oldsleeplock;
+ pthread_spinunlock(self, &self->pt_statelock);
+ PTQ_INSERT_TAIL(oldsleepq, self, pt_sleep);
+ pthread__locked_switch(self, next, oldsleeplock);
+ } else if (oldstate == PT_STATE_BLOCKED_SYS) {
+ /*
+ * We weren't really doing anything before. Just go to
+ * the next thread.
+ */
+ self->pt_state = PT_STATE_BLOCKED_SYS;
+ pthread_spinunlock(self, &self->pt_statelock);
+ pthread__switch(self, next);
+ } else {
+ assert(0);
+ }
+ /*
+ * Jump back to what we were doing before we were interrupted
+ * by a signal.
+ */
+ _setcontext_u(uc);
+ /* NOTREACHED */
+ assert(0);
+}
diff --git a/lib/libpthread/pthread_specific.c b/lib/libpthread/pthread_specific.c
new file mode 100644
index 00000000000..be7d2e93ed2
--- /dev/null
+++ b/lib/libpthread/pthread_specific.c
@@ -0,0 +1,257 @@
+/* $NetBSD: pthread_specific.c,v 1.2 2003/01/18 10:34:16 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nathan J. Williams.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* Functions and structures dealing with thread-specific data */
+#include <assert.h>
+#include <errno.h>
+#include <sys/cdefs.h>
+
+#include "pthread.h"
+#include "pthread_int.h"
+
+static pthread_mutex_t tsd_mutex = PTHREAD_MUTEX_INITIALIZER;
+static int nextkey;
+int pthread__tsd_alloc[PTHREAD_KEYS_MAX];
+void (*pthread__tsd_destructors[PTHREAD_KEYS_MAX])(void *);
+
+__strong_alias(__libc_thr_keycreate,pthread_key_create)
+__strong_alias(__libc_thr_setspecific,pthread_setspecific)
+__strong_alias(__libc_thr_getspecific,pthread_getspecific)
+__strong_alias(__libc_thr_keydelete,pthread_key_delete)
+
+int
+pthread_key_create(pthread_key_t *key, void (*destructor)(void *))
+{
+ int i;
+
+ /* Get a lock on the allocation list */
+ pthread_mutex_lock(&tsd_mutex);
+
+ /* Find an avaliable slot */
+ /* 1. Search from "nextkey" to the end of the list. */
+ for (i = nextkey; i < PTHREAD_KEYS_MAX; i++)
+ if (pthread__tsd_alloc[i] == 0)
+ break;
+
+ if (i == PTHREAD_KEYS_MAX) {
+ /* 2. If that didn't work, search from the start
+ * of the list back to "nextkey".
+ */
+ for (i = 0; i < nextkey; i++)
+ if (pthread__tsd_alloc[i] == 0)
+ break;
+
+ if (i == nextkey) {
+ /* If we didn't find one here, there isn't one
+ * to be found.
+ */
+ pthread_mutex_unlock(&tsd_mutex);
+ return EAGAIN;
+ }
+ }
+
+ /* Got one. */
+ pthread__tsd_alloc[i] = 1;
+ nextkey = (i + 1) % PTHREAD_KEYS_MAX;
+ pthread__tsd_destructors[i] = destructor;
+ pthread_mutex_unlock(&tsd_mutex);
+ *key = i;
+
+ return 0;
+}
+
+int
+pthread_key_delete(pthread_key_t key)
+{
+
+ /*
+ * This is tricky. The standard says of pthread_key_create()
+ * that new keys have the value NULL associated with them in
+ * all threads. According to people who were present at the
+ * standardization meeting, that requirement was written
+ * before pthread_key_delete() was introduced, and not
+ * reconsidered when it was.
+ *
+ * See David Butenhof's article in comp.programming.threads:
+ * Subject: Re: TSD key reusing issue
+ * Message-ID: <u97d8.29$fL6.200@news.cpqcorp.net>
+ * Date: Thu, 21 Feb 2002 09:06:17 -0500
+ * http://groups.google.com/groups?hl=en&selm=u97d8.29%24fL6.200%40news.cpqcorp.net
+ *
+ * Given:
+ *
+ * 1: Applications are not required to clear keys in all
+ * threads before calling pthread_key_delete().
+ * 2: Clearing pointers without running destructors is a
+ * memory leak.
+ * 3: The pthread_key_delete() function is expressly forbidden
+ * to run any destructors.
+ *
+ * Option 1: Make this function effectively a no-op and
+ * prohibit key reuse. This is a possible resource-exhaustion
+ * problem given that we have a static storage area for keys,
+ * but having a non-static storage area would make
+ * pthread_setspecific() expensive (might need to realloc the
+ * TSD array).
+ *
+ * Option 2: Ignore the specified behavior of
+ * pthread_key_create() and leave the old values. If an
+ * application deletes a key that still has non-NULL values in
+ * some threads... it's probably a memory leak and hence
+ * incorrect anyway, and we're within our rights to let the
+ * application lose. However, it's possible (if unlikely) that
+ * the application is storing pointers to non-heap data, or
+ * non-pointers that have been wedged into a void pointer, so
+ * we can't entirely write off such applications as incorrect.
+ * This could also lead to running (new) destructors on old
+ * data that was never supposed to be associated with that
+ * destructor.
+ *
+ * Option 3: Follow the specified behavior of
+ * pthread_key_create(). Either pthread_key_create() or
+ * pthread_key_delete() would then have to clear the values in
+ * every thread's slot for that key. In order to guarantee the
+ * visibility of the NULL value in other threads, there would
+ * have to be synchronization operations in both the clearer
+ * and pthread_getspecific(). Putting synchronization in
+ * pthread_getspecific() is a big performance lose. But in
+ * reality, only (buggy) reuse of an old key would require
+ * this synchronization; for a new key, there has to be a
+ * memory-visibility propagating event between the call to
+ * pthread_key_create() and pthread_getspecific() with that
+ * key, so setting the entries to NULL without synchronization
+ * will work, subject to problem (2) above. However, it's kind
+ * of slow.
+ *
+ * Note that the argument in option 3 only applies because we
+ * keep TSD in ordinary memory which follows the pthreads
+ * visibility rules. The visibility rules are not required by
+ * the standard to apply to TSD, so this arguemnt doesn't
+ * apply in general, just to this implementation.
+ */
+
+ /* For the momemt, we're going with option 1. */
+ pthread_mutex_lock(&tsd_mutex);
+ pthread__tsd_destructors[key] = NULL;
+ pthread_mutex_unlock(&tsd_mutex);
+
+ return 0;
+}
+
+int
+pthread_setspecific(pthread_key_t key, const void *value)
+{
+ pthread_t self;
+
+ if (pthread__tsd_alloc[key] == 0)
+ return EINVAL;
+
+ self = pthread__self();
+ /*
+ * We can't win here on constness. Having been given a
+ * "const void *", we can only assign it to other const void *,
+ * and return it from functions that are const void *, without
+ * generating a warning.
+ */
+ self->pt_specific[key] = (void *) value;
+
+ return 0;
+}
+
+void*
+pthread_getspecific(pthread_key_t key)
+{
+ pthread_t self;
+
+ if (pthread__tsd_alloc[key] == 0)
+ return NULL;
+
+ self = pthread__self();
+ return (self->pt_specific[key]);
+}
+
+/* Perform thread-exit-time destruction of thread-specific data. */
+void
+pthread__destroy_tsd(pthread_t self)
+{
+ int i, done, iterations;
+ void *val;
+ void (*destructor)(void *);
+
+ /* Butenhof, section 5.4.2 (page 167):
+ *
+ * ``Also, Pthreads sets the thread-specific data value for a
+ * key to NULL before calling that key's destructor (passing
+ * the previous value of the key) when a thread terminates [*].
+ * ...
+ * [*] That is, unfortunately, not what the standard
+ * says. This is one of the problems with formal standards -
+ * they say what they say, not what they were intended to
+ * say. Somehow, an error crept in, and the sentence
+ * specifying that "the implementation clears the
+ * thread-specific data value before calling the destructor"
+ * was deleted. Nobody noticed, and the standard was approved
+ * with the error. So the standard says (by omission) that if
+ * you want to write a portable application using
+ * thread-specific data, that will not hang on thread
+ * termination, you must call pthread_setspecific within your
+ * destructor function to change the value to NULL. This would
+ * be silly, and any serious implementation of Pthreads will
+ * violate the standard in this respect. Of course, the
+ * standard will be fixed, probably by the 1003.1n amendment
+ * (assorted corrections to 1003.1c-1995), but that will take
+ * a while.''
+ */
+
+ iterations = PTHREAD_DESTRUCTOR_ITERATIONS;
+ do {
+ done = 1;
+ for (i = 0; i < PTHREAD_KEYS_MAX; i++) {
+ pthread_mutex_lock(&tsd_mutex);
+ destructor = pthread__tsd_destructors[i];
+ pthread_mutex_unlock(&tsd_mutex);
+ if ((self->pt_specific[i] != NULL) &&
+ destructor != NULL) {
+ done = 0;
+ val = self->pt_specific[i];
+ self->pt_specific[i] = NULL; /* see above */
+ (*destructor)(val);
+ }
+ }
+ } while (!done && iterations--);
+}
diff --git a/lib/libpthread/pthread_stack.c b/lib/libpthread/pthread_stack.c
new file mode 100644
index 00000000000..d134b804101
--- /dev/null
+++ b/lib/libpthread/pthread_stack.c
@@ -0,0 +1,146 @@
+/* $NetBSD: pthread_stack.c,v 1.2 2003/01/18 10:34:16 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nathan J. Williams.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <ucontext.h>
+#include <unistd.h>
+#include <sys/queue.h>
+#include <sys/mman.h>
+
+#include <sched.h>
+#include "pthread.h"
+#include "pthread_int.h"
+
+static pthread_t
+pthread__stackid_setup(void *base, int size);
+
+
+/* Allocate a stack for a thread, and set it up. It needs to be aligned, so
+ * that a thread can find itself by its stack pointer.
+ */
+int
+pthread__stackalloc(pthread_t *newt)
+{
+ void *addr;
+ int ret;
+ vaddr_t a, b, c, d;
+
+ /* The mmap() interface doesn't let us specify alignment,
+ * so we work around it by mmap()'ing twice the needed space,
+ * then unmapping the unaligned stuff on the edges.
+ */
+
+ addr = mmap(0, 2 * PT_STACKSIZE, PROT_READ|PROT_WRITE,
+ MAP_ANON|MAP_PRIVATE, -1, 0);
+ if (addr == MAP_FAILED)
+ return ENOMEM;
+
+ a = (vaddr_t) addr;
+
+ if ((a & PT_STACKMASK) != 0) {
+ b = (a & ~PT_STACKMASK) + PT_STACKSIZE;
+ ret = munmap((void *)a, b-a);
+ if (ret == -1)
+ return ENOMEM;
+ } else {
+ b = a;
+ }
+
+ c = b + PT_STACKSIZE;
+ d = a + 2*PT_STACKSIZE;
+
+ ret = munmap((void *)c, d-c);
+ if (ret == -1)
+ return ENOMEM;
+
+ *newt = pthread__stackid_setup((void *)b, PT_STACKSIZE);
+ return 0;
+}
+
+
+/*
+ * Set up the slightly special stack for the "initial" thread, which
+ * runs on the normal system stack, and thus gets slightly different
+ * treatment.
+ */
+void
+pthread__initmain(pthread_t *newt)
+{
+ void *base;
+
+ base = (void *) (pthread__sp() & ~PT_STACKMASK);
+
+ *newt = pthread__stackid_setup(base, PT_STACKSIZE);
+}
+
+static pthread_t
+pthread__stackid_setup(void *base, int size)
+{
+ pthread_t t;
+ int pagesize, ret;
+
+ /* Protect the next-to-bottom stack page as a red zone. */
+ /* XXX assumes that the stack grows down. */
+ pagesize = sysconf(_SC_PAGESIZE);
+ ret = mprotect( (char *)base + pagesize, pagesize, PROT_NONE);
+ if (ret == -1)
+ err(2, "Couldn't mprotect() stack redzone at %p\n",
+ (char *)base + pagesize);
+ /*
+ * Put a pointer to the pthread in the bottom (but
+ * redzone-protected section) of the stack.
+ */
+ t = base;
+
+ t->pt_stack.ss_sp = (char *)base + 2 * pagesize;
+ t->pt_stack.ss_size = PT_STACKSIZE - 2 * pagesize;
+
+ /* Set up an initial ucontext pointer to a "safe" area */
+ t->pt_uc =(ucontext_t *) ((char *)t->pt_stack.ss_sp +
+ t->pt_stack.ss_size - (pagesize/2));
+#ifdef _UC_UCONTEXT_ALIGN
+ t->pt_uc = (ucontext_t *)((uintptr_t)t->pt_uc & _UC_UCONTEXT_ALIGN);
+#endif
+
+ return t;
+}
diff --git a/lib/libpthread/pthread_types.h b/lib/libpthread/pthread_types.h
new file mode 100644
index 00000000000..fd9a42b33bb
--- /dev/null
+++ b/lib/libpthread/pthread_types.h
@@ -0,0 +1,242 @@
+/* $NetBSD: pthread_types.h,v 1.2 2003/01/18 10:34:17 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nathan J. Williams.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _LIB_PTHREAD_TYPES_H
+#define _LIB_PTHREAD_TYPES_H
+
+#include <machine/lock.h>
+
+/* We use the "pthread_spin_t" name internally; "pthread_spinlock_t" is the
+ * POSIX spinlock object.
+ */
+typedef __cpu_simple_lock_t pthread_spin_t;
+
+/*
+ * Copied from PTQ_HEAD in pthread_queue.h
+ */
+#define _PTQ_HEAD(name, type) \
+struct name { \
+ struct type *ptqh_first;/* first element */ \
+ struct type **ptqh_last;/* addr of last next element */ \
+}
+
+_PTQ_HEAD(pthread_queue_t, pthread_st);
+
+struct pthread_st;
+struct pthread_attr_st;
+struct pthread_mutex_st;
+struct pthread_mutexattr_st;
+struct pthread_cond_st;
+struct pthread_condattr_st;
+struct pthread_spin_st;
+struct pthread_rwlock_st;
+struct pthread_rwlockattr_st;
+struct pthread_barrier_st;
+struct pthread_barrierattr_st;
+
+typedef struct pthread_st *pthread_t;
+typedef struct pthread_attr_st pthread_attr_t;
+typedef struct pthread_mutex_st pthread_mutex_t;
+typedef struct pthread_mutexattr_st pthread_mutexattr_t;
+typedef struct pthread_cond_st pthread_cond_t;
+typedef struct pthread_condattr_st pthread_condattr_t;
+typedef struct pthread_once_st pthread_once_t;
+typedef struct pthread_spinlock_st pthread_spinlock_t;
+typedef struct pthread_rwlock_st pthread_rwlock_t;
+typedef struct pthread_rwlockattr_st pthread_rwlockattr_t;
+typedef struct pthread_barrier_st pthread_barrier_t;
+typedef struct pthread_barrierattr_st pthread_barrierattr_t;
+typedef int pthread_key_t;
+
+struct pthread_attr_st {
+ unsigned int pta_magic;
+
+ int pta_flags;
+ void *pta_private;
+};
+
+struct pthread_mutex_st {
+ unsigned int ptm_magic;
+
+ /* Not a real spinlock; will never be spun on. Locked with
+ * pthread__simple_lock_try() or not at all. Therefore, not
+ * subject to preempted-spinlock-continuation.
+ *
+ * Open research question: Would it help threaded applications if
+ * preempted-lock-continuation were applied to mutexes?
+ */
+ pthread_spin_t ptm_lock;
+
+ /* Protects the owner and blocked queue */
+ pthread_spin_t ptm_interlock;
+ pthread_t ptm_owner;
+ struct pthread_queue_t ptm_blocked;
+ void *ptm_private;
+};
+
+#define _PT_MUTEX_MAGIC 0x33330003
+#define _PT_MUTEX_DEAD 0xDEAD0003
+
+#define PTHREAD_MUTEX_INITIALIZER { _PT_MUTEX_MAGIC, \
+ __SIMPLELOCK_UNLOCKED, \
+ __SIMPLELOCK_UNLOCKED, \
+ NULL, \
+ {NULL, NULL}, \
+ NULL \
+ }
+
+
+struct pthread_mutexattr_st {
+ unsigned int ptma_magic;
+ void *ptma_private;
+};
+
+#define _PT_MUTEXATTR_MAGIC 0x44440004
+#define _PT_MUTEXATTR_DEAD 0xDEAD0004
+
+
+struct pthread_cond_st {
+ unsigned int ptc_magic;
+
+ /* Protects the queue of waiters */
+ pthread_spin_t ptc_lock;
+ struct pthread_queue_t ptc_waiters;
+
+ pthread_mutex_t *ptc_mutex; /* Current mutex */
+ void *ptc_private;
+};
+
+#define _PT_COND_MAGIC 0x55550005
+#define _PT_COND_DEAD 0xDEAD0005
+
+#define PTHREAD_COND_INITIALIZER { _PT_COND_MAGIC, \
+ __SIMPLELOCK_UNLOCKED, \
+ {NULL, NULL}, \
+ NULL, \
+ NULL \
+ }
+
+struct pthread_condattr_st {
+ unsigned int ptca_magic;
+ void *ptca_private;
+};
+
+#define _PT_CONDATTR_MAGIC 0x66660006
+#define _PT_CONDATTR_DEAD 0xDEAD0006
+
+struct pthread_once_st {
+ pthread_mutex_t pto_mutex;
+ int pto_done;
+};
+
+#define PTHREAD_ONCE_INIT { PTHREAD_MUTEX_INITIALIZER, 0 }
+
+struct pthread_spinlock_st {
+ unsigned int pts_magic;
+ pthread_spin_t pts_spin;
+ int pts_flags;
+};
+
+#define _PT_SPINLOCK_MAGIC 0x77770007
+#define _PT_SPINLOCK_DEAD 0xDEAD0007
+#define _PT_SPINLOCK_PSHARED 0x00000001
+
+/* PTHREAD_SPINLOCK_INITIALIZER is an extension not specified by POSIX. */
+#define PTHREAD_SPINLOCK_INITIALIZER { _PT_SPINLOCK_MAGIC, \
+ __SIMPLELOCK_UNLOCKED, \
+ 0 \
+ }
+
+struct pthread_rwlock_st {
+ unsigned int ptr_magic;
+
+ /* Protects data below */
+ pthread_spin_t ptr_interlock;
+
+ struct pthread_queue_t ptr_rblocked;
+ struct pthread_queue_t ptr_wblocked;
+ unsigned int ptr_nreaders;
+ pthread_t ptr_writer;
+ void *ptr_private;
+};
+
+#define _PT_RWLOCK_MAGIC 0x99990009
+#define _PT_RWLOCK_DEAD 0xDEAD0009
+
+#define PTHREAD_RWLOCK_INITIALIZER { _PT_RWLOCK_MAGIC, \
+ __SIMPLELOCK_UNLOCKED, \
+ {NULL, NULL}, \
+ {NULL, NULL}, \
+ 0, \
+ NULL, \
+ NULL, \
+ }
+
+struct pthread_rwlockattr_st {
+ unsigned int ptra_magic;
+ void *ptra_private;
+};
+
+#define _PT_RWLOCKATTR_MAGIC 0x99990909
+#define _PT_RWLOCKATTR_DEAD 0xDEAD0909
+
+struct pthread_barrier_st {
+ unsigned int ptb_magic;
+
+ /* Protects data below */
+ pthread_spin_t ptb_lock;
+
+ struct pthread_queue_t ptb_waiters;
+ unsigned int ptb_initcount;
+ unsigned int ptb_curcount;
+
+ void *ptb_private;
+};
+
+#define _PT_BARRIER_MAGIC 0x88880008
+#define _PT_BARRIER_DEAD 0xDEAD0008
+
+struct pthread_barrierattr_st {
+ unsigned int ptba_magic;
+ void *ptba_private;
+};
+
+#define _PT_BARRIERATTR_MAGIC 0x88880808
+#define _PT_BARRIERATTR_DEAD 0xDEAD0808
+
+#endif /* _LIB_PTHREAD_TYPES_H */
diff --git a/lib/libpthread/sched.c b/lib/libpthread/sched.c
new file mode 100644
index 00000000000..8c31647d2b2
--- /dev/null
+++ b/lib/libpthread/sched.c
@@ -0,0 +1,93 @@
+/* $NetBSD: sched.c,v 1.2 2003/01/18 10:34:17 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nathan J. Williams.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#include <errno.h>
+#include <sched.h>
+
+int pthread__sched_binder;
+
+/* ARGSUSED */
+int
+sched_setparam(pid_t pid, const struct sched_param *param)
+{
+ return ENOSYS;
+}
+
+/* ARGSUSED */
+int
+sched_getparam(pid_t pid, struct sched_param *param)
+{
+ return ENOSYS;
+}
+
+/* ARGSUSED */
+int
+sched_setscheduler(pid_t pid, int policy,
+ const struct sched_param *param)
+{
+ return ENOSYS;
+}
+
+/* ARGSUSED */
+int
+sched_getscheduler(pid_t pid)
+{
+ return ENOSYS;
+}
+
+/* ARGSUSED */
+int
+sched_get_priority_max(int policy)
+{
+ return ENOSYS;
+}
+
+/* ARGSUSED */
+int
+sched_get_priority_min(int policy)
+{
+ return ENOSYS;
+}
+
+/* ARGSUSED */
+int
+sched_rr_get_interval(pid_t pid, struct timespec *interval)
+{
+ return ENOSYS;
+}
diff --git a/lib/libpthread/shlib_version b/lib/libpthread/shlib_version
new file mode 100644
index 00000000000..1e5cbf564c0
--- /dev/null
+++ b/lib/libpthread/shlib_version
@@ -0,0 +1,5 @@
+# $NetBSD: shlib_version,v 1.2 2003/01/18 10:34:17 thorpej Exp $
+# Remember to update distrib/sets/lists/base/shl.* when changing
+#
+major=0
+minor=0