diff options
| author | thorpej <thorpej@NetBSD.org> | 2003-01-18 10:32:11 +0000 |
|---|---|---|
| committer | thorpej <thorpej@NetBSD.org> | 2003-01-18 10:32:11 +0000 |
| commit | c62a74e6d5cfb2fb5034e98b983da7e2fc87709d (patch) | |
| tree | f1281fd7111ddb57951e4472b342b53a2f56244a /lib/libpthread | |
| parent | af919042eceb8c19f42acd688efa8cf58168af5d (diff) | |
Merge the nathanw_sa branch.
Diffstat (limited to 'lib/libpthread')
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 |
