diff options
Diffstat (limited to 'lib/libpthread')
185 files changed, 28507 insertions, 0 deletions
diff --git a/lib/libpthread/Makefile b/lib/libpthread/Makefile new file mode 100644 index 0000000..17b46b9 --- /dev/null +++ b/lib/libpthread/Makefile @@ -0,0 +1,54 @@ +# $FreeBSD$ +# +# All library objects contain FreeBSD revision strings by default; they may be +# excluded as a space-saving measure. To produce a library that does +# not contain these strings, add -DSTRIP_FBSDID (see <sys/cdefs.h>) to CFLAGS +# below. Note, there are no IDs for syscall stubs whose sources are generated. +# To included legacy CSRG sccsid strings, add -DLIBC_SCCS and -DSYSLIBC_SCCS +# (for system call stubs) to CFLAGS below. -DSYSLIBC_SCCS affects just the +# system call stubs. +.if ${MACHINE_ARCH} == "alpha" || ${MACHINE_ARCH} == "sparc64" +LIB=kse +.else +LIB=pthread +SHLIBDIR?= /lib +.endif +SHLIB_MAJOR= 2 +CFLAGS+=-DPTHREAD_KERNEL +CFLAGS+=-I${.CURDIR}/../libc/include -I${.CURDIR}/thread \ + -I${.CURDIR}/../../include +CFLAGS+=-I${.CURDIR}/arch/${MACHINE_ARCH}/include +CFLAGS+=-I${.CURDIR}/sys +CFLAGS+=-I${.CURDIR}/../../libexec/rtld-elf +CFLAGS+=-I${.CURDIR}/../../libexec/rtld-elf/${MACHINE_ARCH} +CFLAGS+=-fno-builtin + +# Uncomment this if you want libpthread to contain debug information for +# thread locking. +CFLAGS+=-D_LOCK_DEBUG +WARNS?=2 + +# Uncomment this if you want to build a 1:1 threading mode library +# however it is no longer strictly conformed to POSIX +# CFLAGS+=-DSYSTEM_SCOPE_ONLY + +# Enable extra internal consistancy checks. +CFLAGS+=-D_PTHREADS_INVARIANTS -Wall + +VERSION_MAP=${.CURDIR}/pthread.map + +.if defined(SYMVER_ENABLED) +# Remove this if library version is bumped and LIBPTHREAD_1_0 +# compatability hacks are removed (see thread/thr_private.h). +LDFLAGS+=-Wl,-zmuldefs +CFLAGS+=-DSYMBOL_VERSIONING +.endif + +PRECIOUSLIB= + +.include "${.CURDIR}/arch/${MACHINE_ARCH}/Makefile.inc" +.include "${.CURDIR}/support/Makefile.inc" +.include "${.CURDIR}/sys/Makefile.inc" +.include "${.CURDIR}/thread/Makefile.inc" + +.include <bsd.lib.mk> diff --git a/lib/libpthread/arch/alpha/Makefile.inc b/lib/libpthread/arch/alpha/Makefile.inc new file mode 100644 index 0000000..7bb3ad9 --- /dev/null +++ b/lib/libpthread/arch/alpha/Makefile.inc @@ -0,0 +1,5 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/arch/${MACHINE_ARCH}/${MACHINE_ARCH} + +SRCS+= enter_uts.S context.S pthread_md.c diff --git a/lib/libpthread/arch/alpha/alpha/context.S b/lib/libpthread/arch/alpha/alpha/context.S new file mode 100644 index 0000000..6ef42b6 --- /dev/null +++ b/lib/libpthread/arch/alpha/alpha/context.S @@ -0,0 +1,353 @@ +/* + * Copyright (c) 2001,3 Daniel Eischen <deischen@freebsd.org> + * 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. Neither the name of the author 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 AUTHOR 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 AUTHOR 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. + */ +/* + * Copyright (c) 1994, 1995 Carnegie-Mellon University. + * All rights reserved. + * + * Author: Chris G. Demetriou + * + * Permission to use, copy, modify and distribute this software and + * its documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND + * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie the + * rights to redistribute these changes. + * + */ + +#include <machine/asm.h> +__FBSDID("$FreeBSD$"); + +/* #include <machine/frame.h> */ +#define FRAME_V0 0 +#define FRAME_T0 1 +#define FRAME_T1 2 +#define FRAME_T2 3 +#define FRAME_T3 4 +#define FRAME_T4 5 +#define FRAME_T5 6 +#define FRAME_T6 7 +#define FRAME_T7 8 +#define FRAME_S0 9 +#define FRAME_S1 10 +#define FRAME_S2 11 +#define FRAME_S3 12 +#define FRAME_S4 13 +#define FRAME_S5 14 +#define FRAME_S6 15 +#define FRAME_A3 16 +#define FRAME_A4 17 +#define FRAME_A5 18 +#define FRAME_RA 23 +#define FRAME_T12 24 +#define FRAME_AT 25 +#define FRAME_SP 26 +#define FRAME_TRAPARG_A0 28 +#define FRAME_TRAPARG_A1 29 +#define FRAME_TRAPARG_A2 30 +#define FRAME_PC (FRAME_TRAPARG_A2 + 1 + 1) + +/* #include <machine/reg.h> */ +#define R_V0 0 +#define R_T0 1 +#define R_T1 2 +#define R_T2 3 +#define R_T3 4 +#define R_T4 5 +#define R_T5 6 +#define R_T6 7 +#define R_T7 8 +#define R_S0 9 +#define R_S1 10 +#define R_S2 11 +#define R_S3 12 +#define R_S4 13 +#define R_S5 14 +#define R_S6 15 +#define R_A0 16 +#define R_A1 17 +#define R_A2 18 +#define R_A3 19 +#define R_A4 20 +#define R_A5 21 +#define R_T8 22 +#define R_T9 23 +#define R_T10 24 +#define R_T11 25 +#define R_RA 26 +#define R_T12 27 +#define R_SP 30 +#define R_ZERO 31 + +/* + * XXX - The rev id's are defined in <machine/ucontext.h> + */ +#define MC_FMT_OFFSET 73*8 /* offset to format from mcontext */ +#define REV0_SIGFRAME 0x0001 /* rev R0 sigcontext format */ +#define REV0_TRAPFRAME 0x0002 /* rev R0 trapframe format */ + +/* + * int _alpha_restore_context(const mcontext_t *mcp, + * intptr_t val, intptr_t *loc); + * + * The format of the context is verified at the beginning. + * Returns -1 if invalid format. + */ + .set noreorder +LEAF(_alpha_restore_context, 3) + LDGP(pv) + bne a0, Lsc1 /* argument null? */ +Lscbad: ldiq v0, -1 /* return -1 */ + br Lscend +Lsc1: ldq t1, MC_FMT_OFFSET(a0) /* is mcontext valid format? */ + ldiq t0, REV0_TRAPFRAME + cmpeq t0, t1, t0 /* is it trapframe format? */ + bne t0, Lsc_fp /* if so, check fp state */ + ldiq t0, REV0_SIGFRAME + cmpeq t0, t1, t0 /* is it sigcontext format? */ + beq t0, Lscbad + /* supposedly sigcontext format, check magic number */ + ldiq t0, 0xACEDBADE /* check magic number */ + ldq t1, ((R_ZERO + 1) * 8)(a0) /* magic in mc_regs[R_ZERO] */ + cmpeq t0, t1, t0 + beq t0, Lscbad + /* restore floating point regs first */ +Lsc_fp: ldq t0, ((71 + 1) * 8)(a0) /* if FP regs not saved, */ + beq t0, Lsc2 /* skip setting FP regs */ + ldt $f0, ((37 + 1) * 8)(a0) /* restore FP regs using */ + ldt $f1, ((38 + 1) * 8)(a0) /* hw name */ + ldt $f2, ((39 + 1) * 8)(a0) + ldt $f3, ((40 + 1) * 8)(a0) + ldt $f4, ((41 + 1) * 8)(a0) + ldt $f5, ((42 + 1) * 8)(a0) + ldt $f6, ((43 + 1) * 8)(a0) + ldt $f7, ((44 + 1) * 8)(a0) + ldt $f8, ((45 + 1) * 8)(a0) + ldt $f9, ((46 + 1) * 8)(a0) + ldt $f10, ((47 + 1) * 8)(a0) + ldt $f11, ((48 + 1) * 8)(a0) + ldt $f12, ((49 + 1) * 8)(a0) + ldt $f13, ((50 + 1) * 8)(a0) + ldt $f14, ((51 + 1) * 8)(a0) + ldt $f15, ((52 + 1) * 8)(a0) + ldt $f16, ((53 + 1) * 8)(a0) + ldt $f17, ((54 + 1) * 8)(a0) + ldt $f18, ((55 + 1) * 8)(a0) + ldt $f19, ((56 + 1) * 8)(a0) + ldt $f20, ((57 + 1) * 8)(a0) + ldt $f21, ((58 + 1) * 8)(a0) + ldt $f22, ((59 + 1) * 8)(a0) + ldt $f23, ((60 + 1) * 8)(a0) + ldt $f24, ((61 + 1) * 8)(a0) + ldt $f25, ((62 + 1) * 8)(a0) + ldt $f26, ((63 + 1) * 8)(a0) + ldt $f27, ((64 + 1) * 8)(a0) + .set noat + ldt $f28, ((65 + 1) * 8)(a0) + .set at + ldt $f29, ((66 + 1) * 8)(a0) + ldt $f30, ((67 + 1) * 8)(a0) + /* $f31 is hardwired zero */ + ldt ft0, ((69 + 1) * 8)(a0) /* restore FP control reg */ + mt_fpcr ft0 +Lsc2: ldiq t0, REV0_SIGFRAME /* check the context format */ + ldq t1, MC_FMT_OFFSET(a0) /* again. */ + cmpeq t0, t1, t0 /* is it sigcontext format? */ + bne t0, Lsc_sc + /* trapframe format */ + ldq v0, ((FRAME_V0 + 1) * 8)(a0) /* restore v0 */ + ldq t0, ((FRAME_T0 + 1) * 8)(a0) /* restore t0-t7 */ + ldq t1, ((FRAME_T1 + 1) * 8)(a0) + ldq t2, ((FRAME_T2 + 1) * 8)(a0) + ldq t3, ((FRAME_T3 + 1) * 8)(a0) + ldq t4, ((FRAME_T4 + 1) * 8)(a0) + ldq t5, ((FRAME_T5 + 1) * 8)(a0) + ldq t6, ((FRAME_T6 + 1) * 8)(a0) + ldq t7, ((FRAME_T7 + 1) * 8)(a0) + ldq s0, ((FRAME_S0 + 1) * 8)(a0) /* restore s0-s6 */ + ldq s1, ((FRAME_S1 + 1) * 8)(a0) + ldq s2, ((FRAME_S2 + 1) * 8)(a0) + ldq s3, ((FRAME_S3 + 1) * 8)(a0) + ldq s4, ((FRAME_S4 + 1) * 8)(a0) + ldq s5, ((FRAME_S5 + 1) * 8)(a0) + ldq s6, ((FRAME_S6 + 1) * 8)(a0) + ldq a4, ((FRAME_A4 + 1) * 8)(a0) /* restore a4, a5 */ + ldq a5, ((FRAME_A5 + 1) * 8)(a0) + ldq ra, ((FRAME_RA + 1) * 8)(a0) + ldq sp, ((FRAME_SP + 1) * 8)(a0) + subq sp, 16, sp /* save room on stack */ + ldq a3, ((FRAME_TRAPARG_A1 + 1) * 8)(a0) + stq a3, 0(a0) /* save a1 on stack */ + ldq a3, ((FRAME_TRAPARG_A2 + 1) * 8)(a0) + stq a3, 8(a0) /* save a2 on stack */ + .set noat + ldq at_reg, ((FRAME_PC + 1) * 8)(a0) /* PC at time of trap? */ + .set at + ldq a3, ((FRAME_A3 + 1) * 8)(a0) /* restore a3 */ + ldq a0, ((FRAME_TRAPARG_A0 + 1) * 8)(a0) /* restore a0 */ + br Lsc3 +Lsc_sc: /* sigcontext format */ + ldq v0, ((R_V0 + 1) * 8)(a0) /* restore v0 */ + ldq t0, ((R_T0 + 1) * 8)(a0) /* restore t0-t7 */ + ldq t1, ((R_T1 + 1) * 8)(a0) + ldq t2, ((R_T2 + 1) * 8)(a0) + ldq t3, ((R_T3 + 1) * 8)(a0) + ldq t4, ((R_T4 + 1) * 8)(a0) + ldq t5, ((R_T5 + 1) * 8)(a0) + ldq t6, ((R_T6 + 1) * 8)(a0) + ldq t7, ((R_T7 + 1) * 8)(a0) + ldq s0, ((R_S0 + 1) * 8)(a0) /* restore s0-s6 */ + ldq s1, ((R_S1 + 1) * 8)(a0) + ldq s2, ((R_S2 + 1) * 8)(a0) + ldq s3, ((R_S3 + 1) * 8)(a0) + ldq s4, ((R_S4 + 1) * 8)(a0) + ldq s5, ((R_S5 + 1) * 8)(a0) + ldq s6, ((R_S6 + 1) * 8)(a0) + ldq a4, ((R_A4 + 1) * 8)(a0) /* restore a4, a5 */ + ldq a5, ((R_A5 + 1) * 8)(a0) + ldq ra, ((R_RA + 1) * 8)(a0) + ldq sp, ((R_SP + 1) * 8)(a0) + subq sp, 16, sp /* save room on stack */ + ldq a3, ((R_A1 + 1) * 8)(a0) /* get a1 */ + stq a3, 0(a0) /* save a1 on stack */ + ldq a3, ((R_A2 + 1) * 8)(a0) /* get a2 */ + stq a3, 8(a0) /* save a2 on stack */ + ldq a3, ((R_A3 + 1) * 8)(a0) /* restore a3 */ + ldq a0, ((R_A0 + 1) * 8)(a0) /* restore a0 */ +Lsc3: beq a2, Lsc4 + stq a1, 0(a2) +Lsc4: ldq a1, 0(sp) /* restore a1, a2 */ + ldq a2, 8(sp) + addq sp, 16, sp /* restore stack */ +Lscend: RET +END(_alpha_restore_context) + + +/* + * int _alpha_save_context(mcontext_t *); + * + * Always save in trapframe format. Floating point registers are + * saved but may be optimized away later (see comments below). + */ +LEAF(_alpha_save_context, 1) + LDGP(pv) + bne a0, Lgc1 /* argument null? */ + ldiq v0, -1 /* return -1 */ + br Lgcend +Lgc1: ldiq v0, 1 /* save_context returns 1, */ + stq v0, ((FRAME_V0 + 1) * 8)(a0) /* so save 1 in v0 */ + stq t0, ((FRAME_T0 + 1) * 8)(a0) /* save t0-t7 */ + stq t1, ((FRAME_T1 + 1) * 8)(a0) + stq t2, ((FRAME_T2 + 1) * 8)(a0) + stq t3, ((FRAME_T3 + 1) * 8)(a0) + stq t4, ((FRAME_T4 + 1) * 8)(a0) + stq t5, ((FRAME_T5 + 1) * 8)(a0) + stq t6, ((FRAME_T6 + 1) * 8)(a0) + stq t7, ((FRAME_T7 + 1) * 8)(a0) + stq s0, ((FRAME_S0 + 1) * 8)(a0) /* save s0-s6 */ + stq s1, ((FRAME_S1 + 1) * 8)(a0) + stq s2, ((FRAME_S2 + 1) * 8)(a0) + stq s3, ((FRAME_S3 + 1) * 8)(a0) + stq s4, ((FRAME_S4 + 1) * 8)(a0) + stq s5, ((FRAME_S5 + 1) * 8)(a0) + stq s6, ((FRAME_S6 + 1) * 8)(a0) + stq a0, ((FRAME_TRAPARG_A0 + 1) * 8)(a0) /* save a0-a5 */ + stq a1, ((FRAME_TRAPARG_A1 + 1) * 8)(a0) + stq a2, ((FRAME_TRAPARG_A2 + 1) * 8)(a0) + stq a3, ((FRAME_A3 + 1) * 8)(a0) + stq a4, ((FRAME_A4 + 1) * 8)(a0) + stq a5, ((FRAME_A5 + 1) * 8)(a0) + stq ra, ((FRAME_RA + 1) * 8)(a0) + stq sp, ((FRAME_SP + 1) * 8)(a0) + ldiq t0, REV0_TRAPFRAME /* store trapframe format in */ + stq t0, MC_FMT_OFFSET(a0) /* ucp->uc-rev */ + /* + * XXX - Do we really need to save floating point registers? + * + * This is an explicit call to get the current context, so + * shouldn't the caller be done with the floating point registers? + * Contexts formed by involuntary switches, such as signal delivery, + * should have floating point registers saved by the kernel. + */ +#if 1 + stq zero, ((71 + 1) * 8)(a0) /* FP regs are not saved */ +#else + ldiq t0, 1 /* say we've used FP, */ + stq t0, ((71 + 1) * 8)(a0) /* mc_ownedfp = 1 */ + stt $f0, ((37 + 1) * 8)(a0) /* save first register, using */ + stt $f1, ((38 + 1) * 8)(a0) /* hw name etc. */ + stt $f2, ((39 + 1) * 8)(a0) + stt $f3, ((40 + 1) * 8)(a0) + stt $f4, ((41 + 1) * 8)(a0) + stt $f5, ((42 + 1) * 8)(a0) + stt $f6, ((43 + 1) * 8)(a0) + stt $f7, ((44 + 1) * 8)(a0) + stt $f8, ((45 + 1) * 8)(a0) + stt $f9, ((46 + 1) * 8)(a0) + stt $f10, ((47 + 1) * 8)(a0) + stt $f11, ((48 + 1) * 8)(a0) + stt $f12, ((49 + 1) * 8)(a0) + stt $f13, ((50 + 1) * 8)(a0) + stt $f14, ((51 + 1) * 8)(a0) + stt $f15, ((52 + 1) * 8)(a0) + stt $f16, ((53 + 1) * 8)(a0) + stt $f17, ((54 + 1) * 8)(a0) + stt $f18, ((55 + 1) * 8)(a0) + stt $f19, ((56 + 1) * 8)(a0) + stt $f20, ((57 + 1) * 8)(a0) + stt $f21, ((58 + 1) * 8)(a0) + stt $f22, ((59 + 1) * 8)(a0) + stt $f23, ((60 + 1) * 8)(a0) + stt $f24, ((61 + 1) * 8)(a0) + stt $f25, ((62 + 1) * 8)(a0) + stt $f26, ((63 + 1) * 8)(a0) + stt $f27, ((64 + 1) * 8)(a0) + .set noat + stt $f28, ((65 + 1) * 8)(a0) + .set at + stt $f29, ((66 + 1) * 8)(a0) + stt $f30, ((67 + 1) * 8)(a0) + /* $f31 is hardwired zero */ +#endif + mf_fpcr ft0 /* get FP control reg */ + stt ft0, ((69 + 1) * 8)(a0) /* and store it in mc_fpcr */ + stq zero, ((70 + 1) * 8)(a0) /* FP software control XXX */ + mov zero, v0 /* return zero */ +Lgcend: RET +END(_alpha_save_context) diff --git a/lib/libpthread/arch/alpha/alpha/enter_uts.S b/lib/libpthread/arch/alpha/alpha/enter_uts.S new file mode 100644 index 0000000..6de3bd7 --- /dev/null +++ b/lib/libpthread/arch/alpha/alpha/enter_uts.S @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2003 Daniel Eischen <deischen@freebsd.org> + * 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. Neither the name of the author 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 AUTHOR 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 AUTHOR 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> +__FBSDID("$FreeBSD$"); + +/* + * _alpha_enter_uts(struct kse_mailbox *km, kse_func_t uts, void *stack, + * long stacksz); + */ +LEAF(_alpha_enter_uts, 4) + addq a2, a3, a2 + ldiq a3, ~0xf + and a2, a3, a2 + mov a2, sp + mov a1, ra + mov a1, t12 + RET + END(_alpha_enter_uts) diff --git a/lib/libpthread/arch/alpha/alpha/pthread_md.c b/lib/libpthread/arch/alpha/alpha/pthread_md.c new file mode 100644 index 0000000..c8445b1 --- /dev/null +++ b/lib/libpthread/arch/alpha/alpha/pthread_md.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2003 Daniel Eischen <deischen@freebsd.org> + * 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. Neither the name of the author 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 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdlib.h> +#include <strings.h> +#include "pthread_md.h" + +/* + * The constructors. + */ +struct tcb * +_tcb_ctor(struct pthread *thread, int initial) +{ + struct tcb *tcb; + + if ((tcb = malloc(sizeof(struct tcb))) != NULL) { + bzero(tcb, sizeof(struct tcb)); + tcb->tcb_thread = thread; + /* Allocate TDV */ + } + return (tcb); +} + +void +_tcb_dtor(struct tcb *tcb) +{ + /* Free TDV */ + free(tcb); +} + +struct kcb * +_kcb_ctor(struct kse *kse) +{ + struct kcb *kcb; + + if ((kcb = malloc(sizeof(struct kcb))) != NULL) { + bzero(kcb, sizeof(struct kcb)); + kcb->kcb_faketcb.tcb_isfake = 1; + kcb->kcb_faketcb.tcb_tmbx.tm_flags = TMF_NOUPCALL; + kcb->kcb_curtcb = &kcb->kcb_faketcb; + kcb->kcb_kse = kse; + } + return (kcb); +} + +void +_kcb_dtor(struct kcb *kcb) +{ + free(kcb); +} diff --git a/lib/libpthread/arch/alpha/include/atomic_ops.h b/lib/libpthread/arch/alpha/include/atomic_ops.h new file mode 100644 index 0000000..7c3e62b --- /dev/null +++ b/lib/libpthread/arch/alpha/include/atomic_ops.h @@ -0,0 +1,75 @@ +/*- + * Copyright (c) 2003 Daniel Eischen <deischen@FreeBSD.org> + * 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. Neither the name of the author 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 AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef _ATOMIC_OPS_H_ +#define _ATOMIC_OPS_H_ + +/* + * Atomic swap: + * Atomic (tmp = *dst, *dst = val), then *res = tmp + * + * void atomic_swap_long(long *dst, long val, long *res); + */ +static inline void +atomic_swap_long(long *dst, long val, long *res) +{ + /* $1 and $2 are t0 and t1 respectively. */ + __asm __volatile ( + " ldq $1, %1\n" /* get cache line before lock */ + "1: ldq_l $1, %1\n" /* load *dst asserting lock */ + " mov %2, $2\n" /* save value to be swapped */ + " stq_c $2, %1\n" /* attempt the store; $2 clobbered */ + " beq $2, 1b\n" /* it didn't work, loop */ + " stq $1, %0\n" /* save value of *dst in *res */ + " mb \n" + : "+m"(*res) + : "m"(*dst), "r"(val) + : "memory", "$1", "$2"); /* clobber t0 and t1 */ +} + +static inline void +atomic_swap_int(int *dst, int val, int *res) +{ + /* $1 and $2 are t0 and t1 respectively. */ + __asm __volatile ( + " ldl $1, %1\n" /* get cache line before lock */ + "1: ldl_l $1, %1\n" /* load *dst asserting lock */ + " mov %2, $2\n" /* save value to be swapped */ + " stl_c $2, %1\n" /* attempt the store; $2 clobbered */ + " beq $2, 1b\n" /* it didn't work, loop */ + " stl $1, %0\n" /* save value of *dst in *res */ + " mb \n" + : "+m"(*res) + : "m"(*dst), "r"(val) + : "memory", "$1", "$2"); /* clobber t0 and t1 */ +} + +#define atomic_swap_ptr(d, v, r) \ + atomic_swap_long((long *)(d), (long)(v), (long *)(r)) + +#endif diff --git a/lib/libpthread/arch/alpha/include/pthread_md.h b/lib/libpthread/arch/alpha/include/pthread_md.h new file mode 100644 index 0000000..c7a85f1 --- /dev/null +++ b/lib/libpthread/arch/alpha/include/pthread_md.h @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2003 Marcel Moolenaar + * 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. + * + * 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. + * + * $FreeBSD$ + */ + +#ifndef _PTHREAD_MD_H_ +#define _PTHREAD_MD_H_ + +#include <sys/kse.h> +#include <stddef.h> +#include <ucontext.h> + +#define KSE_STACKSIZE 16384 +#define DTV_OFFSET offsetof(struct tcb, tcb_tp.tp_tdv) + +#define THR_GETCONTEXT(ucp) _alpha_save_context(&(ucp)->uc_mcontext) +#define THR_SETCONTEXT(ucp) PANIC("THR_SETCONTEXT() now in use!\n") + +#define PER_THREAD + +struct kcb; +struct kse; +struct pthread; +struct tcb; +struct tdv; /* We don't know what this is yet? */ + +/* + * tp points to one of these. We define the static TLS as an array + * of long double to enforce 16-byte alignment of the TLS memory, + * struct alpha_tp, struct tcb and also struct kcb. Both static and + * dynamic allocation of any of these structures will result in a + * valid, well-aligned thread pointer. + */ +struct alpha_tp { + struct tdv *tp_tdv; /* dynamic TLS */ + uint64_t _reserved_; + long double tp_tls[0]; /* static TLS */ +}; + +struct tcb { + struct kse_thr_mailbox tcb_tmbx; + struct pthread *tcb_thread; + struct kcb *tcb_curkcb; + long tcb_isfake; + struct alpha_tp tcb_tp; +}; + +struct kcb { + struct kse_mailbox kcb_kmbx; + struct tcb kcb_faketcb; + struct tcb *kcb_curtcb; + struct kse *kcb_kse; +}; + +#define _tp __builtin_thread_pointer() +#define _tcb ((struct tcb*)((char*)(_tp) - offsetof(struct tcb, tcb_tp))) + +/* + * The kcb and tcb constructors. + */ +struct tcb *_tcb_ctor(struct pthread *, int); +void _tcb_dtor(struct tcb *); +struct kcb *_kcb_ctor(struct kse *kse); +void _kcb_dtor(struct kcb *); + +/* Called from the KSE to set its private data. */ +static __inline void +_kcb_set(struct kcb *kcb) +{ + /* There is no thread yet; use the fake tcb. */ + __builtin_set_thread_pointer(&kcb->kcb_faketcb.tcb_tp); +} + +/* + * Get the current kcb. + * + * This can only be called while in a critical region; don't + * worry about having the kcb changed out from under us. + */ +static __inline struct kcb * +_kcb_get(void) +{ + return (_tcb->tcb_curkcb); +} + +/* + * Enter a critical region. + * + * Read and clear km_curthread in the kse mailbox. + */ +static __inline struct kse_thr_mailbox * +_kcb_critical_enter(void) +{ + struct kse_thr_mailbox *crit; + uint32_t flags; + + if (_tcb->tcb_isfake != 0) { + /* + * We already are in a critical region since + * there is no current thread. + */ + crit = NULL; + } else { + flags = _tcb->tcb_tmbx.tm_flags; + _tcb->tcb_tmbx.tm_flags |= TMF_NOUPCALL; + crit = _tcb->tcb_curkcb->kcb_kmbx.km_curthread; + _tcb->tcb_curkcb->kcb_kmbx.km_curthread = NULL; + _tcb->tcb_tmbx.tm_flags = flags; + } + return (crit); +} + +static __inline void +_kcb_critical_leave(struct kse_thr_mailbox *crit) +{ + /* No need to do anything if this is a fake tcb. */ + if (_tcb->tcb_isfake == 0) + _tcb->tcb_curkcb->kcb_kmbx.km_curthread = crit; +} + +static __inline int +_kcb_in_critical(void) +{ + uint32_t flags; + int ret; + + if (_tcb->tcb_isfake != 0) { + /* + * We are in a critical region since there is no + * current thread. + */ + ret = 1; + } else { + flags = _tcb->tcb_tmbx.tm_flags; + _tcb->tcb_tmbx.tm_flags |= TMF_NOUPCALL; + ret = (_tcb->tcb_curkcb->kcb_kmbx.km_curthread == NULL); + _tcb->tcb_tmbx.tm_flags = flags; + } + return (ret); +} + +static __inline void +_tcb_set(struct kcb *kcb, struct tcb *tcb) +{ + if (tcb == NULL) + tcb = &kcb->kcb_faketcb; + kcb->kcb_curtcb = tcb; + tcb->tcb_curkcb = kcb; + __builtin_set_thread_pointer(&tcb->tcb_tp); +} + +static __inline struct tcb * +_tcb_get(void) +{ + return (_tcb); +} + +static __inline struct pthread * +_get_curthread(void) +{ + return (_tcb->tcb_thread); +} + +/* + * Get the current kse. + * + * Like _kcb_get(), this can only be called while in a critical region. + */ +static __inline struct kse * +_get_curkse(void) +{ + return (_tcb->tcb_curkcb->kcb_kse); +} + +void _alpha_enter_uts(struct kse_mailbox *km, kse_func_t uts, void *stack, + size_t stacksz); +int _alpha_restore_context(mcontext_t *mc, intptr_t val, intptr_t *loc); +int _alpha_save_context(mcontext_t *mc); + +static __inline int +_thread_enter_uts(struct tcb *tcb, struct kcb *kcb) +{ + if (_alpha_save_context(&tcb->tcb_tmbx.tm_context.uc_mcontext) == 0) { + /* Make the fake tcb the current thread. */ + kcb->kcb_curtcb = &kcb->kcb_faketcb; + __builtin_set_thread_pointer(&kcb->kcb_faketcb.tcb_tp); + _alpha_enter_uts(&kcb->kcb_kmbx, kcb->kcb_kmbx.km_func, + kcb->kcb_kmbx.km_stack.ss_sp, + kcb->kcb_kmbx.km_stack.ss_size); + /* We should not reach here. */ + return (-1); + } + return (0); +} + +static __inline int +_thread_switch(struct kcb *kcb, struct tcb *tcb, int setmbox) +{ + extern int _libkse_debug; + + _tcb_set(kcb, tcb); + if (_libkse_debug == 0) { + tcb->tcb_tmbx.tm_lwp = kcb->kcb_kmbx.km_lwp; + if (setmbox != 0) + _alpha_restore_context( + &tcb->tcb_tmbx.tm_context.uc_mcontext, + (intptr_t)&tcb->tcb_tmbx, + (intptr_t *)&kcb->kcb_kmbx.km_curthread); + else + _alpha_restore_context( + &tcb->tcb_tmbx.tm_context.uc_mcontext, + 0, NULL); + } else { + if (setmbox) + kse_switchin(&tcb->tcb_tmbx, KSE_SWITCHIN_SETTMBX); + else + kse_switchin(&tcb->tcb_tmbx, 0); + } + + /* We should not reach here. */ + return (-1); +} + +#endif /* _PTHREAD_MD_H_ */ diff --git a/lib/libpthread/arch/amd64/Makefile.inc b/lib/libpthread/arch/amd64/Makefile.inc new file mode 100644 index 0000000..c8b0362 --- /dev/null +++ b/lib/libpthread/arch/amd64/Makefile.inc @@ -0,0 +1,5 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/arch/${MACHINE_ARCH}/${MACHINE_ARCH} + +SRCS+= context.S enter_uts.S pthread_md.c diff --git a/lib/libpthread/arch/amd64/amd64/context.S b/lib/libpthread/arch/amd64/amd64/context.S new file mode 100644 index 0000000..6a6b558 --- /dev/null +++ b/lib/libpthread/arch/amd64/amd64/context.S @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2003 Daniel Eischen <deischen@freebsd.org>. + * 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. Neither the name of the author 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 DANIEL EISCHEN 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 AUTHOR 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> +__FBSDID("$FreeBSD$"); + +/* + * The following notes ("cheat sheet") was provided by Peter Wemm. + * + * scratch: + * rax (1st return) + * rcx (4th arg) + * rdx (3rd arg, 2nd return) + * rsi (2nd arg) + * rdi (1st arg) + * r8 (5th arg) + * r9 (6th arg) + * r10 (temp, static chain?) + * r11 (temp) + * + * preserved: + * rbx (base pointer) + * rsp (stack) + * rbp (frame) + * r12-r15 (general) + * + * calls: + * rdi 1 + * rsi 2 + * rdx 3 + * rcx 4 + * r8 5 + * r9 6 + * + * return: + * rax 1 + * rdx 2 + * + * This means: + * arg1 goes in %rdi, arg2 in %rsi, etc. return value is %rax (and + * secondary return, eg: pipe(2), in %rdx) %rcx,%rsi,%rdi etc are + * trashed by making a call to something. %rbx,%rbp,%r12-15 are the + * only registers preserved across a call. Note that unlike i386, + * %rsi and %rdi are scratch rather than preserved. FPU is + * different, args are in SSE registers rather than the x87 stack. + * + * Aside from the register calling conventions, amd64 can be treated + * very much like i386. Things like setjmp/longjmp etc were literal + * translations from i386 but with the register names updated, etc. + * The main gotcha is that FPU save/restore is in SSE format, which + * means a sparse 512 byte FPU context. + */ + + +/* + * Where do we define these? + */ +#define MC_SIZE 800 /* sizeof mcontext_t */ +#define MC_LEN_OFFSET (25*8) /* offset to mc_len from mcontext */ +#define MC_FPFMT_OFFSET (26*8) /* offset to mc_fpformat from mcontext */ +#define MC_FPFMT_NODEV 0x10000 +#define MC_OWNEDFP_OFFSET (27*8) /* offset to mc_ownedfp from mcontext */ +#define MC_OWNEDFP_NONE 0x20000 +#define MC_OWNEDFP_FPU 0x20001 +#define MC_OWNEDFP_PCB 0x20002 +#define MC_FPREGS_OFFSET (28*8) /* offset to FP registers */ +#define MC_FP_CW_OFFSET (28*8) /* offset to FP control word */ + +#define MC_RDI (1 * 8) +#define MC_RSI (2 * 8) +#define MC_RDX (3 * 8) +#define MC_RCX (4 * 8) +#define MC_R8 (5 * 8) +#define MC_R9 (6 * 8) +#define MC_RAX (7 * 8) +#define MC_RBX (8 * 8) +#define MC_RBP (9 * 8) +#define MC_R10 (10 * 8) +#define MC_R11 (11 * 8) +#define MC_R12 (12 * 8) +#define MC_R13 (13 * 8) +#define MC_R14 (14 * 8) +#define MC_R15 (15 * 8) +#define MC_FLAGS (18 * 8) +#define MC_RIP (20 * 8) +#define MC_CS (21 * 8) +#define MC_RFLAGS (22 * 8) +#define MC_RSP (23 * 8) +#define MC_SS (24 * 8) + +#define REDZONE 128 /* size of the red zone */ + +/* + * _amd64_ctx_save(mcontext_t *mcp) + * + * No values are saved to mc_trapno, mc_addr, mc_err and mc_cs. + * For the FPU state, only the floating point control word is stored. + */ +ENTRY(_amd64_save_context) + cmpq $0, %rdi /* check for null pointer */ + jne 1f + movq $-1, %rax + jmp 2f +1: movq %rdi, MC_RDI(%rdi) + movq %rsi, MC_RSI(%rdi) + movq %rdx, MC_RDX(%rdi) + movq %rcx, MC_RCX(%rdi) + movq %r8, MC_R8(%rdi) + movq %r9, MC_R9(%rdi) + movq $1, MC_RAX(%rdi) /* return 1 when restored */ + movq %rbx, MC_RBX(%rdi) + movq %rbp, MC_RBP(%rdi) + movq %r10, MC_R10(%rdi) + movq %r11, MC_R11(%rdi) + movq %r12, MC_R12(%rdi) + movq %r13, MC_R13(%rdi) + movq %r14, MC_R14(%rdi) + movq %r15, MC_R15(%rdi) + movq (%rsp), %rax /* get return address */ + movq %rax, MC_RIP(%rdi) /* save return address (%rip) */ + pushfq /* get flags */ + popq %rax + movq %rax, MC_RFLAGS(%rdi) /* save flags */ + movq %rsp, %rax /* setcontext pushes the return */ + addq $8, %rax /* address onto the stack; */ + movq %rax, MC_RSP(%rdi) /* account for this -- ???. */ + movw %ss, MC_SS(%rdi) + fnstcw MC_FP_CW_OFFSET(%rdi) /* save FPU control word */ + movq $MC_OWNEDFP_NONE, MC_OWNEDFP_OFFSET(%rdi) /* no FP */ + movq $MC_FPFMT_NODEV, MC_FPFMT_OFFSET(%rdi) + movq $MC_SIZE, MC_LEN_OFFSET(%rdi) + xorq %rax, %rax /* return 0 */ +2: ret + +/* + * _amd64_ctx_restore(mcontext_t *mcp, intptr_t val, intptr_t *loc); + */ +ENTRY(_amd64_restore_context) + cmpq $0, %rdi /* check for null pointer */ + jne 1f + movq $-1, %rax + jmp 2f +1: cmpq $MC_SIZE, MC_LEN_OFFSET(%rdi) /* is context valid? */ + je 2f + movq $-1, %rax /* bzzzt, invalid context */ + ret +2: movq MC_RCX(%rdi), %rcx + movq MC_R8(%rdi), %r8 + movq MC_R9(%rdi), %r9 + movq MC_RBX(%rdi), %rbx + movq MC_RBP(%rdi), %rbp + movq MC_R10(%rdi), %r10 + movq MC_R11(%rdi), %r11 + movq MC_R12(%rdi), %r12 + movq MC_R13(%rdi), %r13 + movq MC_R14(%rdi), %r14 + movq MC_R15(%rdi), %r15 + /* + * if (mc_fpowned == MC_OWNEDFP_FPU || mc_fpowned == MC_OWNEDFP_PCB) + * restore XMM/SSE FP register format + */ + cmpq $MC_OWNEDFP_NONE, MC_OWNEDFP_OFFSET(%rdi) + je 4f + cmpq $MC_OWNEDFP_PCB, MC_OWNEDFP_OFFSET(%rdi) + je 3f + cmpq $MC_OWNEDFP_FPU, MC_OWNEDFP_OFFSET(%rdi) + jne 4f +3: fxrstor MC_FPREGS_OFFSET(%rdi) /* restore XMM FP regs */ + jmp 5f +4: fninit + fldcw MC_FP_CW_OFFSET(%rdi) +5: movq MC_RSP(%rdi), %rsp /* switch to context stack */ + subq $REDZONE, %rsp + movq MC_RIP(%rdi), %rax /* return address on stack */ + pushq %rax + movq MC_RDI(%rdi), %rax /* rdi on stack */ + pushq %rax + movq MC_RDX(%rdi), %rax /* rdx on stack */ + pushq %rax + movq MC_RSI(%rdi), %rax /* rsi on stack */ + pushq %rax + movq MC_RFLAGS(%rdi), %rax /* flags on stack*/ + pushq %rax + movq MC_RAX(%rdi), %rax /* restore rax */ + /* At this point we're done with the context. */ + cmpq $0, %rdx /* set *loc to val */ + je 6f + movq %rsi, (%rdx) +6: popfq /* restore flags */ + popq %rsi /* restore rsi, rdx, and rdi */ + popq %rdx + popq %rdi + ret $REDZONE + diff --git a/lib/libpthread/arch/amd64/amd64/enter_uts.S b/lib/libpthread/arch/amd64/amd64/enter_uts.S new file mode 100644 index 0000000..fb0df87 --- /dev/null +++ b/lib/libpthread/arch/amd64/amd64/enter_uts.S @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2003 Daniel Eischen <deischen@freebsd.org>. + * 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. Neither the name of the author 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 DANIEL EISCHEN 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 AUTHOR 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> +__FBSDID("$FreeBSD$"); + + +/* + * _amd64_enter_uts(struct kse_mailbox *km, kse_func_t uts, void *stack, + * size_t stacksz); + */ +ENTRY(_amd64_enter_uts) + addq %rcx, %rdx /* get stack base */ + andq $~0xf, %rdx /* align to 16 bytes */ + movq %rdx, %rsp /* switch to UTS stack */ + movq %rdx, %rbp /* set frame */ + callq *%rsi + ret diff --git a/lib/libpthread/arch/amd64/amd64/pthread_md.c b/lib/libpthread/arch/amd64/amd64/pthread_md.c new file mode 100644 index 0000000..3aceec7 --- /dev/null +++ b/lib/libpthread/arch/amd64/amd64/pthread_md.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2003 Daniel Eischen <deischen@freebsd.org> + * 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. Neither the name of the author 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 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. + * + * $FreeBSD$ + */ + +#include <stdlib.h> +#include <strings.h> +#include "rtld_tls.h" +#include "pthread_md.h" + +/* + * The constructors. + */ +struct tcb * +_tcb_ctor(struct pthread *thread, int initial) +{ + struct tcb *tcb; + void *oldtls; + + if (initial) { + __asm __volatile("movq %%fs:0, %0" : "=r" (oldtls)); + } else { + oldtls = NULL; + } + + tcb = _rtld_allocate_tls(oldtls, sizeof(struct tcb), 16); + if (tcb) { + tcb->tcb_thread = thread; + bzero(&tcb->tcb_tmbx, sizeof(tcb->tcb_tmbx)); + } + + return (tcb); +} + +void +_tcb_dtor(struct tcb *tcb) +{ + _rtld_free_tls(tcb, sizeof(struct tcb), 16); +} + +struct kcb * +_kcb_ctor(struct kse *kse) +{ + struct kcb *kcb; + + kcb = malloc(sizeof(struct kcb)); + if (kcb != NULL) { + bzero(kcb, sizeof(struct kcb)); + kcb->kcb_self = kcb; + kcb->kcb_kse = kse; + } + return (kcb); +} + +void +_kcb_dtor(struct kcb *kcb) +{ + free(kcb); +} diff --git a/lib/libpthread/arch/amd64/include/atomic_ops.h b/lib/libpthread/arch/amd64/include/atomic_ops.h new file mode 100644 index 0000000..980eb8e --- /dev/null +++ b/lib/libpthread/arch/amd64/include/atomic_ops.h @@ -0,0 +1,57 @@ +/*- + * Copyright (c) 2001 Daniel Eischen <deischen@FreeBSD.org> + * 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. Neither the name of the author 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 AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef _ATOMIC_OPS_H_ +#define _ATOMIC_OPS_H_ + +/* + * Atomic swap: + * Atomic (tmp = *dst, *dst = val), then *res = tmp + * + * void atomic_swap64(intptr_t *dst, intptr_t val, intptr_t *res); + */ +static inline void +atomic_swap64(intptr_t *dst, intptr_t val, intptr_t *res) +{ + __asm __volatile( + "xchgq %2, %1; movq %2, %0" + : "=m" (*res) : "m" (*dst), "r" (val) : "memory"); +} + +static inline void +atomic_swap_int(int *dst, int val, int *res) +{ + __asm __volatile( + "xchgl %2, %1; movl %2, %0" + : "=m" (*res) : "m" (*dst), "r" (val) : "memory"); +} + +#define atomic_swap_ptr(d, v, r) \ + atomic_swap64((intptr_t *)(d), (intptr_t)(v), (intptr_t *)(r)) + +#endif diff --git a/lib/libpthread/arch/amd64/include/pthread_md.h b/lib/libpthread/arch/amd64/include/pthread_md.h new file mode 100644 index 0000000..a7da5df --- /dev/null +++ b/lib/libpthread/arch/amd64/include/pthread_md.h @@ -0,0 +1,268 @@ +/*- + * Copyright (C) 2003 David Xu <davidxu@freebsd.org> + * Copyright (c) 2001 Daniel Eischen <deischen@freebsd.org> + * 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. Neither the name of the author 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 AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ +/* + * Machine-dependent thread prototypes/definitions for the thread kernel. + */ +#ifndef _PTHREAD_MD_H_ +#define _PTHREAD_MD_H_ + +#include <stddef.h> +#include <sys/types.h> +#include <sys/kse.h> +#include <machine/sysarch.h> +#include <ucontext.h> + +#define KSE_STACKSIZE 16384 +#define DTV_OFFSET offsetof(struct tcb, tcb_dtv) + +#define THR_GETCONTEXT(ucp) \ + (void)_amd64_save_context(&(ucp)->uc_mcontext) +#define THR_SETCONTEXT(ucp) \ + (void)_amd64_restore_context(&(ucp)->uc_mcontext, 0, NULL) + +#define PER_KSE +#undef PER_THREAD + +struct kse; +struct pthread; +struct tdv; + +/* + * %fs points to a struct kcb. + */ +struct kcb { + struct tcb *kcb_curtcb; + struct kcb *kcb_self; /* self reference */ + struct kse *kcb_kse; + struct kse_mailbox kcb_kmbx; +}; + +struct tcb { + struct tcb *tcb_self; /* required by rtld */ + void *tcb_dtv; /* required by rtld */ + struct pthread *tcb_thread; + void *tcb_spare[1]; /* align tcb_tmbx to 16 bytes */ + struct kse_thr_mailbox tcb_tmbx; +}; + +/* + * Evaluates to the byte offset of the per-kse variable name. + */ +#define __kcb_offset(name) __offsetof(struct kcb, name) + +/* + * Evaluates to the type of the per-kse variable name. + */ +#define __kcb_type(name) __typeof(((struct kcb *)0)->name) + +/* + * Evaluates to the value of the per-kse variable name. + */ +#define KCB_GET64(name) ({ \ + __kcb_type(name) __result; \ + \ + u_long __i; \ + __asm __volatile("movq %%fs:%1, %0" \ + : "=r" (__i) \ + : "m" (*(u_long *)(__kcb_offset(name)))); \ + __result = (__kcb_type(name))__i; \ + \ + __result; \ +}) + +/* + * Sets the value of the per-kse variable name to value val. + */ +#define KCB_SET64(name, val) ({ \ + __kcb_type(name) __val = (val); \ + \ + u_long __i; \ + __i = (u_long)__val; \ + __asm __volatile("movq %1,%%fs:%0" \ + : "=m" (*(u_long *)(__kcb_offset(name))) \ + : "r" (__i)); \ +}) + +static __inline u_long +__kcb_readandclear64(volatile u_long *addr) +{ + u_long result; + + __asm __volatile ( + " xorq %0, %0;" + " xchgq %%fs:%1, %0;" + "# __kcb_readandclear64" + : "=&r" (result) + : "m" (*addr)); + return (result); +} + +#define KCB_READANDCLEAR64(name) ({ \ + __kcb_type(name) __result; \ + \ + __result = (__kcb_type(name)) \ + __kcb_readandclear64((u_long *)__kcb_offset(name)); \ + __result; \ +}) + + +#define _kcb_curkcb() KCB_GET64(kcb_self) +#define _kcb_curtcb() KCB_GET64(kcb_curtcb) +#define _kcb_curkse() ((struct kse *)KCB_GET64(kcb_kmbx.km_udata)) +#define _kcb_get_tmbx() KCB_GET64(kcb_kmbx.km_curthread) +#define _kcb_set_tmbx(value) KCB_SET64(kcb_kmbx.km_curthread, (void *)value) +#define _kcb_readandclear_tmbx() KCB_READANDCLEAR64(kcb_kmbx.km_curthread) + +/* + * The constructors. + */ +struct tcb *_tcb_ctor(struct pthread *, int); +void _tcb_dtor(struct tcb *tcb); +struct kcb *_kcb_ctor(struct kse *); +void _kcb_dtor(struct kcb *); + +/* Called from the KSE to set its private data. */ +static __inline void +_kcb_set(struct kcb *kcb) +{ + amd64_set_fsbase(kcb); +} + +/* Get the current kcb. */ +static __inline struct kcb * +_kcb_get(void) +{ + return (_kcb_curkcb()); +} + +static __inline struct kse_thr_mailbox * +_kcb_critical_enter(void) +{ + struct kse_thr_mailbox *crit; + + crit = _kcb_readandclear_tmbx(); + return (crit); +} + +static __inline void +_kcb_critical_leave(struct kse_thr_mailbox *crit) +{ + _kcb_set_tmbx(crit); +} + +static __inline int +_kcb_in_critical(void) +{ + return (_kcb_get_tmbx() == NULL); +} + +static __inline void +_tcb_set(struct kcb *kcb, struct tcb *tcb) +{ + kcb->kcb_curtcb = tcb; +} + +static __inline struct tcb * +_tcb_get(void) +{ + return (_kcb_curtcb()); +} + +static __inline struct pthread * +_get_curthread(void) +{ + struct tcb *tcb; + + tcb = _kcb_curtcb(); + if (tcb != NULL) + return (tcb->tcb_thread); + else + return (NULL); +} + +static __inline struct kse * +_get_curkse(void) +{ + return ((struct kse *)_kcb_curkse()); +} + +void _amd64_enter_uts(struct kse_mailbox *km, kse_func_t uts, void *stack, + size_t stacksz); +int _amd64_restore_context(mcontext_t *mc, intptr_t val, intptr_t *loc); +int _amd64_save_context(mcontext_t *mc); + +static __inline int +_thread_enter_uts(struct tcb *tcb, struct kcb *kcb) +{ + int ret; + + ret = _amd64_save_context(&tcb->tcb_tmbx.tm_context.uc_mcontext); + if (ret == 0) { + _amd64_enter_uts(&kcb->kcb_kmbx, kcb->kcb_kmbx.km_func, + kcb->kcb_kmbx.km_stack.ss_sp, + kcb->kcb_kmbx.km_stack.ss_size); + /* We should not reach here. */ + return (-1); + } + else if (ret < 0) + return (-1); + return (0); +} + +static __inline int +_thread_switch(struct kcb *kcb, struct tcb *tcb, int setmbox) +{ + extern int _libkse_debug; + + if ((kcb == NULL) || (tcb == NULL)) + return (-1); + kcb->kcb_curtcb = tcb; + + if (_libkse_debug == 0) { + tcb->tcb_tmbx.tm_lwp = kcb->kcb_kmbx.km_lwp; + if (setmbox != 0) + _amd64_restore_context( + &tcb->tcb_tmbx.tm_context.uc_mcontext, + (intptr_t)&tcb->tcb_tmbx, + (intptr_t *)&kcb->kcb_kmbx.km_curthread); + else + _amd64_restore_context( + &tcb->tcb_tmbx.tm_context.uc_mcontext, + 0, NULL); + /* We should not reach here. */ + } else { + if (setmbox) + kse_switchin(&tcb->tcb_tmbx, KSE_SWITCHIN_SETTMBX); + else + kse_switchin(&tcb->tcb_tmbx, 0); + } + + return (-1); +} +#endif diff --git a/lib/libpthread/arch/arm/Makefile.inc b/lib/libpthread/arch/arm/Makefile.inc new file mode 100644 index 0000000..ced7063 --- /dev/null +++ b/lib/libpthread/arch/arm/Makefile.inc @@ -0,0 +1,7 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/arch/${MACHINE_ARCH}/${MACHINE_ARCH} + +CFLAGS+=-DSYSTEM_SCOPE_ONLY + +SRCS+= pthread_md.c context.S diff --git a/lib/libpthread/arch/arm/arm/context.S b/lib/libpthread/arch/arm/arm/context.S new file mode 100644 index 0000000..c638804 --- /dev/null +++ b/lib/libpthread/arch/arm/arm/context.S @@ -0,0 +1,79 @@ +/* + * Copyright (c) Olivier Houchard + * 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. Neither the name of the author 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 AUTHOR 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 AUTHOR 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> +__FBSDID("$FreeBSD$"); + +/* + * int thr_setcontext(mcontext_t *mcp, intptr_t val, intptr_t *loc) + * + * Restores the context in mcp. + * + * Returns 0 if there are no errors; -1 otherwise + */ + +.weak _C_LABEL(_thr_setcontext) +.set _C_LABEL(_thr_setcontext), _C_LABEL(__thr_setcontext) + +ENTRY(__thr_setcontext) +/* Check for NULL pointer. */ + cmp r0, #0 + moveq r0, #-1 + moveq pc, lr + cmp r2, #0 + strne r1, [r2] + ldr r1, [r0, #(16 * 4)] /* CPSR */ + msr cpsr, r1 + ldmia r0, {r0-r15} + mov pc, lr + /* XXX: FP bits ? */ + +/* + * int thr_getcontext(mcontext_t *mcp); + * + * Returns -1 if there is an error, 0 no errors; 1 upon return + * from a setcontext(). + */ +.weak _C_LABEL(_thr_getcontext) +.set _C_LABEL(_thr_getcontext), _C_LABEL(__thr_getcontext) + +ENTRY(__thr_getcontext) +/* Check for NULL pointer. */ + cmp r0, #0 + moveq r0, #-1 + moveq pc, lr + stmia r0, {r1-r14} + mov r1, #1 + str r1, [r0] /* Return 1 from setcontext */ + str lr, [r0, #(15 * 4)] /* PC */ + mrs r1, cpsr + str r1, [r0, #(16 * 4)] /* CPSR */ + mov r0, #0 /* Return 0. */ + mov pc, lr + +ENTRY(_arm_enter_uts) + add sp, r2, r3 /* Stack addr + size. */ + mov pc, r1 diff --git a/lib/libpthread/arch/arm/arm/pthread_md.c b/lib/libpthread/arch/arm/arm/pthread_md.c new file mode 100644 index 0000000..72426d4 --- /dev/null +++ b/lib/libpthread/arch/arm/arm/pthread_md.c @@ -0,0 +1,86 @@ +/*- + * Copyright (C) 2003 Jake Burkholder <jake@freebsd.org> + * Copyright (C) 2003 David Xu <davidxu@freebsd.org> + * Copyright (c) 2001,2003 Daniel Eischen <deischen@freebsd.org> + * 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. Neither the name of the author 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 AUTHOR 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 AUTHOR 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> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> + +#include <unistd.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <ucontext.h> + +#include <machine/sysarch.h> + +#include "pthread_md.h" + +struct arm_tp **arm_tp = (struct arm_tp **)ARM_TP_ADDRESS; + +struct tcb * +_tcb_ctor(struct pthread *thread, int initial) +{ + struct tcb *tcb; + + if ((tcb = malloc(sizeof(struct tcb)))) { + bzero(tcb, sizeof(struct tcb)); + tcb->tcb_thread = thread; + /* XXX - Allocate tdv/tls */ + } + return (tcb); +} + +void +_tcb_dtor(struct tcb *tcb) +{ + + free(tcb); +} + +struct kcb * +_kcb_ctor(struct kse *kse) +{ + struct kcb *kcb; + + kcb = malloc(sizeof(struct kcb)); + if (kcb != NULL) { + bzero(kcb, sizeof(struct kcb)); + kcb->kcb_faketcb.tcb_isfake = 1; + kcb->kcb_faketcb.tcb_tmbx.tm_flags = TMF_NOUPCALL; + kcb->kcb_curtcb = &kcb->kcb_faketcb; + kcb->kcb_kse = kse; + } + return (kcb); +} + +void +_kcb_dtor(struct kcb *kcb) +{ + free(kcb); +} diff --git a/lib/libpthread/arch/arm/include/atomic_ops.h b/lib/libpthread/arch/arm/include/atomic_ops.h new file mode 100644 index 0000000..3a209b3 --- /dev/null +++ b/lib/libpthread/arch/arm/include/atomic_ops.h @@ -0,0 +1,53 @@ +/*- + * Copyright (c) 2001 Daniel Eischen <deischen@FreeBSD.org> + * 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. Neither the name of the author 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 AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef _ATOMIC_OPS_H_ +#define _ATOMIC_OPS_H_ + +#include <machine/atomic.h> +#include "thr_private.h" + +/* + * Atomic swap: + * Atomic (tmp = *dst, *dst = val), then *res = tmp + * + * void atomic_swap32(intptr_t *dst, intptr_t val, intptr_t *res); + */ +static inline void +atomic_swap32(intptr_t *dst, intptr_t val, intptr_t *res) +{ + *res = __swp(val, dst); +} + +#define atomic_swap_ptr(d, v, r) \ + atomic_swap32((intptr_t *)d, (intptr_t)v, (intptr_t *)r) + +#define atomic_swap_int(d, v, r) \ + atomic_swap32((intptr_t *)d, (intptr_t)v, (intptr_t *)r) +#endif + diff --git a/lib/libpthread/arch/arm/include/pthread_md.h b/lib/libpthread/arch/arm/include/pthread_md.h new file mode 100644 index 0000000..857fa1b --- /dev/null +++ b/lib/libpthread/arch/arm/include/pthread_md.h @@ -0,0 +1,257 @@ +/*- + * Copyright (c) 2003 Jake Burkholder <jake@freebsd.org>. + * Copyright (c) 2003 Marcel Moolenaar + * 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. + * + * 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. + * + * $FreeBSD$ + */ + +/* + * Machine-dependent thread prototypes/definitions for the thread kernel. + */ +#ifndef _PTHREAD_MD_H_ +#define _PTHREAD_MD_H_ + +#include <sys/kse.h> +#include <stddef.h> +#include <ucontext.h> + +#define KSE_STACKSIZE 16384 +#define DTV_OFFSET offsetof(struct tcb, tcb_tp.tp_tdv) + +int _thr_setcontext(mcontext_t *, intptr_t, intptr_t *); +int _thr_getcontext(mcontext_t *); + +#define THR_GETCONTEXT(ucp) _thr_getcontext(&(ucp)->uc_mcontext) +#define THR_SETCONTEXT(ucp) _thr_setcontext(&(ucp)->uc_mcontext, 0, NULL) + +#define PER_THREAD + +struct kcb; +struct kse; +struct pthread; +struct tcb; +struct tdv; /* We don't know what this is yet? */ + + +/* + * %r6 points to one of these. We define the static TLS as an array + * of long double to enforce 16-byte alignment of the TLS memory. + * + * XXX - Both static and dynamic allocation of any of these structures + * will result in a valid, well-aligned thread pointer??? + */ +struct arm_tp { + struct tdv *tp_tdv; /* dynamic TLS */ +}; + +struct tcb { + struct pthread *tcb_thread; + struct kcb *tcb_curkcb; + uint32_t tcb_isfake; + struct kse_thr_mailbox tcb_tmbx; /* needs 32-byte alignment */ + struct arm_tp tcb_tp; +}; + +struct kcb { + struct kse_mailbox kcb_kmbx; + struct tcb kcb_faketcb; + struct tcb *kcb_curtcb; + struct kse *kcb_kse; +}; + +extern struct arm_tp **arm_tp; +#define _tp (*arm_tp) + +#define _tcb ((struct tcb*)((char*)(_tp) - offsetof(struct tcb, tcb_tp))) + +/* + * The kcb and tcb constructors. + */ +struct tcb *_tcb_ctor(struct pthread *, int); +void _tcb_dtor(struct tcb *); +struct kcb *_kcb_ctor(struct kse *kse); +void _kcb_dtor(struct kcb *); + +static __inline uint32_t +__kcb_swp(uint32_t val, void *ptr) +{ + + __asm __volatile("swp %0, %1, [%2]" + : "=r" (val) : "r" (val) , "r" (ptr) : "memory"); + return (val); +} + +/* Called from the KSE to set its private data. */ +static __inline void +_kcb_set(struct kcb *kcb) +{ + /* There is no thread yet; use the fake tcb. */ + __kcb_swp((uint32_t)&kcb->kcb_faketcb.tcb_tp, &_tp); +} + +/* + * Get the current kcb. + * + * This can only be called while in a critical region; don't + * worry about having the kcb changed out from under us. + */ +static __inline struct kcb * +_kcb_get(void) +{ + return (_tcb->tcb_curkcb); +} + +/* + * Enter a critical region. + * + * Read and clear km_curthread in the kse mailbox. + */ +static __inline struct kse_thr_mailbox * +_kcb_critical_enter(void) +{ + struct kse_thr_mailbox *crit; + + if (_tcb->tcb_isfake) + return (NULL); + crit = (struct kse_thr_mailbox *)__kcb_swp((uint32_t)NULL, + &_tcb->tcb_curkcb->kcb_kmbx.km_curthread); + return (crit); +} + +static __inline void +_kcb_critical_leave(struct kse_thr_mailbox *crit) +{ + + if (_tcb->tcb_isfake == 0) + __kcb_swp((uint32_t)crit, + &_tcb->tcb_curkcb->kcb_kmbx.km_curthread); +} + +static __inline int +_kcb_in_critical(void) +{ + uint32_t flags; + int ret; + + return (_tcb->tcb_curkcb->kcb_kmbx.km_curthread == NULL); + if (_tcb->tcb_isfake != 0) { + /* + * We are in a critical region since there is no + * current thread. + */ + ret = 1; + } else { + flags = _tcb->tcb_tmbx.tm_flags; + _tcb->tcb_tmbx.tm_flags |= TMF_NOUPCALL; + ret = (_tcb->tcb_curkcb->kcb_kmbx.km_curthread == NULL); + _tcb->tcb_tmbx.tm_flags = flags; + } + return (ret); +} + +static __inline void +_tcb_set(struct kcb *kcb, struct tcb *tcb) +{ + if (tcb == NULL) + tcb = &kcb->kcb_faketcb; + __kcb_swp((uint32_t)&tcb->tcb_tp, &_tp); + kcb->kcb_curtcb = tcb; + tcb->tcb_curkcb = kcb; +} + +static __inline struct tcb * +_tcb_get(void) +{ + return (_tcb); +} + +static __inline struct pthread * +_get_curthread(void) +{ + return (_tcb->tcb_thread); +} + +/* + * Get the current kse. + * + * Like _kcb_get(), this can only be called while in a critical region. + */ +static __inline struct kse * +_get_curkse(void) +{ + return (_tcb->tcb_curkcb->kcb_kse); +} + +void _arm_enter_uts(struct kse_mailbox *km, kse_func_t uts, void *stack, + size_t stacksz); + +static __inline int +_thread_enter_uts(struct tcb *tcb, struct kcb *kcb) +{ + int ret; + + if ((ret = _thr_getcontext(&tcb->tcb_tmbx.tm_context.uc_mcontext)) + == 0) { + kcb->kcb_curtcb = &kcb->kcb_faketcb; + __kcb_swp((int)&kcb->kcb_faketcb.tcb_tp, &_tp); + _arm_enter_uts(&kcb->kcb_kmbx, kcb->kcb_kmbx.km_func, + kcb->kcb_kmbx.km_stack.ss_sp, + kcb->kcb_kmbx.km_stack.ss_size); + /* We should not reach here. */ + return (-1); + } else if (ret < 0) + return (-1); + return (0); +} + +static __inline int +_thread_switch(struct kcb *kcb, struct tcb *tcb, int setmbox) +{ + extern int _libkse_debug; + mcontext_t *mc; + + if (!tcb || !kcb) + return (-1); + _tcb_set(kcb, tcb); + mc = &tcb->tcb_tmbx.tm_context.uc_mcontext; + if (_libkse_debug == 0) { + tcb->tcb_tmbx.tm_lwp = kcb->kcb_kmbx.km_lwp; + if (setmbox) + _thr_setcontext(mc, (intptr_t)&tcb->tcb_tmbx, + (intptr_t *)&kcb->kcb_kmbx.km_curthread); + else + _thr_setcontext(mc, 0, NULL); + } else { + if (setmbox) + kse_switchin(&tcb->tcb_tmbx, KSE_SWITCHIN_SETTMBX); + else + kse_switchin(&tcb->tcb_tmbx, 0); + } + + /* We should not reach here. */ + return (-1); +} + +#endif /* _PTHREAD_MD_H_ */ diff --git a/lib/libpthread/arch/i386/Makefile.inc b/lib/libpthread/arch/i386/Makefile.inc new file mode 100644 index 0000000..73a9a8a --- /dev/null +++ b/lib/libpthread/arch/i386/Makefile.inc @@ -0,0 +1,5 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/arch/${MACHINE_ARCH}/${MACHINE_ARCH} + +SRCS+= thr_enter_uts.S thr_getcontext.S pthread_md.c diff --git a/lib/libpthread/arch/i386/i386/pthread_md.c b/lib/libpthread/arch/i386/i386/pthread_md.c new file mode 100644 index 0000000..cbea6d4 --- /dev/null +++ b/lib/libpthread/arch/i386/i386/pthread_md.c @@ -0,0 +1,100 @@ +/*- + * Copyright (C) 2003 David Xu <davidxu@freebsd.org> + * Copyright (c) 2001,2003 Daniel Eischen <deischen@freebsd.org> + * 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. Neither the name of the author 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 AUTHOR 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 AUTHOR 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> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <machine/cpufunc.h> +#include <machine/sysarch.h> + +#include <unistd.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <ucontext.h> + +#include "rtld_tls.h" +#include "pthread_md.h" + +struct tcb * +_tcb_ctor(struct pthread *thread, int initial) +{ + struct tcb *tcb; + void *oldtls; + + if (initial) { + __asm __volatile("movl %%gs:0, %0" : "=r" (oldtls)); + } else { + oldtls = NULL; + } + + tcb = _rtld_allocate_tls(oldtls, sizeof(struct tcb), 16); + if (tcb) { + tcb->tcb_thread = thread; + tcb->tcb_spare = 0; + bzero(&tcb->tcb_tmbx, sizeof(tcb->tcb_tmbx)); + } + + return (tcb); +} + +void +_tcb_dtor(struct tcb *tcb) +{ + _rtld_free_tls(tcb, sizeof(struct tcb), 16); +} + +/* + * Initialize KSD. This also includes setting up the LDT. + */ +struct kcb * +_kcb_ctor(struct kse *kse) +{ + struct kcb *kcb; + + kcb = malloc(sizeof(struct kcb)); + if (kcb != NULL) { + bzero(kcb, sizeof(struct kcb)); + kcb->kcb_self = kcb; + kcb->kcb_kse = kse; + } + return (kcb); +} + +void +_kcb_dtor(struct kcb *kcb) +{ + free(kcb); +} + +int +i386_set_gsbase(void *addr) +{ + + return (sysarch(I386_SET_GSBASE, &addr)); +} diff --git a/lib/libpthread/arch/i386/i386/thr_enter_uts.S b/lib/libpthread/arch/i386/i386/thr_enter_uts.S new file mode 100644 index 0000000..bf6df8f --- /dev/null +++ b/lib/libpthread/arch/i386/i386/thr_enter_uts.S @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2002 Jonathan Mini <mini@freebsd.org>. + * Copyright (c) 2001 Daniel Eischen <deischen@freebsd.org>. + * 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. Neither the name of the author 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 DANIEL EISCHEN 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 AUTHOR 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> +__FBSDID("$FreeBSD$"); + + +/* + * _i386_enter_uts(struct kse_mailbox *km, kse_func_t uts, void *stack, + * long stacksz); + * +4 = km, +8 = uts, +12 = stack, +16 = size + */ +ENTRY(_i386_enter_uts) + movl %esp, %edx /* save stack */ + movl 12(%edx), %eax /* get bottom of stack */ + addl 16(%edx), %eax /* add length */ + movl %eax, %esp /* switch to uts stack */ + pushl 4(%edx) /* push the address of the mailbox */ + call *8(%edx) + ret diff --git a/lib/libpthread/arch/i386/i386/thr_getcontext.S b/lib/libpthread/arch/i386/i386/thr_getcontext.S new file mode 100644 index 0000000..d9d300f --- /dev/null +++ b/lib/libpthread/arch/i386/i386/thr_getcontext.S @@ -0,0 +1,156 @@ +/*- + * Copyright (c) 2001 Daniel Eischen <deischen@freebsd.org>. + * 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. Neither the name of the author 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 DANIEL EISCHEN 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 AUTHOR 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> +__FBSDID("$FreeBSD$"); + +/* + * Where do we define these? + */ +#define MC_LEN_OFFSET 80 /* offset to mc_len from mcontext */ +#define MC_LEN 640 /* mc_len <machine/ucontext.h> */ +#define MC_FPFMT_OFFSET 84 +#define MC_FPFMT_NODEV 0x10000 +#define MC_FPFMT_387 0x10001 +#define MC_FPFMT_XMM 0x10002 +#define MC_OWNEDFP_OFFSET 88 +#define MC_OWNEDFP_NONE 0x20000 +#define MC_OWNEDFP_FPU 0x20001 +#define MC_OWNEDFP_PCB 0x20002 +#define MC_FPREGS_OFFSET 96 /* offset to FP regs from mcontext */ +#define MC_FP_CW_OFFSET 96 /* offset to FP control word */ + +/* + * int thr_setcontext(mcontext_t *mcp, intptr_t val, intptr_t *loc) + * + * Restores the context in mcp. + * + * Returns 0 if there are no errors; -1 otherwise + */ + .weak CNAME(_thr_setcontext) + .set CNAME(_thr_setcontext),CNAME(__thr_setcontext) +ENTRY(__thr_setcontext) + movl 4(%esp), %edx /* get address of mcontext */ + cmpl $0, %edx /* check for null pointer */ + jne 1f + movl $-1, %eax + jmp 8f +1: cmpl $MC_LEN, MC_LEN_OFFSET(%edx) /* is context valid? */ + je 2f + movl $-1, %eax /* bzzzt, invalid context */ + jmp 8f +2: /*movl 4(%edx), %gs*/ /* we don't touch %gs */ + movw 8(%edx), %fs + movw 12(%edx), %es + movw 16(%edx), %ds + movw 76(%edx), %ss + movl 20(%edx), %edi + movl 24(%edx), %esi + movl 28(%edx), %ebp + movl %esp, %ecx /* save current stack in ecx */ + movl 72(%edx), %esp /* switch to context defined stack */ + pushl 60(%edx) /* push return address on stack */ + pushl 44(%edx) /* push ecx on stack */ + pushl 48(%edx) /* push eax on stack */ + /* + * if (mc_fpowned == MC_OWNEDFP_FPU || mc_fpowned == MC_OWNEDFP_PCB) { + * if (mc_fpformat == MC_FPFMT_387) + * restore 387 FP register format + * else if (mc_fpformat == MC_FPFMT_XMM) + * restore XMM/SSE FP register format + * } + */ + cmpl $MC_OWNEDFP_FPU, MC_OWNEDFP_OFFSET(%edx) + je 3f + cmpl $MC_OWNEDFP_PCB, MC_OWNEDFP_OFFSET(%edx) + jne 5f +3: cmpl $MC_FPFMT_387, MC_FPFMT_OFFSET(%edx) + jne 4f + frstor MC_FPREGS_OFFSET(%edx) /* restore 387 FP regs */ + jmp 6f +4: cmpl $MC_FPFMT_XMM, MC_FPFMT_OFFSET(%edx) + jne 5f + fxrstor MC_FPREGS_OFFSET(%edx) /* restore XMM FP regs */ + jmp 6f +5: fninit + fldcw MC_FP_CW_OFFSET(%edx) +6: pushl 68(%edx) /* push flags register on stack*/ + movl 36(%edx), %ebx /* restore ebx and edx */ + movl 40(%edx), %edx + movl 12(%ecx), %eax /* get 3rd arg (loc) */ + cmpl $0, %eax /* do nothing if loc == null */ + je 7f + movl 8(%ecx), %ecx /* get 2nd arg (val) */ + movl %ecx, (%eax) /* set loc = val */ +7: popfl /* restore flags after test */ + popl %eax /* restore eax and ecx last */ + popl %ecx +8: ret + +/* + * int thr_getcontext(mcontext_t *mcp); + * + * Returns -1 if there is an error, 0 no errors; 1 upon return + * from a setcontext(). + */ + .weak CNAME(_thr_getcontext) + .set CNAME(_thr_getcontext),CNAME(__thr_getcontext) +ENTRY(__thr_getcontext) + pushl %edx /* save edx */ + movl 8(%esp), %edx /* get address of mcontext */ + cmpl $0, %edx /* check for null pointer */ + jne 1f + popl %edx /* restore edx and stack */ + movl $-1, %eax + jmp 2f +1: /*movw %gs, 4(%edx)*/ /* we don't touch %gs */ + movw %fs, 8(%edx) + movw %es, 12(%edx) + movw %ds, 16(%edx) + movw %ss, 76(%edx) + movl %edi, 20(%edx) + movl %esi, 24(%edx) + movl %ebp, 28(%edx) + movl %ebx, 36(%edx) + movl $1, 48(%edx) /* store successful return in eax */ + popl %eax /* get saved value of edx */ + movl %eax, 40(%edx) /* save edx */ + movl %ecx, 44(%edx) + movl (%esp), %eax /* get return address */ + movl %eax, 60(%edx) /* save return address */ + fnstcw MC_FP_CW_OFFSET(%edx) + movl $MC_LEN, MC_LEN_OFFSET(%edx) + movl $MC_FPFMT_NODEV, MC_FPFMT_OFFSET(%edx) /* no FP */ + movl $MC_OWNEDFP_NONE, MC_OWNEDFP_OFFSET(%edx) /* no FP */ + pushfl + popl %eax /* get eflags */ + movl %eax, 68(%edx) /* store eflags */ + movl %esp, %eax /* setcontext pushes the return */ + addl $4, %eax /* address onto the top of the */ + movl %eax, 72(%edx) /* stack; account for this */ + movl 40(%edx), %edx /* restore edx -- is this needed? */ + xorl %eax, %eax /* return 0 */ +2: ret diff --git a/lib/libpthread/arch/i386/include/atomic_ops.h b/lib/libpthread/arch/i386/include/atomic_ops.h new file mode 100644 index 0000000..7bc3d1b --- /dev/null +++ b/lib/libpthread/arch/i386/include/atomic_ops.h @@ -0,0 +1,51 @@ +/*- + * Copyright (c) 2001 Daniel Eischen <deischen@FreeBSD.org> + * 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. Neither the name of the author 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 AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef _ATOMIC_OPS_H_ +#define _ATOMIC_OPS_H_ + +/* + * Atomic swap: + * Atomic (tmp = *dst, *dst = val), then *res = tmp + * + * void atomic_swap32(intptr_t *dst, intptr_t val, intptr_t *res); + */ +static inline void +atomic_swap32(intptr_t *dst, intptr_t val, intptr_t *res) +{ + __asm __volatile( + "xchgl %2, %1; movl %2, %0" + : "=m" (*res) : "m" (*dst), "r" (val) : "memory"); +} + +#define atomic_swap_ptr(d, v, r) \ + atomic_swap32((intptr_t *)d, (intptr_t)v, (intptr_t *)r) + +#define atomic_swap_int(d, v, r) \ + atomic_swap32((intptr_t *)d, (intptr_t)v, (intptr_t *)r) +#endif diff --git a/lib/libpthread/arch/i386/include/pthread_md.h b/lib/libpthread/arch/i386/include/pthread_md.h new file mode 100644 index 0000000..52afd6a --- /dev/null +++ b/lib/libpthread/arch/i386/include/pthread_md.h @@ -0,0 +1,264 @@ +/*- + * Copyright (c) 2002 Daniel Eischen <deischen@freebsd.org>. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ +/* + * Machine-dependent thread prototypes/definitions for the thread kernel. + */ +#ifndef _PTHREAD_MD_H_ +#define _PTHREAD_MD_H_ + +#include <stddef.h> +#include <sys/types.h> +#include <sys/kse.h> +#include <machine/sysarch.h> +#include <ucontext.h> + +extern int _thr_setcontext(mcontext_t *, intptr_t, intptr_t *); +extern int _thr_getcontext(mcontext_t *); + +#define KSE_STACKSIZE 16384 +#define DTV_OFFSET offsetof(struct tcb, tcb_dtv) + +#define THR_GETCONTEXT(ucp) _thr_getcontext(&(ucp)->uc_mcontext) +#define THR_SETCONTEXT(ucp) _thr_setcontext(&(ucp)->uc_mcontext, 0, NULL) + +#define PER_KSE +#undef PER_THREAD + +struct kse; +struct pthread; + +/* + * %gs points to a struct kcb. + */ +struct kcb { + struct tcb *kcb_curtcb; + struct kcb *kcb_self; /* self reference */ + struct kse *kcb_kse; + struct kse_mailbox kcb_kmbx; +}; + +struct tcb { + struct tcb *tcb_self; /* required by rtld */ + void *tcb_dtv; /* required by rtld */ + struct pthread *tcb_thread; + void *tcb_spare; /* align tcb_tmbx to 16 bytes */ + struct kse_thr_mailbox tcb_tmbx; +}; + +/* + * Evaluates to the byte offset of the per-kse variable name. + */ +#define __kcb_offset(name) __offsetof(struct kcb, name) + +/* + * Evaluates to the type of the per-kse variable name. + */ +#define __kcb_type(name) __typeof(((struct kcb *)0)->name) + +/* + * Evaluates to the value of the per-kse variable name. + */ +#define KCB_GET32(name) ({ \ + __kcb_type(name) __result; \ + \ + u_int __i; \ + __asm __volatile("movl %%gs:%1, %0" \ + : "=r" (__i) \ + : "m" (*(u_int *)(__kcb_offset(name)))); \ + __result = (__kcb_type(name))__i; \ + \ + __result; \ +}) + +/* + * Sets the value of the per-kse variable name to value val. + */ +#define KCB_SET32(name, val) ({ \ + __kcb_type(name) __val = (val); \ + \ + u_int __i; \ + __i = (u_int)__val; \ + __asm __volatile("movl %1,%%gs:%0" \ + : "=m" (*(u_int *)(__kcb_offset(name))) \ + : "r" (__i)); \ +}) + +static __inline u_long +__kcb_readandclear32(volatile u_long *addr) +{ + u_long result; + + __asm __volatile ( + " xorl %0, %0;" + " xchgl %%gs:%1, %0;" + "# __kcb_readandclear32" + : "=&r" (result) + : "m" (*addr)); + return (result); +} + +#define KCB_READANDCLEAR32(name) ({ \ + __kcb_type(name) __result; \ + \ + __result = (__kcb_type(name)) \ + __kcb_readandclear32((u_long *)__kcb_offset(name)); \ + __result; \ +}) + + +#define _kcb_curkcb() KCB_GET32(kcb_self) +#define _kcb_curtcb() KCB_GET32(kcb_curtcb) +#define _kcb_curkse() ((struct kse *)KCB_GET32(kcb_kmbx.km_udata)) +#define _kcb_get_tmbx() KCB_GET32(kcb_kmbx.km_curthread) +#define _kcb_set_tmbx(value) KCB_SET32(kcb_kmbx.km_curthread, (void *)value) +#define _kcb_readandclear_tmbx() KCB_READANDCLEAR32(kcb_kmbx.km_curthread) + + +/* + * The constructors. + */ +struct tcb *_tcb_ctor(struct pthread *, int); +void _tcb_dtor(struct tcb *tcb); +struct kcb *_kcb_ctor(struct kse *); +void _kcb_dtor(struct kcb *); + +/* Called from the KSE to set its private data. */ +static __inline void +_kcb_set(struct kcb *kcb) +{ + i386_set_gsbase(kcb); +} + +/* Get the current kcb. */ +static __inline struct kcb * +_kcb_get(void) +{ + return (_kcb_curkcb()); +} + +static __inline struct kse_thr_mailbox * +_kcb_critical_enter(void) +{ + struct kse_thr_mailbox *crit; + + crit = _kcb_readandclear_tmbx(); + return (crit); +} + +static __inline void +_kcb_critical_leave(struct kse_thr_mailbox *crit) +{ + _kcb_set_tmbx(crit); +} + +static __inline int +_kcb_in_critical(void) +{ + return (_kcb_get_tmbx() == NULL); +} + +static __inline void +_tcb_set(struct kcb *kcb, struct tcb *tcb) +{ + kcb->kcb_curtcb = tcb; +} + +static __inline struct tcb * +_tcb_get(void) +{ + return (_kcb_curtcb()); +} + +static __inline struct pthread * +_get_curthread(void) +{ + struct tcb *tcb; + + tcb = _kcb_curtcb(); + if (tcb != NULL) + return (tcb->tcb_thread); + else + return (NULL); +} + +static __inline struct kse * +_get_curkse(void) +{ + return ((struct kse *)_kcb_curkse()); +} + +void _i386_enter_uts(struct kse_mailbox *km, kse_func_t uts, void *stack, + size_t stacksz); + +static __inline int +_thread_enter_uts(struct tcb *tcb, struct kcb *kcb) +{ + int ret; + + ret = _thr_getcontext(&tcb->tcb_tmbx.tm_context.uc_mcontext); + if (ret == 0) { + _i386_enter_uts(&kcb->kcb_kmbx, kcb->kcb_kmbx.km_func, + kcb->kcb_kmbx.km_stack.ss_sp, + kcb->kcb_kmbx.km_stack.ss_size); + /* We should not reach here. */ + return (-1); + } + else if (ret < 0) + return (-1); + return (0); +} + +static __inline int +_thread_switch(struct kcb *kcb, struct tcb *tcb, int setmbox) +{ + extern int _libkse_debug; + + if ((kcb == NULL) || (tcb == NULL)) + return (-1); + kcb->kcb_curtcb = tcb; + if (_libkse_debug == 0) { + tcb->tcb_tmbx.tm_lwp = kcb->kcb_kmbx.km_lwp; + if (setmbox != 0) + _thr_setcontext(&tcb->tcb_tmbx.tm_context.uc_mcontext, + (intptr_t)&tcb->tcb_tmbx, + (intptr_t *)&kcb->kcb_kmbx.km_curthread); + else + _thr_setcontext(&tcb->tcb_tmbx.tm_context.uc_mcontext, + 0, NULL); + } else { + if (setmbox) + kse_switchin(&tcb->tcb_tmbx, KSE_SWITCHIN_SETTMBX); + else + kse_switchin(&tcb->tcb_tmbx, 0); + } + + /* We should not reach here. */ + return (-1); +} + +#endif diff --git a/lib/libpthread/arch/ia64/Makefile.inc b/lib/libpthread/arch/ia64/Makefile.inc new file mode 100644 index 0000000..c8b0362 --- /dev/null +++ b/lib/libpthread/arch/ia64/Makefile.inc @@ -0,0 +1,5 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/arch/${MACHINE_ARCH}/${MACHINE_ARCH} + +SRCS+= context.S enter_uts.S pthread_md.c diff --git a/lib/libpthread/arch/ia64/ia64/context.S b/lib/libpthread/arch/ia64/ia64/context.S new file mode 100644 index 0000000..9411293 --- /dev/null +++ b/lib/libpthread/arch/ia64/ia64/context.S @@ -0,0 +1,351 @@ +/* + * Copyright (c) 2003 Marcel Moolenaar + * 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. + * + * 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. + */ + +#include <machine/asm.h> +__FBSDID("$FreeBSD$"); + +#include <sys/syscall.h> + +#define SIZEOF_SPECIAL (18*8) + +/* + * int _ia64_restore_context(mcontext_t *mc, intptr_t val, intptr_t *loc); + */ +ENTRY(_ia64_restore_context, 3) +{ .mmi + invala + mov ar.rsc=0xc + add r32=16,r32 + ;; +} +{ .mmi + loadrs + ld8 r12=[r32] // sp + add r31=8,r32 + ;; +} +{ .mii + ld8 r16=[r31],16 // unat (before) + add r30=16,r32 + add r14=SIZEOF_SPECIAL,r32 + ;; +} +{ .mmi + ld8 r17=[r30],16 // rp + ld8 r18=[r31],16 // pr + mov r2=r33 + ;; +} +{ .mmi + ld8 r19=[r30],16 // pfs + ld8 r20=[r31],32 // bspstore + mov rp=r17 + ;; +} +{ .mmi + ld8 r21=[r30],32 // rnat + ld8 r22=[r31],16 // rsc + mov pr=r18,0x1fffe + ;; +} +{ .mmi + ld8 r23=[r30] // fpsr + ld8 r24=[r31] // psr -- not used + mov r3=r34 + ;; +} +{ .mmi + ld8 r17=[r14],8 // unat (after) + mov ar.bspstore=r20 + cmp.ne p15,p0=r0,r3 + ;; +} +{ .mmi + mov ar.rnat=r21 + mov ar.unat=r17 + add r15=8,r14 + ;; +} +{ .mmi + ld8.fill r4=[r14],16 // r4 + ld8.fill r5=[r15],16 // r5 + mov ar.pfs=r19 + ;; +} +{ .mmi + ld8.fill r6=[r14],16 // r6 + ld8.fill r7=[r15],16 // r7 + nop 0 + ;; +} +{ .mmi + mov ar.unat=r16 + mov ar.rsc=r22 + nop 0 +} +{ .mmi + ld8 r17=[r14],16 // b1 + ld8 r18=[r15],16 // b2 + nop 0 + ;; +} +{ .mmi + ld8 r19=[r14],16 // b3 + ld8 r20=[r15],16 // b4 + mov b1=r17 + ;; +} +{ .mmi + ld8 r16=[r14],24 // b5 + ld8 r17=[r15],32 // lc + mov b2=r18 + ;; +} +{ .mmi + ldf.fill f2=[r14],32 + ldf.fill f3=[r15],32 + mov b3=r19 + ;; +} +{ .mmi + ldf.fill f4=[r14],32 + ldf.fill f5=[r15],32 + mov b4=r20 + ;; +} +{ .mmi + ldf.fill f16=[r14],32 + ldf.fill f17=[r15],32 + mov b5=r16 + ;; +} +{ .mmi + ldf.fill f18=[r14],32 + ldf.fill f19=[r15],32 + mov ar.lc=r17 + ;; +} + ldf.fill f20=[r14],32 + ldf.fill f21=[r15],32 + ;; + ldf.fill f22=[r14],32 + ldf.fill f23=[r15],32 + ;; + ldf.fill f24=[r14],32 + ldf.fill f25=[r15],32 + ;; + ldf.fill f26=[r14],32 + ldf.fill f27=[r15],32 + ;; + ldf.fill f28=[r14],32 + ldf.fill f29=[r15],32 + ;; + ldf.fill f30=[r14],32+24 + ldf.fill f31=[r15],24+24 + ;; + ld8 r8=[r14],16 + ld8 r9=[r15],16 + ;; + ld8 r10=[r14] + ld8 r11=[r15] + ;; +{ .mmb +(p15) st8 [r3]=r2 + mov ar.fpsr=r23 + br.ret.sptk rp + ;; +} +END(_ia64_restore_context) + +/* + * int _ia64_save_context(mcontext_t *mc); + */ +ENTRY(_ia64_save_context, 1) +{ .mmi + mov r14=ar.rsc + mov r15=ar.fpsr + add r31=8,r32 + ;; +} +{ .mmi + st8 [r32]=r0,16 + st8 [r31]=r0,16 + nop 0 + ;; +} +{ .mmi + mov ar.rsc=0xc + mov r16=ar.unat + nop 0 + ;; +} +{ .mmi + flushrs + st8 [r32]=sp,16 // sp + mov r17=rp + ;; +} +{ .mmi + st8 [r31]=r16,16 // unat (before) + st8 [r32]=r17,16 // rp + mov r16=pr + ;; +} +{ .mmi + st8 [r31]=r16,16 // pr + mov r17=ar.bsp + mov r16=ar.pfs + ;; +} +{ .mmi + st8 [r32]=r16,16 // pfs + st8 [r31]=r17,16 // bspstore + nop 0 + ;; +} +{ .mmi + mov r16=ar.rnat + mov ar.rsc=r14 + add r30=SIZEOF_SPECIAL-(6*8),r32 + ;; +} +{ .mmi + st8 [r32]=r16,16 // rnat + st8 [r31]=r0,16 // __spare + nop 0 + ;; +} +{ .mmi + st8 [r32]=r13,16 // tp -- not used + st8 [r31]=r14,16 // rsc + mov r16=b1 + ;; +} +{ .mmi + st8 [r32]=r15,10*8 // fpr + st8 [r31]=r0,8*8 // psr + nop 0 + ;; +} + /* callee_saved */ +{ .mmi + .mem.offset 8,0 + st8.spill [r31]=r4,16 // r4 + .mem.offset 16,0 + st8.spill [r32]=r5,16 // r5 + mov r17=b2 + ;; +} +{ .mmi + .mem.offset 24,0 + st8.spill [r31]=r6,16 // r6 + .mem.offset 32,0 + st8.spill [r32]=r7,16 // r7 + mov r18=b3 + ;; +} +{ .mmi + st8 [r31]=r16,16 // b1 + mov r16=ar.unat + mov r19=b4 + ;; +} +{ .mmi + st8 [r30]=r16 // unat (after) + st8 [r32]=r17,16 // b2 + mov r16=b5 + ;; +} +{ .mmi + st8 [r31]=r18,16 // b3 + st8 [r32]=r19,16 // b4 + mov r17=ar.lc + ;; +} + st8 [r31]=r16,16 // b5 + st8 [r32]=r17,16 // lc + ;; + st8 [r31]=r0,24 // __spare + stf.spill [r32]=f2,32 + ;; + stf.spill [r31]=f3,32 + stf.spill [r32]=f4,32 + ;; + stf.spill [r31]=f5,32 + stf.spill [r32]=f16,32 + ;; + stf.spill [r31]=f17,32 + stf.spill [r32]=f18,32 + ;; + stf.spill [r31]=f19,32 + stf.spill [r32]=f20,32 + ;; + stf.spill [r31]=f21,32 + stf.spill [r32]=f22,32 + ;; + stf.spill [r31]=f23,32 + stf.spill [r32]=f24,32 + ;; + stf.spill [r31]=f25,32 + stf.spill [r32]=f26,32 + ;; + stf.spill [r31]=f27,32 + stf.spill [r32]=f28,32 + ;; +{ .mmi + stf.spill [r31]=f29,32 + stf.spill [r32]=f30,32+24 + add r14=1,r0 + ;; +} +{ .mmi + stf.spill [r31]=f31,24+24 + st8 [r32]=r14,16 // r8 + add r8=0,r0 + ;; +} + st8 [r31]=r0,16 // r9 + st8 [r32]=r0 // r10 + ;; +{ .mmb + st8 [r31]=r0 // r11 + mf + br.ret.sptk rp + ;; +} +END(_ia64_save_context) + +/* + * void _ia64_break_setcontext(mcontext_t *mc); + */ +ENTRY(_ia64_break_setcontext, 1) +{ .mmi + mov r8=r32 + break 0x180000 + nop 0 + ;; +} +END(_ia64_break_setcontext) diff --git a/lib/libpthread/arch/ia64/ia64/enter_uts.S b/lib/libpthread/arch/ia64/ia64/enter_uts.S new file mode 100644 index 0000000..5df4d93 --- /dev/null +++ b/lib/libpthread/arch/ia64/ia64/enter_uts.S @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2003 Marcel Moolenaar + * 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. + * + * 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. + */ + +#include <machine/asm.h> +__FBSDID("$FreeBSD$"); + +/* + * void _ia64_enter_uts(kse_func_t uts, struct kse_mailbox *km, void *stack, + * size_t stacksz); + */ +ENTRY(_ia64_enter_uts, 4) +{ .mmi + ld8 r14=[in0],8 + mov ar.rsc=0xc + add r15=in2,in3 + ;; +} +{ .mmi + flushrs + ld8 r1=[in0] + mov b7=r14 + ;; +} +{ .mii + mov ar.bspstore=in2 + add sp=-16,r15 + mov rp=r14 + ;; +} +{ .mib + mov ar.rsc=0xf + mov in0=in1 + br.cond.sptk b7 + ;; +} +1: br.cond.sptk 1b +END(_ia64_enter_uts) diff --git a/lib/libpthread/arch/ia64/ia64/pthread_md.c b/lib/libpthread/arch/ia64/ia64/pthread_md.c new file mode 100644 index 0000000..00e9a40 --- /dev/null +++ b/lib/libpthread/arch/ia64/ia64/pthread_md.c @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2003 Daniel Eischen <deischen@freebsd.org> + * 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. Neither the name of the author 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 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. + * + * $FreeBSD$ + */ + +#include <stdlib.h> +#include <strings.h> +#include "pthread_md.h" + +/* + * The constructors. + */ +struct tcb * +_tcb_ctor(struct pthread *thread, int initial) +{ + struct tcb *tcb; + + if ((tcb = malloc(sizeof(struct tcb))) != NULL) { + bzero(tcb, sizeof(struct tcb)); + tcb->tcb_thread = thread; + /* Allocate TDV */ + } + return (tcb); +} + +void +_tcb_dtor(struct tcb *tcb) +{ + /* Free TDV */ + free(tcb); +} + +struct kcb * +_kcb_ctor(struct kse *kse) +{ + struct kcb *kcb; + + if ((kcb = malloc(sizeof(struct kcb))) != NULL) { + bzero(kcb, sizeof(struct kcb)); + kcb->kcb_faketcb.tcb_isfake = 1; + kcb->kcb_faketcb.tcb_tmbx.tm_flags = TMF_NOUPCALL; + kcb->kcb_curtcb = &kcb->kcb_faketcb; + kcb->kcb_kse = kse; + } + return (kcb); +} + +void +_kcb_dtor(struct kcb *kcb) +{ + free(kcb); +} diff --git a/lib/libpthread/arch/ia64/include/atomic_ops.h b/lib/libpthread/arch/ia64/include/atomic_ops.h new file mode 100644 index 0000000..483c905 --- /dev/null +++ b/lib/libpthread/arch/ia64/include/atomic_ops.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2003 Marcel Moolenaar + * 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. + * + * 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. + * + * $FreeBSD$ + */ + +#ifndef _ATOMIC_OPS_H_ +#define _ATOMIC_OPS_H_ + +static inline void +atomic_swap_int(int *dst, int val, int *res) +{ + __asm("xchg4 %0=[%2],%1" : "=r"(*res) : "r"(val), "r"(dst)); +} + +static inline void +atomic_swap_long(long *dst, long val, long *res) +{ + __asm("xchg8 %0=[%2],%1" : "=r"(*res) : "r"(val), "r"(dst)); +} + +#define atomic_swap_ptr(d,v,r) \ + atomic_swap_long((long*)d, (long)v, (long*)r) + +#endif /* _ATOMIC_OPS_H_ */ diff --git a/lib/libpthread/arch/ia64/include/pthread_md.h b/lib/libpthread/arch/ia64/include/pthread_md.h new file mode 100644 index 0000000..1df5046 --- /dev/null +++ b/lib/libpthread/arch/ia64/include/pthread_md.h @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2003 Marcel Moolenaar + * 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. + * + * 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. + * + * $FreeBSD$ + */ + +#ifndef _PTHREAD_MD_H_ +#define _PTHREAD_MD_H_ + +#include <sys/kse.h> +#include <stddef.h> +#include <ucontext.h> + +#define KSE_STACKSIZE 16384 +#define DTV_OFFSET offsetof(struct tcb, tcb_tp.tp_tdv) + +#define THR_GETCONTEXT(ucp) _ia64_save_context(&(ucp)->uc_mcontext) +#define THR_SETCONTEXT(ucp) PANIC("THR_SETCONTEXT() now in use!\n") + +#define PER_THREAD + +struct kcb; +struct kse; +struct pthread; +struct tcb; +struct tdv; /* We don't know what this is yet? */ + +/* + * tp points to one of these. We define the static TLS as an array + * of long double to enforce 16-byte alignment of the TLS memory, + * struct ia64_tp, struct tcb and also struct kcb. Both static and + * dynamic allocation of any of these structures will result in a + * valid, well-aligned thread pointer. + */ +struct ia64_tp { + struct tdv *tp_tdv; /* dynamic TLS */ + uint64_t _reserved_; + long double tp_tls[0]; /* static TLS */ +}; + +struct tcb { + struct kse_thr_mailbox tcb_tmbx; + struct pthread *tcb_thread; + struct kcb *tcb_curkcb; + long tcb_isfake; + struct ia64_tp tcb_tp; +}; + +struct kcb { + struct kse_mailbox kcb_kmbx; + struct tcb kcb_faketcb; + struct tcb *kcb_curtcb; + struct kse *kcb_kse; +}; + +register struct ia64_tp *_tp __asm("%r13"); + +#define _tcb ((struct tcb*)((char*)(_tp) - offsetof(struct tcb, tcb_tp))) + +/* + * The kcb and tcb constructors. + */ +struct tcb *_tcb_ctor(struct pthread *, int); +void _tcb_dtor(struct tcb *); +struct kcb *_kcb_ctor(struct kse *kse); +void _kcb_dtor(struct kcb *); + +/* Called from the KSE to set its private data. */ +static __inline void +_kcb_set(struct kcb *kcb) +{ + /* There is no thread yet; use the fake tcb. */ + _tp = &kcb->kcb_faketcb.tcb_tp; +} + +/* + * Get the current kcb. + * + * This can only be called while in a critical region; don't + * worry about having the kcb changed out from under us. + */ +static __inline struct kcb * +_kcb_get(void) +{ + return (_tcb->tcb_curkcb); +} + +/* + * Enter a critical region. + * + * Read and clear km_curthread in the kse mailbox. + */ +static __inline struct kse_thr_mailbox * +_kcb_critical_enter(void) +{ + struct kse_thr_mailbox *crit; + uint32_t flags; + + if (_tcb->tcb_isfake != 0) { + /* + * We already are in a critical region since + * there is no current thread. + */ + crit = NULL; + } else { + flags = _tcb->tcb_tmbx.tm_flags; + _tcb->tcb_tmbx.tm_flags |= TMF_NOUPCALL; + crit = _tcb->tcb_curkcb->kcb_kmbx.km_curthread; + _tcb->tcb_curkcb->kcb_kmbx.km_curthread = NULL; + _tcb->tcb_tmbx.tm_flags = flags; + } + return (crit); +} + +static __inline void +_kcb_critical_leave(struct kse_thr_mailbox *crit) +{ + /* No need to do anything if this is a fake tcb. */ + if (_tcb->tcb_isfake == 0) + _tcb->tcb_curkcb->kcb_kmbx.km_curthread = crit; +} + +static __inline int +_kcb_in_critical(void) +{ + uint32_t flags; + int ret; + + if (_tcb->tcb_isfake != 0) { + /* + * We are in a critical region since there is no + * current thread. + */ + ret = 1; + } else { + flags = _tcb->tcb_tmbx.tm_flags; + _tcb->tcb_tmbx.tm_flags |= TMF_NOUPCALL; + ret = (_tcb->tcb_curkcb->kcb_kmbx.km_curthread == NULL); + _tcb->tcb_tmbx.tm_flags = flags; + } + return (ret); +} + +static __inline void +_tcb_set(struct kcb *kcb, struct tcb *tcb) +{ + if (tcb == NULL) + tcb = &kcb->kcb_faketcb; + kcb->kcb_curtcb = tcb; + tcb->tcb_curkcb = kcb; + _tp = &tcb->tcb_tp; +} + +static __inline struct tcb * +_tcb_get(void) +{ + return (_tcb); +} + +static __inline struct pthread * +_get_curthread(void) +{ + return (_tcb->tcb_thread); +} + +/* + * Get the current kse. + * + * Like _kcb_get(), this can only be called while in a critical region. + */ +static __inline struct kse * +_get_curkse(void) +{ + return (_tcb->tcb_curkcb->kcb_kse); +} + +void _ia64_break_setcontext(mcontext_t *mc); +void _ia64_enter_uts(kse_func_t uts, struct kse_mailbox *km, void *stack, + size_t stacksz); +int _ia64_restore_context(mcontext_t *mc, intptr_t val, intptr_t *loc); +int _ia64_save_context(mcontext_t *mc); + +static __inline int +_thread_enter_uts(struct tcb *tcb, struct kcb *kcb) +{ + if (_ia64_save_context(&tcb->tcb_tmbx.tm_context.uc_mcontext) == 0) { + /* Make the fake tcb the current thread. */ + kcb->kcb_curtcb = &kcb->kcb_faketcb; + _tp = &kcb->kcb_faketcb.tcb_tp; + _ia64_enter_uts(kcb->kcb_kmbx.km_func, &kcb->kcb_kmbx, + kcb->kcb_kmbx.km_stack.ss_sp, + kcb->kcb_kmbx.km_stack.ss_size); + /* We should not reach here. */ + return (-1); + } + return (0); +} + +static __inline int +_thread_switch(struct kcb *kcb, struct tcb *tcb, int setmbox) +{ + mcontext_t *mc; + + _tcb_set(kcb, tcb); + mc = &tcb->tcb_tmbx.tm_context.uc_mcontext; + if (mc->mc_flags & _MC_FLAGS_ASYNC_CONTEXT) { + if (setmbox) { + mc->mc_flags |= _MC_FLAGS_KSE_SET_MBOX; + mc->mc_special.ifa = + (intptr_t)&kcb->kcb_kmbx.km_curthread; + mc->mc_special.isr = (intptr_t)&tcb->tcb_tmbx; + } + _ia64_break_setcontext(mc); + } else if (mc->mc_flags & _MC_FLAGS_SYSCALL_CONTEXT) { + if (setmbox) + kse_switchin(&tcb->tcb_tmbx, KSE_SWITCHIN_SETTMBX); + else + kse_switchin(&tcb->tcb_tmbx, 0); + } else { + if (setmbox) + _ia64_restore_context(mc, (intptr_t)&tcb->tcb_tmbx, + (intptr_t *)&kcb->kcb_kmbx.km_curthread); + else + _ia64_restore_context(mc, 0, NULL); + } + /* We should not reach here. */ + return (-1); +} + +#endif /* _PTHREAD_MD_H_ */ diff --git a/lib/libpthread/arch/powerpc/Makefile.inc b/lib/libpthread/arch/powerpc/Makefile.inc new file mode 100644 index 0000000..f4417a6 --- /dev/null +++ b/lib/libpthread/arch/powerpc/Makefile.inc @@ -0,0 +1,8 @@ +# $FreeBSD$ + +# XXX temporary +CFLAGS+=-DSYSTEM_SCOPE_ONLY + +.PATH: ${.CURDIR}/arch/${MACHINE_ARCH}/${MACHINE_ARCH} + +SRCS+= enter_uts.S context.S pthread_md.c diff --git a/lib/libpthread/arch/powerpc/include/atomic_ops.h b/lib/libpthread/arch/powerpc/include/atomic_ops.h new file mode 100644 index 0000000..8068e6f --- /dev/null +++ b/lib/libpthread/arch/powerpc/include/atomic_ops.h @@ -0,0 +1,62 @@ +/* + * Copyright 2004 by Peter Grehan. 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. 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. + * + * $FreeBSD$ + */ + +#ifndef _ATOMIC_OPS_H_ +#define _ATOMIC_OPS_H_ + +/* + * Atomic swap: + * Atomic (tmp = *dst, *dst = val), then *res = tmp + * + * void atomic_swap32(intptr_t *dst, intptr_t val, intptr_t *res); + */ +static inline void +atomic_swap32(intptr_t *dst, intptr_t val, intptr_t *res) +{ + int tmp; + + tmp = 0; /* should be a better way to quieten cc1... */ +#ifdef __GNUC__ + __asm __volatile( + "1: lwarx %0, 0, %4\n" /* load with reservation */ + " stwcx. %3, 0, %4\n" /* attempt to store val */ + " bne- 1b\n" /* interrupted? retry */ + " stw %0, %1\n" /* else, *dst -> *res */ + : "=&r" (tmp), "=m" (*res), "+m" (*dst) + : "r" (val), "r" (dst) + : "cc", "memory"); +#endif +} + +#define atomic_swap_ptr(d, v, r) \ + atomic_swap32((intptr_t *)d, (intptr_t)v, (intptr_t *)r) + +#define atomic_swap_int(d, v, r) \ + atomic_swap32((intptr_t *)d, (intptr_t)v, (intptr_t *)r) +#endif diff --git a/lib/libpthread/arch/powerpc/include/pthread_md.h b/lib/libpthread/arch/powerpc/include/pthread_md.h new file mode 100644 index 0000000..33a58b5 --- /dev/null +++ b/lib/libpthread/arch/powerpc/include/pthread_md.h @@ -0,0 +1,258 @@ +/* + * Copyright 2004 by Peter Grehan. 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. 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. + * + * $FreeBSD$ + */ + +/* + * Machine-dependent thread prototypes/definitions for the thread kernel. + */ +#ifndef _PTHREAD_MD_H_ +#define _PTHREAD_MD_H_ + +#include <sys/kse.h> +#include <stddef.h> +#include <ucontext.h> + +extern void _ppc32_enter_uts(struct kse_mailbox *, kse_func_t, void *, size_t); +extern int _ppc32_setcontext(mcontext_t *, intptr_t, intptr_t *); +extern int _ppc32_getcontext(mcontext_t *); + +#define KSE_STACKSIZE 16384 +#define DTV_OFFSET offsetof(struct tcb, tcb_tp.tp_tdv) + +#define THR_GETCONTEXT(ucp) _ppc32_getcontext(&(ucp)->uc_mcontext) +#define THR_SETCONTEXT(ucp) _ppc32_setcontext(&(ucp)->uc_mcontext, 0, NULL) + +#define PER_THREAD + +struct kcb; +struct kse; +struct pthread; +struct tcb; +struct tdv; + +/* + * %r2 points to a struct kcb. + */ +struct ppc32_tp { + struct tdv *tp_tdv; /* dynamic TLS */ + uint32_t _reserved_; + long double tp_tls[0]; /* static TLS */ +}; + +struct tcb { + struct kse_thr_mailbox tcb_tmbx; + struct pthread *tcb_thread; + struct kcb *tcb_curkcb; + long tcb_isfake; + struct ppc32_tp tcb_tp; +}; + +struct kcb { + struct kse_mailbox kcb_kmbx; + struct tcb kcb_faketcb; + struct tcb *kcb_curtcb; + struct kse *kcb_kse; +}; + +/* + * From the PowerPC32 TLS spec: + * + * "r2 is the thread pointer, and points 0x7000 past the end of the + * thread control block." Or, 0x7008 past the start of the 8-byte tcb + */ +#define TP_OFFSET 0x7008 +register uint8_t *_tpr __asm("%r2"); + +#define _tcb ((struct tcb *)(_tpr - TP_OFFSET - offsetof(struct tcb, tcb_tp))) + +/* + * The kcb and tcb constructors. + */ +struct tcb *_tcb_ctor(struct pthread *, int); +void _tcb_dtor(struct tcb *); +struct kcb *_kcb_ctor(struct kse *kse); +void _kcb_dtor(struct kcb *); + +/* Called from the KSE to set its private data. */ +static __inline void +_kcb_set(struct kcb *kcb) +{ + /* There is no thread yet; use the fake tcb. */ + _tpr = (uint8_t *)&kcb->kcb_faketcb.tcb_tp + TP_OFFSET; +} + +/* + * Get the current kcb. + * + * This can only be called while in a critical region; don't + * worry about having the kcb changed out from under us. + */ +static __inline struct kcb * +_kcb_get(void) +{ + return (_tcb->tcb_curkcb); +} + +/* + * Enter a critical region. + * + * Read and clear km_curthread in the kse mailbox. + */ +static __inline struct kse_thr_mailbox * +_kcb_critical_enter(void) +{ + struct kse_thr_mailbox *crit; + uint32_t flags; + + if (_tcb->tcb_isfake != 0) { + /* + * We already are in a critical region since + * there is no current thread. + */ + crit = NULL; + } else { + flags = _tcb->tcb_tmbx.tm_flags; + _tcb->tcb_tmbx.tm_flags |= TMF_NOUPCALL; + crit = _tcb->tcb_curkcb->kcb_kmbx.km_curthread; + _tcb->tcb_curkcb->kcb_kmbx.km_curthread = NULL; + _tcb->tcb_tmbx.tm_flags = flags; + } + return (crit); +} + +static __inline void +_kcb_critical_leave(struct kse_thr_mailbox *crit) +{ + /* No need to do anything if this is a fake tcb. */ + if (_tcb->tcb_isfake == 0) + _tcb->tcb_curkcb->kcb_kmbx.km_curthread = crit; +} + +static __inline int +_kcb_in_critical(void) +{ + uint32_t flags; + int ret; + + if (_tcb->tcb_isfake != 0) { + /* + * We are in a critical region since there is no + * current thread. + */ + ret = 1; + } else { + flags = _tcb->tcb_tmbx.tm_flags; + _tcb->tcb_tmbx.tm_flags |= TMF_NOUPCALL; + ret = (_tcb->tcb_curkcb->kcb_kmbx.km_curthread == NULL); + _tcb->tcb_tmbx.tm_flags = flags; + } + return (ret); +} + +static __inline void +_tcb_set(struct kcb *kcb, struct tcb *tcb) +{ + if (tcb == NULL) + tcb = &kcb->kcb_faketcb; + kcb->kcb_curtcb = tcb; + tcb->tcb_curkcb = kcb; + _tpr = (uint8_t *)&tcb->tcb_tp + TP_OFFSET; +} + +static __inline struct tcb * +_tcb_get(void) +{ + return (_tcb); +} + +static __inline struct pthread * +_get_curthread(void) +{ + return (_tcb->tcb_thread); +} + +/* + * Get the current kse. + * + * Like _kcb_get(), this can only be called while in a critical region. + */ +static __inline struct kse * +_get_curkse(void) +{ + return (_tcb->tcb_curkcb->kcb_kse); +} + +static __inline int +_thread_enter_uts(struct tcb *tcb, struct kcb *kcb) +{ + if (_ppc32_getcontext(&tcb->tcb_tmbx.tm_context.uc_mcontext) == 0) { + /* Make the fake tcb the current thread. */ + kcb->kcb_curtcb = &kcb->kcb_faketcb; + _tpr = (uint8_t *)&kcb->kcb_faketcb.tcb_tp + TP_OFFSET; + _ppc32_enter_uts(&kcb->kcb_kmbx, kcb->kcb_kmbx.km_func, + kcb->kcb_kmbx.km_stack.ss_sp, + kcb->kcb_kmbx.km_stack.ss_size - 32); + /* We should not reach here. */ + return (-1); + } + return (0); +} + +static __inline int +_thread_switch(struct kcb *kcb, struct tcb *tcb, int setmbox) +{ + mcontext_t *mc; + extern int _libkse_debug; + + _tcb_set(kcb, tcb); + mc = &tcb->tcb_tmbx.tm_context.uc_mcontext; + + /* + * A full context needs a system call to restore, so use + * kse_switchin. Otherwise, the partial context can be + * restored with _ppc32_setcontext + */ + if (mc->mc_vers != _MC_VERSION_KSE && _libkse_debug != 0) { + if (setmbox) + kse_switchin(&tcb->tcb_tmbx, KSE_SWITCHIN_SETTMBX); + else + kse_switchin(&tcb->tcb_tmbx, 0); + } else { + tcb->tcb_tmbx.tm_lwp = kcb->kcb_kmbx.km_lwp; + if (setmbox) + _ppc32_setcontext(mc, (intptr_t)&tcb->tcb_tmbx, + (intptr_t *)&kcb->kcb_kmbx.km_curthread); + else + _ppc32_setcontext(mc, 0, NULL); + } + + /* We should not reach here. */ + return (-1); +} + +#endif /* _PTHREAD_MD_H_ */ diff --git a/lib/libpthread/arch/powerpc/powerpc/assym.c b/lib/libpthread/arch/powerpc/powerpc/assym.c new file mode 100644 index 0000000..a8479e7 --- /dev/null +++ b/lib/libpthread/arch/powerpc/powerpc/assym.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2004 Peter Grehan. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +/* Used to generate mcontext_t offsets */ + +#include <sys/types.h> +#include <sys/assym.h> +#include <sys/ucontext.h> + +#include <stddef.h> + +ASSYM(_MC_VERSION, _MC_VERSION); +ASSYM(_MC_VERSION_KSE, _MC_VERSION_KSE); +ASSYM(_MC_FP_VALID, _MC_FP_VALID); + +ASSYM(_MC_VERS, offsetof(mcontext_t, mc_vers)); +ASSYM(_MC_FLAGS, offsetof(mcontext_t, mc_flags)); + +ASSYM(_MC_R0, offsetof(mcontext_t, mc_frame[0])); +ASSYM(_MC_R1, offsetof(mcontext_t, mc_frame[1])); +ASSYM(_MC_R2, offsetof(mcontext_t, mc_frame[2])); +ASSYM(_MC_R3, offsetof(mcontext_t, mc_frame[3])); +ASSYM(_MC_R4, offsetof(mcontext_t, mc_frame[4])); +ASSYM(_MC_R5, offsetof(mcontext_t, mc_frame[5])); +ASSYM(_MC_R6, offsetof(mcontext_t, mc_frame[6])); +ASSYM(_MC_R7, offsetof(mcontext_t, mc_frame[7])); +ASSYM(_MC_R8, offsetof(mcontext_t, mc_frame[8])); +ASSYM(_MC_R9, offsetof(mcontext_t, mc_frame[9])); +ASSYM(_MC_R10, offsetof(mcontext_t, mc_frame[10])); +ASSYM(_MC_R11, offsetof(mcontext_t, mc_frame[11])); +ASSYM(_MC_R12, offsetof(mcontext_t, mc_frame[12])); +ASSYM(_MC_R13, offsetof(mcontext_t, mc_frame[13])); +ASSYM(_MC_R14, offsetof(mcontext_t, mc_frame[14])); +ASSYM(_MC_R15, offsetof(mcontext_t, mc_frame[15])); +ASSYM(_MC_R16, offsetof(mcontext_t, mc_frame[16])); +ASSYM(_MC_R17, offsetof(mcontext_t, mc_frame[17])); +ASSYM(_MC_R18, offsetof(mcontext_t, mc_frame[18])); +ASSYM(_MC_R19, offsetof(mcontext_t, mc_frame[19])); +ASSYM(_MC_R20, offsetof(mcontext_t, mc_frame[20])); +ASSYM(_MC_R21, offsetof(mcontext_t, mc_frame[21])); +ASSYM(_MC_R22, offsetof(mcontext_t, mc_frame[22])); +ASSYM(_MC_R23, offsetof(mcontext_t, mc_frame[23])); +ASSYM(_MC_R24, offsetof(mcontext_t, mc_frame[24])); +ASSYM(_MC_R25, offsetof(mcontext_t, mc_frame[25])); +ASSYM(_MC_R26, offsetof(mcontext_t, mc_frame[26])); +ASSYM(_MC_R27, offsetof(mcontext_t, mc_frame[27])); +ASSYM(_MC_R28, offsetof(mcontext_t, mc_frame[28])); +ASSYM(_MC_R29, offsetof(mcontext_t, mc_frame[29])); +ASSYM(_MC_R30, offsetof(mcontext_t, mc_frame[30])); +ASSYM(_MC_R31, offsetof(mcontext_t, mc_frame[31])); +ASSYM(_MC_LR, offsetof(mcontext_t, mc_frame[32])); +ASSYM(_MC_CR, offsetof(mcontext_t, mc_frame[33])); +ASSYM(_MC_XER, offsetof(mcontext_t, mc_frame[34])); +ASSYM(_MC_CTR, offsetof(mcontext_t, mc_frame[35])); + +ASSYM(_MC_FPSCR, offsetof(mcontext_t, mc_fpreg[32])); +ASSYM(_MC_F0, offsetof(mcontext_t, mc_fpreg[0])); +ASSYM(_MC_F1, offsetof(mcontext_t, mc_fpreg[1])); +ASSYM(_MC_F2, offsetof(mcontext_t, mc_fpreg[2])); +ASSYM(_MC_F3, offsetof(mcontext_t, mc_fpreg[3])); +ASSYM(_MC_F4, offsetof(mcontext_t, mc_fpreg[4])); +ASSYM(_MC_F5, offsetof(mcontext_t, mc_fpreg[5])); +ASSYM(_MC_F6, offsetof(mcontext_t, mc_fpreg[6])); +ASSYM(_MC_F7, offsetof(mcontext_t, mc_fpreg[7])); +ASSYM(_MC_F8, offsetof(mcontext_t, mc_fpreg[8])); +ASSYM(_MC_F9, offsetof(mcontext_t, mc_fpreg[9])); +ASSYM(_MC_F10, offsetof(mcontext_t, mc_fpreg[10])); +ASSYM(_MC_F11, offsetof(mcontext_t, mc_fpreg[11])); +ASSYM(_MC_F12, offsetof(mcontext_t, mc_fpreg[12])); +ASSYM(_MC_F13, offsetof(mcontext_t, mc_fpreg[13])); +ASSYM(_MC_F14, offsetof(mcontext_t, mc_fpreg[14])); +ASSYM(_MC_F15, offsetof(mcontext_t, mc_fpreg[15])); +ASSYM(_MC_F16, offsetof(mcontext_t, mc_fpreg[16])); +ASSYM(_MC_F17, offsetof(mcontext_t, mc_fpreg[17])); +ASSYM(_MC_F18, offsetof(mcontext_t, mc_fpreg[18])); +ASSYM(_MC_F19, offsetof(mcontext_t, mc_fpreg[19])); +ASSYM(_MC_F20, offsetof(mcontext_t, mc_fpreg[20])); +ASSYM(_MC_F21, offsetof(mcontext_t, mc_fpreg[21])); +ASSYM(_MC_F22, offsetof(mcontext_t, mc_fpreg[22])); +ASSYM(_MC_F23, offsetof(mcontext_t, mc_fpreg[23])); +ASSYM(_MC_F24, offsetof(mcontext_t, mc_fpreg[24])); +ASSYM(_MC_F25, offsetof(mcontext_t, mc_fpreg[25])); +ASSYM(_MC_F26, offsetof(mcontext_t, mc_fpreg[26])); +ASSYM(_MC_F27, offsetof(mcontext_t, mc_fpreg[27])); +ASSYM(_MC_F28, offsetof(mcontext_t, mc_fpreg[28])); +ASSYM(_MC_F29, offsetof(mcontext_t, mc_fpreg[29])); +ASSYM(_MC_F30, offsetof(mcontext_t, mc_fpreg[30])); +ASSYM(_MC_F31, offsetof(mcontext_t, mc_fpreg[31])); diff --git a/lib/libpthread/arch/powerpc/powerpc/assym.s b/lib/libpthread/arch/powerpc/powerpc/assym.s new file mode 100644 index 0000000..7017c15 --- /dev/null +++ b/lib/libpthread/arch/powerpc/powerpc/assym.s @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2004 Peter Grehan. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +/* + * Struct offsets for version 0x1 of the mcontext struct. + * Generated with + * cc -c assym.c + * ${SYSSRC}/kern/genassym.sh assym.o > assym_syms.s + * hand-edit output + */ +#define _MC_VERSION 0x1 +#define _MC_VERSION_KSE 0xee +#define _MC_FP_VALID 0x1 + +#define _MC_VERS 0x0 +#define _MC_FLAGS 0x4 + +#define _MC_R0 0x298 +#define _MC_R1 0x21c +#define _MC_R2 0x220 +#define _MC_R3 0x224 +#define _MC_R4 0x228 +#define _MC_R5 0x22c +#define _MC_R6 0x230 +#define _MC_R7 0x234 +#define _MC_R8 0x238 +#define _MC_R9 0x23c +#define _MC_R10 0x240 +#define _MC_R11 0x244 +#define _MC_R12 0x248 +#define _MC_R13 0x24c +#define _MC_R14 0x250 +#define _MC_R15 0x254 +#define _MC_R16 0x258 +#define _MC_R17 0x25c +#define _MC_R18 0x260 +#define _MC_R19 0x264 +#define _MC_R20 0x268 +#define _MC_R21 0x26c +#define _MC_R22 0x270 +#define _MC_R23 0x274 +#define _MC_R24 0x278 +#define _MC_R25 0x27c +#define _MC_R26 0x280 +#define _MC_R27 0x284 +#define _MC_R28 0x288 +#define _MC_R29 0x28c +#define _MC_R30 0x290 +#define _MC_R31 0x294 +#define _MC_LR 0x298 +#define _MC_CR 0x29c +#define _MC_XER 0x2a0 +#define _MC_CTR 0x2a4 + +#define _MC_FPSCR 0x3c0 +#define _MC_F0 0x2c0 +#define _MC_F1 0x2c8 +#define _MC_F2 0x2d0 +#define _MC_F3 0x2d8 +#define _MC_F4 0x2e0 +#define _MC_F5 0x2e8 +#define _MC_F6 0x2f0 +#define _MC_F7 0x2f8 +#define _MC_F8 0x300 +#define _MC_F9 0x308 +#define _MC_F10 0x310 +#define _MC_F11 0x318 +#define _MC_F12 0x320 +#define _MC_F13 0x328 +#define _MC_F14 0x330 +#define _MC_F15 0x338 +#define _MC_F16 0x340 +#define _MC_F17 0x348 +#define _MC_F18 0x350 +#define _MC_F19 0x358 +#define _MC_F20 0x360 +#define _MC_F21 0x368 +#define _MC_F22 0x370 +#define _MC_F23 0x378 +#define _MC_F24 0x380 +#define _MC_F25 0x388 +#define _MC_F26 0x390 +#define _MC_F27 0x398 +#define _MC_F28 0x3a0 +#define _MC_F29 0x3a8 +#define _MC_F30 0x3b0 +#define _MC_F31 0x3b8 + diff --git a/lib/libpthread/arch/powerpc/powerpc/context.S b/lib/libpthread/arch/powerpc/powerpc/context.S new file mode 100644 index 0000000..34d175a --- /dev/null +++ b/lib/libpthread/arch/powerpc/powerpc/context.S @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2004 Peter Grehan. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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> +__FBSDID("$FreeBSD$"); + +#include "assym.s" + +/* + * int _ppc32_getcontext(mcontext_t *mcp) + * + * Save register state from a voluntary context switch. + * Only volatile registers, and those needed to complete + * a setcontext call, need to be saved. + * + * r1 + * r14-31 + * f14-31 XXX + * lr + * + * Return 0 for this call, and set up the context so it will return + * 1 when restored with _ppc32_setcontext(). + * + * XXX XXX + * Floating-point is a big issue. Since there's no way to determine + * if the caller has used FP, all volatile register need to be saved. + * If FP hasn't been used, this results in a lazy FP exception in + * the kernel and from that point on FP is always switched in/out + * for the thread, which may be a big performance drag for the system. + * An alternative is to use a system call to get the context, which + * will do the right thing for floating point, but will save all + * registers rather than the caller-saved subset, and has the overhead + * of a syscall. + * Maybe another option would be to give a light-weight way for a + * thread to determine if FP is in used: perhaps a syscall that + * returns in the asm traphandler, or an OSX-style read-only page + * with a flag to indicate FP state. + * + * For now, punt the issue ala Alpha 1:1 model and fix in the future. + */ +ENTRY(_ppc32_getcontext) + stw %r1, _MC_R1(%r3) + stw %r13, _MC_R13(%r3) + stw %r14, _MC_R14(%r3) + stw %r15, _MC_R15(%r3) + stw %r16, _MC_R16(%r3) + stw %r17, _MC_R17(%r3) + stw %r18, _MC_R18(%r3) + stw %r19, _MC_R19(%r3) + stw %r20, _MC_R20(%r3) + stw %r21, _MC_R21(%r3) + stw %r22, _MC_R22(%r3) + stw %r23, _MC_R23(%r3) + stw %r24, _MC_R24(%r3) + stw %r25, _MC_R25(%r3) + stw %r26, _MC_R26(%r3) + stw %r27, _MC_R27(%r3) + stw %r28, _MC_R28(%r3) + stw %r29, _MC_R28(%r3) + stw %r30, _MC_R30(%r3) + stw %r31, _MC_R31(%r3) + mflr %r4 + stw %r4, _MC_LR(%r3) + mfcr %r4 + stw %r4, _MC_CR(%r3) + + /* XXX f14-31 ? */ + + li %r4, _MC_VERSION_KSE /* partial ucontext version */ + stw %r4, _MC_VERS(%r3) + + /* Return 0 */ + li %r3, 0 + blr + +/* + * int _ppc32_setcontext(const mcontext_t *mcp, intptr_t val, + * intptr_t *loc); + * + * Should only be called for partial KSE contexts. The full context + * case is handled by kse_switchin() in _thread_switch() + * + * Returns -1 on error and 1 for return from a saved context + */ + +ENTRY(_ppc32_setcontext) + lwz %r6, _MC_VERS(%r3) + cmpwi %r6, _MC_VERSION_KSE /* KSE partial context ? */ + beq 1f + li %r3, -1 /* invalid context type, return -1 */ + blr + +1: /* partial format, callee-saved regs assumed */ + lwz %r1, _MC_R1(%r3) + lwz %r13, _MC_R13(%r3) + lwz %r14, _MC_R14(%r3) + lwz %r15, _MC_R15(%r3) + lwz %r16, _MC_R16(%r3) + lwz %r17, _MC_R17(%r3) + lwz %r18, _MC_R18(%r3) + lwz %r19, _MC_R19(%r3) + lwz %r20, _MC_R20(%r3) + lwz %r21, _MC_R21(%r3) + lwz %r22, _MC_R22(%r3) + lwz %r23, _MC_R23(%r3) + lwz %r24, _MC_R24(%r3) + lwz %r25, _MC_R25(%r3) + lwz %r26, _MC_R26(%r3) + lwz %r27, _MC_R27(%r3) + lwz %r28, _MC_R28(%r3) + lwz %r29, _MC_R28(%r3) + lwz %r30, _MC_R30(%r3) + lwz %r31, _MC_R31(%r3) + lwz %r6, _MC_LR(%r3) + mtlr %r6 + lwz %r6, _MC_CR(%r3) + mtcr %r6 + + /* XXX f14-31 ? */ + + /* if (loc != NULL) *loc = val */ + cmpwi %r5, 0 + beq 2f + stw %r4, 0(%r5) + + /* Return 1 */ +2: li %r3, 1 + blr diff --git a/lib/libpthread/arch/powerpc/powerpc/enter_uts.S b/lib/libpthread/arch/powerpc/powerpc/enter_uts.S new file mode 100644 index 0000000..7cc4d7f --- /dev/null +++ b/lib/libpthread/arch/powerpc/powerpc/enter_uts.S @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2004 Peter Grehan. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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> +__FBSDID("$FreeBSD$"); + +/* + * _ppc32_enter_uts(struct kse_mailbox *km, kse_func_t uts, void *stack, + * long stacksz); + * + * Call (*uts)(km) on the requested stack. This function doesn't + * return. The km parameter stays in %r3. + */ +ENTRY(_ppc32_enter_uts) + add %r1,%r5,%r6 /* new stack = stack + stacksz */ + mtlr %r4 /* link register = uts */ + blrl /* (*uts)(km) */ diff --git a/lib/libpthread/arch/powerpc/powerpc/pthread_md.c b/lib/libpthread/arch/powerpc/powerpc/pthread_md.c new file mode 100644 index 0000000..c8445b1 --- /dev/null +++ b/lib/libpthread/arch/powerpc/powerpc/pthread_md.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2003 Daniel Eischen <deischen@freebsd.org> + * 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. Neither the name of the author 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 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdlib.h> +#include <strings.h> +#include "pthread_md.h" + +/* + * The constructors. + */ +struct tcb * +_tcb_ctor(struct pthread *thread, int initial) +{ + struct tcb *tcb; + + if ((tcb = malloc(sizeof(struct tcb))) != NULL) { + bzero(tcb, sizeof(struct tcb)); + tcb->tcb_thread = thread; + /* Allocate TDV */ + } + return (tcb); +} + +void +_tcb_dtor(struct tcb *tcb) +{ + /* Free TDV */ + free(tcb); +} + +struct kcb * +_kcb_ctor(struct kse *kse) +{ + struct kcb *kcb; + + if ((kcb = malloc(sizeof(struct kcb))) != NULL) { + bzero(kcb, sizeof(struct kcb)); + kcb->kcb_faketcb.tcb_isfake = 1; + kcb->kcb_faketcb.tcb_tmbx.tm_flags = TMF_NOUPCALL; + kcb->kcb_curtcb = &kcb->kcb_faketcb; + kcb->kcb_kse = kse; + } + return (kcb); +} + +void +_kcb_dtor(struct kcb *kcb) +{ + free(kcb); +} diff --git a/lib/libpthread/arch/sparc64/Makefile.inc b/lib/libpthread/arch/sparc64/Makefile.inc new file mode 100644 index 0000000..07107b4 --- /dev/null +++ b/lib/libpthread/arch/sparc64/Makefile.inc @@ -0,0 +1,5 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/arch/${MACHINE_ARCH}/${MACHINE_ARCH} + +SRCS+= pthread_md.c thr_getcontext.S diff --git a/lib/libpthread/arch/sparc64/include/atomic_ops.h b/lib/libpthread/arch/sparc64/include/atomic_ops.h new file mode 100644 index 0000000..4f4d8af --- /dev/null +++ b/lib/libpthread/arch/sparc64/include/atomic_ops.h @@ -0,0 +1,75 @@ +/*- + * Copyright (c) 2003 Jake Burkholder <jake@FreeBSD.org> + * 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. Neither the name of the author 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 AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef _ATOMIC_OPS_H_ +#define _ATOMIC_OPS_H_ + +#include <machine/atomic.h> + +/* + * Atomic swap: + * Atomic (tmp = *dst, *dst = val), then *res = tmp + * + * void atomic_swap_long(long *dst, long val, long *res); + */ +static __inline void +atomic_swap_long(long *dst, long val, long *res) +{ + long tmp; + long r; + + tmp = *dst; + for (;;) { + r = atomic_cas_64(dst, tmp, val); + if (r == tmp) + break; + tmp = r; + } + *res = tmp; +} + +static __inline void +atomic_swap_int(int *dst, int val, int *res) +{ + int tmp; + int r; + + tmp = *dst; + for (;;) { + r = atomic_cas_32(dst, tmp, val); + if (r == tmp) + break; + tmp = r; + } + *res = tmp; +} + +#define atomic_swap_ptr(dst, val, res) \ + atomic_swap_long((long *)dst, (long)val, (long *)res) + +#endif diff --git a/lib/libpthread/arch/sparc64/include/pthread_md.h b/lib/libpthread/arch/sparc64/include/pthread_md.h new file mode 100644 index 0000000..fac62c2 --- /dev/null +++ b/lib/libpthread/arch/sparc64/include/pthread_md.h @@ -0,0 +1,254 @@ +/*- + * Copyright (c) 2003 Jake Burkholder <jake@freebsd.org>. + * Copyright (c) 2003 Marcel Moolenaar + * 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. + * + * 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. + * + * $FreeBSD$ + */ + +/* + * Machine-dependent thread prototypes/definitions for the thread kernel. + */ +#ifndef _PTHREAD_MD_H_ +#define _PTHREAD_MD_H_ + +#include <sys/kse.h> +#include <stddef.h> +#include <ucontext.h> + +#define KSE_STACKSIZE 16384 +#define DTV_OFFSET offsetof(struct tcb, tcb_tp.tp_tdv) + +int _thr_setcontext(mcontext_t *, intptr_t, intptr_t *); +int _thr_getcontext(mcontext_t *); + +#define THR_GETCONTEXT(ucp) _thr_getcontext(&(ucp)->uc_mcontext) +#define THR_SETCONTEXT(ucp) _thr_setcontext(&(ucp)->uc_mcontext, 0, NULL) + +#define PER_THREAD + +struct kcb; +struct kse; +struct pthread; +struct tcb; +struct tdv; /* We don't know what this is yet? */ + + +/* + * %g6 points to one of these. We define the static TLS as an array + * of long double to enforce 16-byte alignment of the TLS memory. + * + * XXX - Both static and dynamic allocation of any of these structures + * will result in a valid, well-aligned thread pointer??? + */ +struct sparc64_tp { + struct tdv *tp_tdv; /* dynamic TLS */ + uint64_t _reserved_; + long double tp_tls[0]; /* static TLS */ +}; + +struct tcb { + struct pthread *tcb_thread; + void *tcb_addr; /* allocated tcb address */ + struct kcb *tcb_curkcb; + uint64_t tcb_isfake; + uint64_t tcb_spare[4]; + struct kse_thr_mailbox tcb_tmbx; /* needs 64-byte alignment */ + struct sparc64_tp tcb_tp; +} __aligned(64); + +struct kcb { + struct kse_mailbox kcb_kmbx; + struct tcb kcb_faketcb; + struct tcb *kcb_curtcb; + struct kse *kcb_kse; +}; + +register struct sparc64_tp *_tp __asm("%g6"); + +#define _tcb ((struct tcb*)((char*)(_tp) - offsetof(struct tcb, tcb_tp))) + +/* + * The kcb and tcb constructors. + */ +struct tcb *_tcb_ctor(struct pthread *, int); +void _tcb_dtor(struct tcb *); +struct kcb *_kcb_ctor(struct kse *kse); +void _kcb_dtor(struct kcb *); + +/* Called from the KSE to set its private data. */ +static __inline void +_kcb_set(struct kcb *kcb) +{ + /* There is no thread yet; use the fake tcb. */ + _tp = &kcb->kcb_faketcb.tcb_tp; +} + +/* + * Get the current kcb. + * + * This can only be called while in a critical region; don't + * worry about having the kcb changed out from under us. + */ +static __inline struct kcb * +_kcb_get(void) +{ + return (_tcb->tcb_curkcb); +} + +/* + * Enter a critical region. + * + * Read and clear km_curthread in the kse mailbox. + */ +static __inline struct kse_thr_mailbox * +_kcb_critical_enter(void) +{ + struct kse_thr_mailbox *crit; + uint32_t flags; + + if (_tcb->tcb_isfake != 0) { + /* + * We already are in a critical region since + * there is no current thread. + */ + crit = NULL; + } else { + flags = _tcb->tcb_tmbx.tm_flags; + _tcb->tcb_tmbx.tm_flags |= TMF_NOUPCALL; + crit = _tcb->tcb_curkcb->kcb_kmbx.km_curthread; + _tcb->tcb_curkcb->kcb_kmbx.km_curthread = NULL; + _tcb->tcb_tmbx.tm_flags = flags; + } + return (crit); +} + +static __inline void +_kcb_critical_leave(struct kse_thr_mailbox *crit) +{ + /* No need to do anything if this is a fake tcb. */ + if (_tcb->tcb_isfake == 0) + _tcb->tcb_curkcb->kcb_kmbx.km_curthread = crit; +} + +static __inline int +_kcb_in_critical(void) +{ + uint32_t flags; + int ret; + + if (_tcb->tcb_isfake != 0) { + /* + * We are in a critical region since there is no + * current thread. + */ + ret = 1; + } else { + flags = _tcb->tcb_tmbx.tm_flags; + _tcb->tcb_tmbx.tm_flags |= TMF_NOUPCALL; + ret = (_tcb->tcb_curkcb->kcb_kmbx.km_curthread == NULL); + _tcb->tcb_tmbx.tm_flags = flags; + } + return (ret); +} + +static __inline void +_tcb_set(struct kcb *kcb, struct tcb *tcb) +{ + if (tcb == NULL) + tcb = &kcb->kcb_faketcb; + kcb->kcb_curtcb = tcb; + tcb->tcb_curkcb = kcb; + _tp = &tcb->tcb_tp; +} + +static __inline struct tcb * +_tcb_get(void) +{ + return (_tcb); +} + +static __inline struct pthread * +_get_curthread(void) +{ + return (_tcb->tcb_thread); +} + +/* + * Get the current kse. + * + * Like _kcb_get(), this can only be called while in a critical region. + */ +static __inline struct kse * +_get_curkse(void) +{ + return (_tcb->tcb_curkcb->kcb_kse); +} + +void _sparc64_enter_uts(kse_func_t uts, struct kse_mailbox *km, void *stack, + size_t stacksz); + +static __inline int +_thread_enter_uts(struct tcb *tcb, struct kcb *kcb) +{ + if (_thr_getcontext(&tcb->tcb_tmbx.tm_context.uc_mcontext) == 0) { + /* Make the fake tcb the current thread. */ + kcb->kcb_curtcb = &kcb->kcb_faketcb; + _tp = &kcb->kcb_faketcb.tcb_tp; + _sparc64_enter_uts(kcb->kcb_kmbx.km_func, &kcb->kcb_kmbx, + kcb->kcb_kmbx.km_stack.ss_sp, + kcb->kcb_kmbx.km_stack.ss_size); + /* We should not reach here. */ + return (-1); + } + return (0); +} + +static __inline int +_thread_switch(struct kcb *kcb, struct tcb *tcb, int setmbox) +{ + extern int _libkse_debug; + mcontext_t *mc; + + _tcb_set(kcb, tcb); + mc = &tcb->tcb_tmbx.tm_context.uc_mcontext; + if (_libkse_debug == 0) { + tcb->tcb_tmbx.tm_lwp = kcb->kcb_kmbx.km_lwp; + if (setmbox) + _thr_setcontext(mc, (intptr_t)&tcb->tcb_tmbx, + (intptr_t *)&kcb->kcb_kmbx.km_curthread); + else + _thr_setcontext(mc, 0, NULL); + } else { + if (setmbox) + kse_switchin(&tcb->tcb_tmbx, KSE_SWITCHIN_SETTMBX); + else + kse_switchin(&tcb->tcb_tmbx, 0); + } + + /* We should not reach here. */ + return (-1); +} + +#endif /* _PTHREAD_MD_H_ */ diff --git a/lib/libpthread/arch/sparc64/sparc64/assym.s b/lib/libpthread/arch/sparc64/sparc64/assym.s new file mode 100644 index 0000000..3e22c9f --- /dev/null +++ b/lib/libpthread/arch/sparc64/sparc64/assym.s @@ -0,0 +1,15 @@ +/* + * Offsets into structures used from asm. Must be kept in sync with + * appropriate headers. + * + * $FreeBSD$ + */ + +#define UC_MCONTEXT 0x40 + +#define MC_FLAGS 0x0 +#define MC_VALID_FLAGS 0x1 +#define MC_GLOBAL 0x0 +#define MC_OUT 0x40 +#define MC_TPC 0xc8 +#define MC_TNPC 0xc0 diff --git a/lib/libpthread/arch/sparc64/sparc64/pthread_md.c b/lib/libpthread/arch/sparc64/sparc64/pthread_md.c new file mode 100644 index 0000000..d6bf95d --- /dev/null +++ b/lib/libpthread/arch/sparc64/sparc64/pthread_md.c @@ -0,0 +1,91 @@ +/*- + * Copyright (C) 2003 Jake Burkholder <jake@freebsd.org> + * Copyright (C) 2003 David Xu <davidxu@freebsd.org> + * Copyright (c) 2001,2003 Daniel Eischen <deischen@freebsd.org> + * 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. Neither the name of the author 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 AUTHOR 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 AUTHOR 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> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> + +#include <unistd.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <ucontext.h> + +#include "pthread_md.h" + +struct tcb * +_tcb_ctor(struct pthread *thread, int initial) +{ + struct tcb *tcb; + void *addr; + + addr = malloc(sizeof(struct tcb) + 63); + if (addr == NULL) + tcb = NULL; + else { + tcb = (struct tcb *)(((uintptr_t)(addr) + 63) & ~63); + bzero(tcb, sizeof(struct tcb)); + tcb->tcb_addr = addr; + tcb->tcb_thread = thread; + /* XXX - Allocate tdv/tls */ + } + return (tcb); +} + +void +_tcb_dtor(struct tcb *tcb) +{ + void *addr; + + addr = tcb->tcb_addr; + tcb->tcb_addr = NULL; + free(addr); +} + +struct kcb * +_kcb_ctor(struct kse *kse) +{ + struct kcb *kcb; + + kcb = malloc(sizeof(struct kcb)); + if (kcb != NULL) { + bzero(kcb, sizeof(struct kcb)); + kcb->kcb_faketcb.tcb_isfake = 1; + kcb->kcb_faketcb.tcb_tmbx.tm_flags = TMF_NOUPCALL; + kcb->kcb_curtcb = &kcb->kcb_faketcb; + kcb->kcb_kse = kse; + } + return (kcb); +} + +void +_kcb_dtor(struct kcb *kcb) +{ + free(kcb); +} diff --git a/lib/libpthread/arch/sparc64/sparc64/thr_getcontext.S b/lib/libpthread/arch/sparc64/sparc64/thr_getcontext.S new file mode 100644 index 0000000..ca6473a --- /dev/null +++ b/lib/libpthread/arch/sparc64/sparc64/thr_getcontext.S @@ -0,0 +1,87 @@ +/*- + * Copyright (C) 2003 Jake Burkholder <jake@freebsd.org> + * 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. Neither the name of the author 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 AUTHOR 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 AUTHOR 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> +__FBSDID("$FreeBSD$"); + +#include "assym.s" + + .weak CNAME(_thr_getcontext) + .set CNAME(_thr_getcontext),CNAME(__thr_getcontext) +ENTRY(__thr_getcontext) + add %o7, 8, %o1 + add %o1, 4, %o2 + stx %sp, [%o0 + MC_OUT + (6 * 8)] + stx %o1, [%o0 + MC_TPC] + stx %o2, [%o0 + MC_TNPC] + mov MC_VALID_FLAGS, %l0 /* Validate the context. */ + stx %l0, [%o0 + MC_FLAGS] + mov 1, %l0 + stx %l0, [%o0 + MC_OUT + (0 * 8)] /* return 1 when resumed */ + retl + mov 0, %o0 /* return 0 */ +END(__thr_getcontext) + + .weak CNAME(_thr_setcontext) + .set CNAME(_thr_setcontext),CNAME(__thr_setcontext) +ENTRY(__thr_setcontext) + save %sp, -CCFSZ, %sp + flushw + mov %i0, %l0 + mov %i1, %l1 + mov %i2, %l2 + ldx [%l0 + MC_GLOBAL + (1 * 8)], %g1 + ldx [%l0 + MC_GLOBAL + (2 * 8)], %g2 + ldx [%l0 + MC_GLOBAL + (3 * 8)], %g3 + ldx [%l0 + MC_GLOBAL + (4 * 8)], %g4 + ldx [%l0 + MC_GLOBAL + (5 * 8)], %g5 + ldx [%l0 + MC_GLOBAL + (6 * 8)], %g6 + ldx [%l0 + MC_GLOBAL + (7 * 8)], %g7 + ldx [%l0 + MC_OUT + (0 * 8)], %i0 + ldx [%l0 + MC_OUT + (1 * 8)], %i1 + ldx [%l0 + MC_OUT + (2 * 8)], %i2 + ldx [%l0 + MC_OUT + (3 * 8)], %i3 + ldx [%l0 + MC_OUT + (4 * 8)], %i4 + ldx [%l0 + MC_OUT + (5 * 8)], %i5 + ldx [%l0 + MC_OUT + (6 * 8)], %i6 + ldx [%l0 + MC_OUT + (7 * 8)], %i7 + ldx [%l0 + MC_TPC], %l4 + ldx [%l0 + MC_TNPC], %l3 + brz %l2, 1f + nop + stx %l1, [%l2] +1: jmpl %l3, %g0 + return %l4 +END(__thr_setcontext) + +ENTRY(_sparc64_enter_uts) + save %sp, -CCFSZ, %sp + flushw + add %i2, %i3, %i2 + sub %i2, SPOFF + CCFSZ, %sp + jmpl %i0, %g0 + mov %i1, %o0 +END(_sparc64_enter_uts) diff --git a/lib/libpthread/pthread.map b/lib/libpthread/pthread.map new file mode 100644 index 0000000..31ee580 --- /dev/null +++ b/lib/libpthread/pthread.map @@ -0,0 +1,737 @@ +# $FreeBSD$ + +# +# Hack. libpthread had versioning before libc, but we need to +# reside in the same namespace as libc if we want to override +# libc functions. Use this so we don't break older applications +# that require symbols from "LIBTHREAD_1_0". +# +# From now on, use the same naming scheme as libc. +# +# +LIBTHREAD_1_0 { +global: + ___creat; + __accept; + __close; + __connect; + __error; + __fcntl; + __fsync; + __msync; + __nanosleep; + __open; + __poll; + __pthread_cond_timedwait; + __pthread_cond_wait; + __pthread_mutex_init; + __pthread_mutex_lock; + __pthread_mutex_trylock; + __pthread_mutex_timedlock; + __read; + __readv; + __select; + __sigsuspend; + __sigtimedwait; + __sigwait; + __sigwaitinfo; + __wait4; + __write; + __writev; + _aio_suspend; + _execve; + _fork; + _nanosleep; + _pause; + _pselect; + _pthread_atfork; + _pthread_barrier_destroy; + _pthread_barrier_init; + _pthread_barrier_wait; + _pthread_barrierattr_destroy; + _pthread_barrierattr_getpshared; + _pthread_barrierattr_init; + _pthread_barrierattr_setpshared; + _pthread_attr_destroy; + _pthread_attr_get_np; + _pthread_attr_getdetachstate; + _pthread_attr_getguardsize; + _pthread_attr_getinheritsched; + _pthread_attr_getschedparam; + _pthread_attr_getschedpolicy; + _pthread_attr_getscope; + _pthread_attr_getstack; + _pthread_attr_getstackaddr; + _pthread_attr_getstacksize; + _pthread_attr_init; + _pthread_attr_setcreatesuspend_np; + _pthread_attr_setdetachstate; + _pthread_attr_setguardsize; + _pthread_attr_setinheritsched; + _pthread_attr_setschedparam; + _pthread_attr_setschedpolicy; + _pthread_attr_setscope; + _pthread_attr_setstack; + _pthread_attr_setstackaddr; + _pthread_attr_setstacksize; + _pthread_cancel; + _pthread_cleanup_pop; + _pthread_cleanup_push; + _pthread_cond_broadcast; + _pthread_cond_destroy; + _pthread_cond_init; + _pthread_cond_signal; + _pthread_cond_timedwait; + _pthread_cond_wait; + _pthread_condattr_default; + _pthread_condattr_destroy; + _pthread_condattr_getpshared; + _pthread_condattr_init; + _pthread_condattr_setpshared; + _pthread_create; + _pthread_detach; + _pthread_equal; + _pthread_exit; + _pthread_getconcurrency; + _pthread_getprio; + _pthread_getschedparam; + _pthread_getspecific; + _pthread_join; + _pthread_key_create; + _pthread_key_delete; + _pthread_kill; + _pthread_main_np; + _pthread_multi_np; + _pthread_mutex_destroy; + _pthread_mutex_getprioceiling; + _pthread_mutex_init; + _pthread_mutex_lock; + _pthread_mutex_setprioceiling; + _pthread_mutex_timedlock; + _pthread_mutex_trylock; + _pthread_mutex_unlock; + _pthread_mutexattr_default; + _pthread_mutexattr_destroy; + _pthread_mutexattr_getkind_np; + _pthread_mutexattr_getprioceiling; + _pthread_mutexattr_getprotocol; + _pthread_mutexattr_getpshared; + _pthread_mutexattr_gettype; + _pthread_mutexattr_init; + _pthread_mutexattr_setkind_np; + _pthread_mutexattr_setprioceiling; + _pthread_mutexattr_setprotocol; + _pthread_mutexattr_setpshared; + _pthread_mutexattr_settype; + _pthread_once; + _pthread_resume_all_np; + _pthread_resume_np; + _pthread_rwlock_destroy; + _pthread_rwlock_init; + _pthread_rwlock_rdlock; + _pthread_rwlock_timedrdlock; + _pthread_rwlock_timedwrlock; + _pthread_rwlock_tryrdlock; + _pthread_rwlock_trywrlock; + _pthread_rwlock_unlock; + _pthread_rwlock_wrlock; + _pthread_rwlockattr_destroy; + _pthread_rwlockattr_getpshared; + _pthread_rwlockattr_init; + _pthread_rwlockattr_setpshared; + _pthread_self; + _pthread_set_name_np; + _pthread_setcancelstate; + _pthread_setcanceltype; + _pthread_setconcurrency; + _pthread_setprio; + _pthread_setschedparam; + _pthread_setspecific; + _pthread_sigmask; + _pthread_single_np; + _pthread_spin_destroy; + _pthread_spin_init; + _pthread_spin_lock; + _pthread_spin_trylock; + _pthread_spin_unlock; + _pthread_suspend_all_np; + _pthread_suspend_np; + _pthread_switch_add_np; + _pthread_switch_delete_np; + _pthread_testcancel; + _pthread_yield; + _raise; + _sched_yield; + _sem_init; + _sem_post; + _sem_timedwait; + _sem_wait; + _sigaction; + _sigaltstack; + _sigpending; + _sigprocmask; + _sigsuspend; + _sigtimedwait; + _sigwait; + _sigwaitinfo; + _sleep; + _spinlock; + _spinlock_debug; + _spinunlock; + _system; + _tcdrain; + _usleep; + _vfork; + _wait; + _waitpid; + accept; + aio_suspend; + close; + connect; + creat; + execve; + fcntl; + fork; + fsync; + msync; + nanosleep; + open; + pause; + poll; + pselect; + pthread_atfork; + pthread_barrier_destroy; + pthread_barrier_init; + pthread_barrier_wait; + pthread_barrierattr_destroy; + pthread_barrierattr_getpshared; + pthread_barrierattr_init; + pthread_barrierattr_setpshared; + pthread_attr_destroy; + pthread_attr_get_np; + pthread_attr_getdetachstate; + pthread_attr_getguardsize; + pthread_attr_getinheritsched; + pthread_attr_getschedparam; + pthread_attr_getschedpolicy; + pthread_attr_getscope; + pthread_attr_getstack; + pthread_attr_getstackaddr; + pthread_attr_getstacksize; + pthread_attr_init; + pthread_attr_setcreatesuspend_np; + pthread_attr_setdetachstate; + pthread_attr_setguardsize; + pthread_attr_setinheritsched; + pthread_attr_setschedparam; + pthread_attr_setschedpolicy; + pthread_attr_setscope; + pthread_attr_setstack; + pthread_attr_setstackaddr; + pthread_attr_setstacksize; + pthread_cancel; + pthread_cleanup_pop; + pthread_cleanup_push; + pthread_cond_broadcast; + pthread_cond_destroy; + pthread_cond_init; + pthread_cond_signal; + pthread_cond_timedwait; + pthread_cond_wait; + pthread_condattr_destroy; + pthread_condattr_getpshared; + pthread_condattr_init; + pthread_condattr_setpshared; + pthread_create; + pthread_detach; + pthread_equal; + pthread_exit; + pthread_getconcurrency; + pthread_getprio; + pthread_getschedparam; + pthread_getspecific; + pthread_join; + pthread_key_create; + pthread_key_delete; + pthread_kill; + pthread_main_np; + pthread_multi_np; + pthread_mutex_destroy; + pthread_mutex_getprioceiling; + pthread_mutex_init; + pthread_mutex_lock; + pthread_mutex_setprioceiling; + pthread_mutex_timedlock; + pthread_mutex_trylock; + pthread_mutex_unlock; + pthread_mutexattr_destroy; + pthread_mutexattr_getkind_np; + pthread_mutexattr_getprioceiling; + pthread_mutexattr_getprotocol; + pthread_mutexattr_getpshared; + pthread_mutexattr_gettype; + pthread_mutexattr_init; + pthread_mutexattr_setkind_np; + pthread_mutexattr_setprioceiling; + pthread_mutexattr_setprotocol; + pthread_mutexattr_setpshared; + pthread_mutexattr_settype; + pthread_once; + pthread_resume_all_np; + pthread_resume_np; + pthread_rwlock_destroy; + pthread_rwlock_init; + pthread_rwlock_rdlock; + pthread_rwlock_timedrdlock; + pthread_rwlock_timedwrlock; + pthread_rwlock_tryrdlock; + pthread_rwlock_trywrlock; + pthread_rwlock_unlock; + pthread_rwlock_wrlock; + pthread_rwlockattr_destroy; + pthread_rwlockattr_getpshared; + pthread_rwlockattr_init; + pthread_rwlockattr_setpshared; + pthread_self; + pthread_set_name_np; + pthread_setcancelstate; + pthread_setcanceltype; + pthread_setconcurrency; + pthread_setprio; + pthread_setschedparam; + pthread_setspecific; + pthread_sigmask; + pthread_single_np; + pthread_spin_destroy; + pthread_spin_init; + pthread_spin_lock; + pthread_spin_trylock; + pthread_spin_unlock; + pthread_suspend_all_np; + pthread_suspend_np; + pthread_switch_add_np; + pthread_switch_delete_np; + pthread_testcancel; + pthread_yield; + raise; + read; + readv; + sched_yield; + select; + sem_init; + sem_post; + sem_timedwait; + sem_wait; + sigaction; + sigaltstack; + sigpending; + sigprocmask; + sigsuspend; + sigwait; + sigwaitinfo; + sigtimedwait; + sleep; + system; + tcdrain; + usleep; + vfork; + wait4; + wait; + waitpid; + write; + writev; + + # Debugger needs these. + _libkse_debug; + _thread_activated; + _thread_active_threads; + _thread_keytable; + _thread_list; + _thread_max_keys; + _thread_off_attr_flags; + _thread_off_dtv; + _thread_off_linkmap; + _thread_off_next; + _thread_off_tcb; + _thread_off_tmbx; + _thread_off_key_allocated; + _thread_off_key_destructor; + _thread_off_kse; + _thread_off_kse_locklevel; + _thread_off_state; + _thread_off_thr_locklevel; + _thread_off_tlsindex; + _thread_size_key; + _thread_state_running; + _thread_state_zoombie; + +local: + *; +}; + +# +# Use the same naming scheme as libc. +# +FBSD_1.0 { +global: + __error; + accept; + aio_suspend; + close; + connect; + creat; + execve; + fcntl; + fork; + fsync; + msync; + nanosleep; + open; + pause; + poll; + pselect; + pthread_atfork; + pthread_barrier_destroy; + pthread_barrier_init; + pthread_barrier_wait; + pthread_barrierattr_destroy; + pthread_barrierattr_getpshared; + pthread_barrierattr_init; + pthread_barrierattr_setpshared; + pthread_attr_destroy; + pthread_attr_get_np; + pthread_attr_getdetachstate; + pthread_attr_getguardsize; + pthread_attr_getinheritsched; + pthread_attr_getschedparam; + pthread_attr_getschedpolicy; + pthread_attr_getscope; + pthread_attr_getstack; + pthread_attr_getstackaddr; + pthread_attr_getstacksize; + pthread_attr_init; + pthread_attr_setcreatesuspend_np; + pthread_attr_setdetachstate; + pthread_attr_setguardsize; + pthread_attr_setinheritsched; + pthread_attr_setschedparam; + pthread_attr_setschedpolicy; + pthread_attr_setscope; + pthread_attr_setstack; + pthread_attr_setstackaddr; + pthread_attr_setstacksize; + pthread_cancel; + pthread_cleanup_pop; + pthread_cleanup_push; + pthread_cond_broadcast; + pthread_cond_destroy; + pthread_cond_init; + pthread_cond_signal; + pthread_cond_timedwait; + pthread_cond_wait; + pthread_condattr_destroy; + pthread_condattr_init; + pthread_create; + pthread_detach; + pthread_equal; + pthread_exit; + pthread_getconcurrency; + pthread_getprio; + pthread_getschedparam; + pthread_getspecific; + pthread_join; + pthread_key_create; + pthread_key_delete; + pthread_kill; + pthread_main_np; + pthread_multi_np; + pthread_mutex_destroy; + pthread_mutex_getprioceiling; + pthread_mutex_init; + pthread_mutex_lock; + pthread_mutex_setprioceiling; + pthread_mutex_timedlock; + pthread_mutex_trylock; + pthread_mutex_unlock; + pthread_mutexattr_destroy; + pthread_mutexattr_getkind_np; + pthread_mutexattr_getprioceiling; + pthread_mutexattr_getprotocol; + pthread_mutexattr_gettype; + pthread_mutexattr_init; + pthread_mutexattr_setkind_np; + pthread_mutexattr_setprioceiling; + pthread_mutexattr_setprotocol; + pthread_mutexattr_settype; + pthread_once; + pthread_resume_all_np; + pthread_resume_np; + pthread_rwlock_destroy; + pthread_rwlock_init; + pthread_rwlock_rdlock; + pthread_rwlock_timedrdlock; + pthread_rwlock_timedwrlock; + pthread_rwlock_tryrdlock; + pthread_rwlock_trywrlock; + pthread_rwlock_unlock; + pthread_rwlock_wrlock; + pthread_rwlockattr_destroy; + pthread_rwlockattr_getpshared; + pthread_rwlockattr_init; + pthread_rwlockattr_setpshared; + pthread_self; + pthread_set_name_np; + pthread_setcancelstate; + pthread_setcanceltype; + pthread_setconcurrency; + pthread_setprio; + pthread_setschedparam; + pthread_setspecific; + pthread_sigmask; + pthread_single_np; + pthread_spin_destroy; + pthread_spin_init; + pthread_spin_lock; + pthread_spin_trylock; + pthread_spin_unlock; + pthread_suspend_all_np; + pthread_suspend_np; + pthread_switch_add_np; + pthread_switch_delete_np; + pthread_testcancel; + pthread_yield; + raise; + read; + readv; + sched_yield; + select; + sem_init; + sem_post; + sem_timedwait; + sem_wait; + sigaction; + sigaltstack; + sigpending; + sigprocmask; + sigsuspend; + sigwait; + sigwaitinfo; + sigtimedwait; + sleep; + system; + tcdrain; + usleep; + vfork; + wait4; + wait; + waitpid; + write; + writev; +local: + *; +}; + +# +# List the private interfaces reserved for use in FreeBSD libraries. +# These are not part of our application ABI. +# +FBSDprivate { +global: + ___creat; + __accept; + __close; + __connect; + __fcntl; + __fsync; + __msync; + __nanosleep; + __open; + __poll; + __pthread_cond_timedwait; + __pthread_cond_wait; + __pthread_mutex_init; + __pthread_mutex_lock; + __pthread_mutex_trylock; + __pthread_mutex_timedlock; + __read; + __readv; + __select; + __sigsuspend; + __sigtimedwait; + __sigwait; + __sigwaitinfo; + __wait4; + __write; + __writev; + _aio_suspend; + _execve; + _fork; + _nanosleep; + _pause; + _pselect; + _pthread_atfork; + _pthread_barrier_destroy; + _pthread_barrier_init; + _pthread_barrier_wait; + _pthread_barrierattr_destroy; + _pthread_barrierattr_getpshared; + _pthread_barrierattr_init; + _pthread_barrierattr_setpshared; + _pthread_attr_destroy; + _pthread_attr_get_np; + _pthread_attr_getdetachstate; + _pthread_attr_getguardsize; + _pthread_attr_getinheritsched; + _pthread_attr_getschedparam; + _pthread_attr_getschedpolicy; + _pthread_attr_getscope; + _pthread_attr_getstack; + _pthread_attr_getstackaddr; + _pthread_attr_getstacksize; + _pthread_attr_init; + _pthread_attr_setcreatesuspend_np; + _pthread_attr_setdetachstate; + _pthread_attr_setguardsize; + _pthread_attr_setinheritsched; + _pthread_attr_setschedparam; + _pthread_attr_setschedpolicy; + _pthread_attr_setscope; + _pthread_attr_setstack; + _pthread_attr_setstackaddr; + _pthread_attr_setstacksize; + _pthread_cancel; + _pthread_cleanup_pop; + _pthread_cleanup_push; + _pthread_cond_broadcast; + _pthread_cond_destroy; + _pthread_cond_init; + _pthread_cond_signal; + _pthread_cond_timedwait; + _pthread_cond_wait; + _pthread_condattr_default; + _pthread_condattr_destroy; + _pthread_condattr_init; + _pthread_create; + _pthread_detach; + _pthread_equal; + _pthread_exit; + _pthread_getconcurrency; + _pthread_getprio; + _pthread_getschedparam; + _pthread_getspecific; + _pthread_join; + _pthread_key_create; + _pthread_key_delete; + _pthread_kill; + _pthread_main_np; + _pthread_multi_np; + _pthread_mutex_destroy; + _pthread_mutex_getprioceiling; + _pthread_mutex_init; + _pthread_mutex_lock; + _pthread_mutex_setprioceiling; + _pthread_mutex_timedlock; + _pthread_mutex_trylock; + _pthread_mutex_unlock; + _pthread_mutexattr_default; + _pthread_mutexattr_destroy; + _pthread_mutexattr_getkind_np; + _pthread_mutexattr_getprioceiling; + _pthread_mutexattr_getprotocol; + _pthread_mutexattr_gettype; + _pthread_mutexattr_init; + _pthread_mutexattr_setkind_np; + _pthread_mutexattr_setprioceiling; + _pthread_mutexattr_setprotocol; + _pthread_mutexattr_settype; + _pthread_once; + _pthread_resume_all_np; + _pthread_resume_np; + _pthread_rwlock_destroy; + _pthread_rwlock_init; + _pthread_rwlock_rdlock; + _pthread_rwlock_timedrdlock; + _pthread_rwlock_timedwrlock; + _pthread_rwlock_tryrdlock; + _pthread_rwlock_trywrlock; + _pthread_rwlock_unlock; + _pthread_rwlock_wrlock; + _pthread_rwlockattr_destroy; + _pthread_rwlockattr_getpshared; + _pthread_rwlockattr_init; + _pthread_rwlockattr_setpshared; + _pthread_self; + _pthread_set_name_np; + _pthread_setcancelstate; + _pthread_setcanceltype; + _pthread_setconcurrency; + _pthread_setprio; + _pthread_setschedparam; + _pthread_setspecific; + _pthread_sigmask; + _pthread_single_np; + _pthread_spin_destroy; + _pthread_spin_init; + _pthread_spin_lock; + _pthread_spin_trylock; + _pthread_spin_unlock; + _pthread_suspend_all_np; + _pthread_suspend_np; + _pthread_switch_add_np; + _pthread_switch_delete_np; + _pthread_testcancel; + _pthread_yield; + _raise; + _sched_yield; + _sem_init; + _sem_post; + _sem_timedwait; + _sem_wait; + _sigaction; + _sigaltstack; + _sigpending; + _sigprocmask; + _sigsuspend; + _sigtimedwait; + _sigwait; + _sigwaitinfo; + _sleep; + _spinlock; + _spinlock_debug; + _spinunlock; + _system; + _tcdrain; + _usleep; + _vfork; + _wait; + _waitpid; + + # Debugger needs these. + _libkse_debug; + _thread_activated; + _thread_active_threads; + _thread_keytable; + _thread_list; + _thread_max_keys; + _thread_off_attr_flags; + _thread_off_dtv; + _thread_off_linkmap; + _thread_off_next; + _thread_off_tcb; + _thread_off_tmbx; + _thread_off_key_allocated; + _thread_off_key_destructor; + _thread_off_kse; + _thread_off_kse_locklevel; + _thread_off_sigmask; + _thread_off_sigpend; + _thread_off_state; + _thread_off_thr_locklevel; + _thread_off_tlsindex; + _thread_size_key; + _thread_state_running; + _thread_state_zoombie; + +local: + *; +}; diff --git a/lib/libpthread/support/Makefile.inc b/lib/libpthread/support/Makefile.inc new file mode 100644 index 0000000..956667f --- /dev/null +++ b/lib/libpthread/support/Makefile.inc @@ -0,0 +1,40 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/support ${.CURDIR}/../libc/gen ${.CURDIR}/../libc/string +.PATH: ${.CURDIR}/../libc/${MACHINE_ARCH}/sys + +CFLAGS+= -I${.CURDIR}/../libc/${MACHINE_ARCH} + +SYSCALLS= clock_gettime \ + kse_create \ + kse_exit \ + kse_release \ + kse_switchin \ + kse_thr_interrupt \ + kse_wakeup \ + sigaction \ + sigprocmask \ + sigtimedwait \ + write + +SYSCALL_SRC= ${SYSCALLS:S/$/.S/} +SYSCALL_OBJ= ${SYSCALLS:S/$/.So/} + +${SYSCALL_SRC}: + printf '#include "SYS.h"\nRSYSCALL(${.PREFIX})\n' > ${.TARGET} + +LIBC_OBJS= sigsetops.So \ + bcopy.So \ + bzero.So \ + cerror.So \ + memcpy.So \ + memset.So \ + strcpy.So \ + strlen.So + +SOBJS+= thr_libc.So +CLEANFILES+= ${SYSCALL_SRC} ${SYSCALL_OBJ} ${LIBC_OBJS} + +thr_libc.So: ${SYSCALL_OBJ} ${LIBC_OBJS} + ${CC} -fPIC -nostdlib -o ${.TARGET} -r ${.ALLSRC} + diff --git a/lib/libpthread/support/thr_support.c b/lib/libpthread/support/thr_support.c new file mode 100644 index 0000000..2956e07 --- /dev/null +++ b/lib/libpthread/support/thr_support.c @@ -0,0 +1,62 @@ +/*- + * Copyright 2003 Alexander Kabaev. + * 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. + * + * 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. + * + * $FreeBSD$ + */ + +#include <sys/cdefs.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/kse.h> +#include <signal.h> +#include <string.h> + +#include "thr_private.h" + +__strong_reference(clock_gettime, _thr_clock_gettime); +__strong_reference(kse_exit, _thr_kse_exit); +__strong_reference(kse_wakeup, _thr_kse_wakeup); +__strong_reference(kse_create, _thr_kse_create); +__strong_reference(kse_thr_interrupt, _thr_kse_thr_interrupt); +__strong_reference(kse_release, _thr_kse_release); +__strong_reference(kse_switchin, _thr_kse_switchin); + +__strong_reference(sigaction, _thr_sigaction); +__strong_reference(sigprocmask, _thr_sigprocmask); +__strong_reference(sigemptyset, _thr_sigemptyset); +__strong_reference(sigaddset, _thr_sigaddset); +__strong_reference(sigfillset, _thr_sigfillset); +__strong_reference(sigismember, _thr_sigismember); +__strong_reference(sigdelset, _thr_sigdelset); + +__strong_reference(memset, _thr_memset); +__strong_reference(memcpy, _thr_memcpy); +__strong_reference(strcpy, _thr_strcpy); +__strong_reference(strlen, _thr_strlen); +__strong_reference(bzero, _thr_bzero); +__strong_reference(bcopy, _thr_bcopy); + +__strong_reference(__sys_write, _thr__sys_write); +__strong_reference(__sys_sigtimedwait, _thr__sys_sigtimedwait); + diff --git a/lib/libpthread/sys/Makefile.inc b/lib/libpthread/sys/Makefile.inc new file mode 100644 index 0000000..fb4a108 --- /dev/null +++ b/lib/libpthread/sys/Makefile.inc @@ -0,0 +1,5 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/sys + +SRCS+= lock.c thr_error.c diff --git a/lib/libpthread/sys/lock.c b/lib/libpthread/sys/lock.c new file mode 100644 index 0000000..2ac8c0c --- /dev/null +++ b/lib/libpthread/sys/lock.c @@ -0,0 +1,351 @@ +/*- + * Copyright (c) 2001, 2003 Daniel Eischen <deischen@freebsd.org>. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <machine/atomic.h> +#include <assert.h> +#include <stdlib.h> + +#include "atomic_ops.h" +#include "lock.h" + +#ifdef _LOCK_DEBUG +#define LCK_ASSERT(e) assert(e) +#else +#define LCK_ASSERT(e) +#endif + +#define MAX_SPINS 500 + +void +_lock_destroy(struct lock *lck) +{ + if ((lck != NULL) && (lck->l_head != NULL)) { + free(lck->l_head); + lck->l_head = NULL; + lck->l_tail = NULL; + } +} + +int +_lock_init(struct lock *lck, enum lock_type ltype, + lock_handler_t *waitfunc, lock_handler_t *wakeupfunc) +{ + if (lck == NULL) + return (-1); + else if ((lck->l_head = malloc(sizeof(struct lockreq))) == NULL) + return (-1); + else { + lck->l_type = ltype; + lck->l_wait = waitfunc; + lck->l_wakeup = wakeupfunc; + lck->l_head->lr_locked = 0; + lck->l_head->lr_watcher = NULL; + lck->l_head->lr_owner = NULL; + lck->l_head->lr_active = 1; + lck->l_tail = lck->l_head; + } + return (0); +} + +int +_lock_reinit(struct lock *lck, enum lock_type ltype, + lock_handler_t *waitfunc, lock_handler_t *wakeupfunc) +{ + if (lck == NULL) + return (-1); + else if (lck->l_head == NULL) + return (_lock_init(lck, ltype, waitfunc, wakeupfunc)); + else { + lck->l_head->lr_locked = 0; + lck->l_head->lr_watcher = NULL; + lck->l_head->lr_owner = NULL; + lck->l_head->lr_active = 1; + lck->l_tail = lck->l_head; + } + return (0); +} + +int +_lockuser_init(struct lockuser *lu, void *priv) +{ + if (lu == NULL) + return (-1); + else if ((lu->lu_myreq == NULL) && + ((lu->lu_myreq = malloc(sizeof(struct lockreq))) == NULL)) + return (-1); + else { + lu->lu_myreq->lr_locked = 1; + lu->lu_myreq->lr_watcher = NULL; + lu->lu_myreq->lr_owner = lu; + lu->lu_myreq->lr_active = 0; + lu->lu_watchreq = NULL; + lu->lu_priority = 0; + lu->lu_private = priv; + lu->lu_private2 = NULL; + } + return (0); +} + +int +_lockuser_reinit(struct lockuser *lu, void *priv) +{ + if (lu == NULL) + return (-1); + /* + * All lockusers keep their watch request and drop their + * own (lu_myreq) request. Their own request is either + * some other lockuser's watch request or is the head of + * the lock. + */ + lu->lu_myreq = lu->lu_watchreq; + if (lu->lu_myreq == NULL) + return (_lockuser_init(lu, priv)); + else { + lu->lu_myreq->lr_locked = 1; + lu->lu_myreq->lr_watcher = NULL; + lu->lu_myreq->lr_owner = lu; + lu->lu_myreq->lr_active = 0; + lu->lu_watchreq = NULL; + lu->lu_priority = 0; + lu->lu_private = priv; + lu->lu_private2 = NULL; + } + return (0); +} + +void +_lockuser_destroy(struct lockuser *lu) +{ + if ((lu != NULL) && (lu->lu_myreq != NULL)) + free(lu->lu_myreq); +} + +/* + * Acquire a lock waiting (spin or sleep) for it to become available. + */ +void +_lock_acquire(struct lock *lck, struct lockuser *lu, int prio) +{ + int i; + int lval; + + /** + * XXX - We probably want to remove these checks to optimize + * performance. It is also a bug if any one of the + * checks fail, so it's probably better to just let it + * SEGV and fix it. + */ +#if 0 + if (lck == NULL || lu == NULL || lck->l_head == NULL) + return; +#endif + if ((lck->l_type & LCK_PRIORITY) != 0) { + LCK_ASSERT(lu->lu_myreq->lr_locked == 1); + LCK_ASSERT(lu->lu_myreq->lr_watcher == NULL); + LCK_ASSERT(lu->lu_myreq->lr_owner == lu); + LCK_ASSERT(lu->lu_watchreq == NULL); + + lu->lu_priority = prio; + } + /* + * Atomically swap the head of the lock request with + * this request. + */ + atomic_swap_ptr(&lck->l_head, lu->lu_myreq, &lu->lu_watchreq); + + if (lu->lu_watchreq->lr_locked != 0) { + atomic_store_rel_ptr + ((volatile uintptr_t *)&lu->lu_watchreq->lr_watcher, + (uintptr_t)lu); + if ((lck->l_wait == NULL) || + ((lck->l_type & LCK_ADAPTIVE) == 0)) { + while (lu->lu_watchreq->lr_locked != 0) + ; /* spin, then yield? */ + } else { + /* + * Spin for a bit before invoking the wait function. + * + * We should be a little smarter here. If we're + * running on a single processor, then the lock + * owner got preempted and spinning will accomplish + * nothing but waste time. If we're running on + * multiple processors, the owner could be running + * on another CPU and we might acquire the lock if + * we spin for a bit. + * + * The other thing to keep in mind is that threads + * acquiring these locks are considered to be in + * critical regions; they will not be preempted by + * the _UTS_ until they release the lock. It is + * therefore safe to assume that if a lock can't + * be acquired, it is currently held by a thread + * running in another KSE. + */ + for (i = 0; i < MAX_SPINS; i++) { + if (lu->lu_watchreq->lr_locked == 0) + return; + if (lu->lu_watchreq->lr_active == 0) + break; + } + atomic_swap_int((int *)&lu->lu_watchreq->lr_locked, + 2, &lval); + if (lval == 0) + lu->lu_watchreq->lr_locked = 0; + else + lck->l_wait(lck, lu); + + } + } + lu->lu_myreq->lr_active = 1; +} + +/* + * Release a lock. + */ +void +_lock_release(struct lock *lck, struct lockuser *lu) +{ + struct lockuser *lu_tmp, *lu_h; + struct lockreq *myreq; + int prio_h; + int lval; + + /** + * XXX - We probably want to remove these checks to optimize + * performance. It is also a bug if any one of the + * checks fail, so it's probably better to just let it + * SEGV and fix it. + */ +#if 0 + if ((lck == NULL) || (lu == NULL)) + return; +#endif + if ((lck->l_type & LCK_PRIORITY) != 0) { + prio_h = 0; + lu_h = NULL; + + /* Update tail if our request is last. */ + if (lu->lu_watchreq->lr_owner == NULL) { + atomic_store_rel_ptr((volatile uintptr_t *)&lck->l_tail, + (uintptr_t)lu->lu_myreq); + atomic_store_rel_ptr + ((volatile uintptr_t *)&lu->lu_myreq->lr_owner, + (uintptr_t)NULL); + } else { + /* Remove ourselves from the list. */ + atomic_store_rel_ptr((volatile uintptr_t *) + &lu->lu_myreq->lr_owner, + (uintptr_t)lu->lu_watchreq->lr_owner); + atomic_store_rel_ptr((volatile uintptr_t *) + &lu->lu_watchreq->lr_owner->lu_myreq, + (uintptr_t)lu->lu_myreq); + } + /* + * The watch request now becomes our own because we've + * traded away our previous request. Save our previous + * request so that we can grant the lock. + */ + myreq = lu->lu_myreq; + lu->lu_myreq = lu->lu_watchreq; + lu->lu_watchreq = NULL; + lu->lu_myreq->lr_locked = 1; + lu->lu_myreq->lr_owner = lu; + lu->lu_myreq->lr_watcher = NULL; + /* + * Traverse the list of lock requests in reverse order + * looking for the user with the highest priority. + */ + for (lu_tmp = lck->l_tail->lr_watcher; lu_tmp != NULL; + lu_tmp = lu_tmp->lu_myreq->lr_watcher) { + if (lu_tmp->lu_priority > prio_h) { + lu_h = lu_tmp; + prio_h = lu_tmp->lu_priority; + } + } + if (lu_h != NULL) { + /* Give the lock to the highest priority user. */ + if (lck->l_wakeup != NULL) { + atomic_swap_int( + (int *)&lu_h->lu_watchreq->lr_locked, + 0, &lval); + if (lval == 2) + /* Notify the sleeper */ + lck->l_wakeup(lck, + lu_h->lu_myreq->lr_watcher); + } + else + atomic_store_rel_int( + &lu_h->lu_watchreq->lr_locked, 0); + } else { + if (lck->l_wakeup != NULL) { + atomic_swap_int((int *)&myreq->lr_locked, + 0, &lval); + if (lval == 2) + /* Notify the sleeper */ + lck->l_wakeup(lck, myreq->lr_watcher); + } + else + /* Give the lock to the previous request. */ + atomic_store_rel_int(&myreq->lr_locked, 0); + } + } else { + /* + * The watch request now becomes our own because we've + * traded away our previous request. Save our previous + * request so that we can grant the lock. + */ + myreq = lu->lu_myreq; + lu->lu_myreq = lu->lu_watchreq; + lu->lu_watchreq = NULL; + lu->lu_myreq->lr_locked = 1; + if (lck->l_wakeup) { + atomic_swap_int((int *)&myreq->lr_locked, 0, &lval); + if (lval == 2) + /* Notify the sleeper */ + lck->l_wakeup(lck, myreq->lr_watcher); + } + else + /* Give the lock to the previous request. */ + atomic_store_rel_int(&myreq->lr_locked, 0); + } + lu->lu_myreq->lr_active = 0; +} + +void +_lock_grant(struct lock *lck /* unused */, struct lockuser *lu) +{ + atomic_store_rel_int(&lu->lu_watchreq->lr_locked, 3); +} + +void +_lockuser_setactive(struct lockuser *lu, int active) +{ + lu->lu_myreq->lr_active = active; +} + diff --git a/lib/libpthread/sys/lock.h b/lib/libpthread/sys/lock.h new file mode 100644 index 0000000..6102a0b --- /dev/null +++ b/lib/libpthread/sys/lock.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2001, 2003 Daniel Eischen <deischen@freebsd.org>. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef _LOCK_H_ +#define _LOCK_H_ + +struct lockreq; +struct lockuser; +struct lock; + +enum lock_type { + LCK_DEFAULT = 0x0000, /* default is FIFO spin locks */ + LCK_PRIORITY = 0x0001, + LCK_ADAPTIVE = 0x0002 /* call user-supplied handlers */ +}; + +typedef void lock_handler_t(struct lock *, struct lockuser *); + +struct lock { + struct lockreq *l_head; + struct lockreq *l_tail; /* only used for priority locks */ + enum lock_type l_type; + lock_handler_t *l_wait; /* only used for adaptive locks */ + lock_handler_t *l_wakeup; /* only used for adaptive locks */ +}; + +/* Try to make this >= CACHELINESIZE */ +struct lockreq { + struct lockuser *lr_watcher; /* only used for priority locks */ + struct lockuser *lr_owner; /* only used for priority locks */ + volatile int lr_locked; /* lock granted = 0, busy otherwise */ + volatile int lr_active; /* non-zero if the lock is last lock for thread */ +}; + +struct lockuser { + struct lockreq *lu_myreq; /* request to give up/trade */ + struct lockreq *lu_watchreq; /* watch this request */ + int lu_priority; /* only used for priority locks */ + void *lu_private1; /* private{1,2} are initialized to */ + void *lu_private2; /* NULL and can be used by caller */ +#define lu_private lu_private1 +}; + +#define _LCK_INITIALIZER(lck_req) { &lck_req, NULL, LCK_DEFAULT, \ + NULL, NULL } +#define _LCK_REQUEST_INITIALIZER { 0, NULL, NULL, 0 } + +#define _LCK_BUSY(lu) ((lu)->lu_watchreq->lr_locked != 0) +#define _LCK_ACTIVE(lu) ((lu)->lu_watchreq->lr_active != 0) +#define _LCK_GRANTED(lu) ((lu)->lu_watchreq->lr_locked == 3) + +#define _LCK_SET_PRIVATE(lu, p) (lu)->lu_private = (void *)(p) +#define _LCK_GET_PRIVATE(lu) (lu)->lu_private +#define _LCK_SET_PRIVATE2(lu, p) (lu)->lu_private2 = (void *)(p) +#define _LCK_GET_PRIVATE2(lu) (lu)->lu_private2 + +void _lock_acquire(struct lock *, struct lockuser *, int); +void _lock_destroy(struct lock *); +void _lock_grant(struct lock *, struct lockuser *); +int _lock_init(struct lock *, enum lock_type, + lock_handler_t *, lock_handler_t *); +int _lock_reinit(struct lock *, enum lock_type, + lock_handler_t *, lock_handler_t *); +void _lock_release(struct lock *, struct lockuser *); +int _lockuser_init(struct lockuser *lu, void *priv); +void _lockuser_destroy(struct lockuser *lu); +int _lockuser_reinit(struct lockuser *lu, void *priv); +void _lockuser_setactive(struct lockuser *lu, int active); + +#endif diff --git a/lib/libpthread/sys/thr_error.c b/lib/libpthread/sys/thr_error.c new file mode 100644 index 0000000..0ba34e0 --- /dev/null +++ b/lib/libpthread/sys/thr_error.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * Copyright (c) 1994 by Chris Provenzano, proven@mit.edu + * 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 John Birrell + * and Chris Provenzano. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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. + * + * $FreeBSD$ + */ +#include <pthread.h> +#include "libc_private.h" +#include "thr_private.h" + +#undef errno +extern int errno; + +LT10_COMPAT_DEFAULT(__error); + +int * +__error(void) +{ + struct pthread *curthread; + + if (__isthreaded == 0) + return (&errno); + else if (_kse_in_critical()) + return &(_get_curkse()->k_error); + else { + curthread = _get_curthread(); + if ((curthread == NULL) || (curthread == _thr_initial)) + return (&errno); + else + return (&curthread->error); + } +} diff --git a/lib/libpthread/test/Makefile b/lib/libpthread/test/Makefile new file mode 100644 index 0000000..5b5eb3c --- /dev/null +++ b/lib/libpthread/test/Makefile @@ -0,0 +1,116 @@ +# +# $FreeBSD$ +# +# Automated test suite for libpthread (pthreads). +# + +# File lists. + +# Tests written in C. +CTESTS := hello_d.c hello_s.c join_leak_d.c mutex_d.c sem_d.c sigsuspend_d.c \ + sigwait_d.c + +# C programs that are used internally by the tests. The build system merely +# compiles these. +BTESTS := guard_b.c hello_b.c + +# Tests written in perl. +PTESTS := guard_s.pl propagate_s.pl + +# Munge the file lists to their final executable names (strip the .c). +CTESTS := $(CTESTS:R) +BTESTS := $(BTESTS:R) + +CPPFLAGS := -D_LIBC_R_ -D_REENTRANT +CFLAGS := -Wall -pipe -g3 +LDFLAGS_A := -static +LDFLAGS_P := -pg +LDFLAGS_S := +LIBS := -lpthread + +# Flags passed to verify. "-v" or "-u" may be useful. +VERIFY = perl verify +VFLAGS := + +all : default + +# Only use the following suffixes, in order to avoid any strange built-in rules. +.SUFFIXES : +.SUFFIXES : .c .o .d .pl + +# Clear out all paths, then set just one (default path) for the main build +# directory. +.PATH : +.PATH : . + +# Build the C programs. +.for bin in $(CTESTS) $(BTESTS) +$(bin)_a : $(bin:S/$/&.c/) + $(CC) $(CFLAGS) $(CPPFLAGS) -c $(bin:S/$/&.c/) -o $(@:S/$/&.o/) + $(CC) -o $@ $(@:S/$/&.o/) $(LDFLAGS_A) $(LIBS) + @$(SHELL) -ec "$(CC) -M $(CPPFLAGS) $(bin:S/$/&.c/) | sed \"s/\($(bin:T)\)\.o\([ :]*\)/$(bin:H:S!/!\\/!g)\/\1_a.o \2/g\" > $(@:R:S/$/&.d/)" + +$(bin)_p : $(bin:S/$/&.c/) + $(CC) $(CFLAGS) $(CPPFLAGS) -c $(bin:S/$/&.c/) -o $(@:S/$/&.o/) + $(CC) -o $@ $(@:S/$/&.o/) $(LDFLAGS_P) $(LIBS) + @$(SHELL) -ec "$(CC) -M $(CPPFLAGS) $(bin:S/$/&.c/) | sed \"s/\($(bin:T)\)\.o\([ :]*\)/$(bin:H:S!/!\\/!g)\/\1_p.o \2/g\" > $(@:R:S/$/&.d/)" + +$(bin)_s : $(bin:S/$/&.c/) + $(CC) $(CFLAGS) $(CPPFLAGS) -c $(bin:S/$/&.c/) -o $(@:S/$/&.o/) + $(CC) -o $@ $(@:S/$/&.o/) $(LDFLAGS_S) $(LIBS) + @$(SHELL) -ec "$(CC) -M $(CPPFLAGS) $(bin:S/$/&.c/) | sed \"s/\($(bin:T)\)\.o\([ :]*\)/$(bin:H:S!/!\\/!g)\/\1_s.o \2/g\" > $(@:R:S/$/&.d/)" +.endfor + +# Dependency file inclusion. +.for depfile in $(CTESTS:R:S/$/&_a.d/) $(BTESTS:R:S/$/&_a.d/) \ + $(CTESTS:R:S/$/&_p.d/) $(BTESTS:R:S/$/&_p.d/) \ + $(CTESTS:R:S/$/&_s.d/) $(BTESTS:R:S/$/&_s.d/) +.if exists($(depfile)) +.include "$(depfile)" +.endif +.endfor + +default : check + +tests_a : $(CTESTS:S/$/&_a/) $(BTESTS:S/$/&_a/) +tests_p : $(CTESTS:S/$/&_p/) $(BTESTS:S/$/&_p/) +tests_s : $(CTESTS:S/$/&_s/) $(BTESTS:S/$/&_s/) + +tests : tests_a tests_p tests_s + +check_a : tests_a +.for bin in $(CTESTS) $(BTESTS) + @cp $(bin)_a $(bin) +.endfor + @echo "Test static library:" + @$(VERIFY) $(VFLAGS) $(CTESTS) $(PTESTS) + +check_p : tests_p +.for bin in $(CTESTS) $(BTESTS) + @cp $(bin)_p $(bin) +.endfor + @echo "Test profile library:" + @$(VERIFY) $(VFLAGS) $(CTESTS) $(PTESTS) + +check_s : tests_s +.for bin in $(CTESTS) $(BTESTS) + @cp $(bin)_s $(bin) +.endfor + @echo "Test shared library:" + @$(VERIFY) $(VFLAGS) $(CTESTS) $(PTESTS) + +check : check_a check_p check_s + +clean : + rm -f *~ + rm -f *.core + rm -f *.out + rm -f *.perf + rm -f *.diff + rm -f *.gmon + rm -f $(CTESTS) $(BTESTS) + rm -f $(CTESTS:S/$/&_a/) $(BTESTS:S/$/&_a/) + rm -f $(CTESTS:S/$/&_p/) $(BTESTS:S/$/&_p/) + rm -f $(CTESTS:S/$/&_s/) $(BTESTS:S/$/&_s/) + rm -f *.d + rm -f *.o diff --git a/lib/libpthread/test/README b/lib/libpthread/test/README new file mode 100644 index 0000000..8f625a1 --- /dev/null +++ b/lib/libpthread/test/README @@ -0,0 +1,28 @@ +$FreeBSD$ + +This test suite is meant to test general functionality of pthreads, as well as +provide a simple framework for regression tests. In general, this test suite +can be used with any pthreads library, but in reality there are a number of +libpthread-specific aspects to this test suite which would require some +effort to get around if testing another pthreads library. + +This test suite assumes that libpthread is installed. + +There are two forms of test that the 'verify' script understands. The simpler +form is the diff format, where the output of the test program is diff'ed with +the correspondingly named .exp file. If there is diff output, the test fails. +The sequence test format is somewhat more complex, and is documented in the +command line usage output for verify. The advantage of this format is that it +allows multiple tests to pass/fail within one program. + +There is no driving need for test naming consistency, but the existing tests +generally follow these conventions: + +<name>_d.c <name>_d.exp : Diff mode C test and expected output file. +<name>_s.c : Sequence mode C test. +<name>_b*.c : Back end C program used by perl tests. +<name>_d.pl <name>_d.pl.exp : Diff mode perl test and expected output file. +<name>_s.pl : Sequence mode perl test. + +<name> is something descriptive, such as "pr14685" in the case of a PR-related +regression test, or "mutex" in the case of a test of mutexes. diff --git a/lib/libpthread/test/guard_b.c b/lib/libpthread/test/guard_b.c new file mode 100644 index 0000000..2e27031 --- /dev/null +++ b/lib/libpthread/test/guard_b.c @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2001 Jason Evans <jasone@freebsd.org>. + * 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(s), this list of conditions and the following disclaimer + * unmodified other than the allowable addition of one or more + * copyright notices. + * 2. Redistributions in binary form must reproduce the above copyright + * notice(s), this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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. + * + * $FreeBSD$ + * + * Test thread stack guard functionality. + */ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <limits.h> +#include <pthread.h> + +#define FRAME_SIZE 1024 +#define FRAME_OVERHEAD 40 + +struct args +{ + void *top; /* Top of thread's initial stack frame. */ + int cur; /* Recursion depth. */ + int max; /* Maximum recursion depth. */ +}; + +void * +recurse(void *args) +{ + int top; + struct args *parms = (struct args *)args; + char filler[FRAME_SIZE - FRAME_OVERHEAD]; + + /* Touch the memory in this stack frame. */ + top = 0xa5; + memset(filler, 0xa5, sizeof(filler)); + + if (parms->top == NULL) { + /* Initial stack frame. */ + parms->top = (void*)⊤ + } + + /* + * Make sure frame size is what we expect. Getting this right involves + * hand tweaking, so just print a warning rather than aborting. + */ + if (parms->top - (void *)&top != FRAME_SIZE * parms->cur) { + fprintf(stderr, + "Stack size (%ld) != expected (%ld), frame %ld\n", + (long)parms->top - (long)&top, + (long)(FRAME_SIZE * parms->cur), (long)parms->cur); + } + + parms->cur++; + if (parms->cur < parms->max) + recurse(args); + + return NULL; +} + + +int +main(int argc, char **argv) +{ + size_t def_stacksize, def_guardsize; + size_t stacksize, guardsize; + pthread_t thread; + pthread_attr_t attr; + struct args args; + + if (argc != 3) { + fprintf(stderr, "usage: guard_b <stacksize> <guardsize>\n"); + exit(1); + } + fprintf(stderr, "Test begin\n"); + + stacksize = strtoul(argv[1], NULL, 10); + guardsize = strtoul(argv[2], NULL, 10); + + assert(pthread_attr_init(&attr) == 0); + /* + * Exercise the attribute APIs more thoroughly than is strictly + * necessary for the meat of this test program. + */ + assert(pthread_attr_getstacksize(&attr, &def_stacksize) == 0); + assert(pthread_attr_getguardsize(&attr, &def_guardsize) == 0); + if (def_stacksize != stacksize) { + assert(pthread_attr_setstacksize(&attr, stacksize) == 0); + assert(pthread_attr_getstacksize(&attr, &def_stacksize) == 0); + assert(def_stacksize == stacksize); + } + if (def_guardsize != guardsize) { + assert(pthread_attr_setguardsize(&attr, guardsize) == 0); + assert(pthread_attr_getguardsize(&attr, &def_guardsize) == 0); + assert(def_guardsize >= guardsize); + } + + /* + * Create a thread that will come just short of overflowing the thread + * stack. We need to leave a bit of breathing room in case the thread + * is context switched, and we also have to take care not to call any + * functions in the deepest stack frame. + */ + args.top = NULL; + args.cur = 0; + args.max = (stacksize / FRAME_SIZE) - 1; + fprintf(stderr, "No overflow:\n"); + assert(pthread_create(&thread, &attr, recurse, &args) == 0); + assert(pthread_join(thread, NULL) == 0); + + /* + * Create a thread that will barely of overflow the thread stack. This + * should cause a segfault. + */ + args.top = NULL; + args.cur = 0; + args.max = (stacksize / FRAME_SIZE) + 1; + fprintf(stderr, "Overflow:\n"); + assert(pthread_create(&thread, &attr, recurse, &args) == 0); + assert(pthread_join(thread, NULL) == 0); + + /* Not reached. */ + fprintf(stderr, "Unexpected success\n"); + abort(); + + return 0; +} diff --git a/lib/libpthread/test/guard_b.exp b/lib/libpthread/test/guard_b.exp new file mode 100644 index 0000000..8e5b9e4 --- /dev/null +++ b/lib/libpthread/test/guard_b.exp @@ -0,0 +1,3 @@ +Test begin +No overflow: +Overflow: diff --git a/lib/libpthread/test/guard_s.pl b/lib/libpthread/test/guard_s.pl new file mode 100755 index 0000000..7802ff3 --- /dev/null +++ b/lib/libpthread/test/guard_s.pl @@ -0,0 +1,69 @@ +#!/usr/bin/perl -w +# +# Copyright (C) 2001 Jason Evans <jasone@freebsd.org>. +# 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(s), this list of conditions and the following disclaimer +# unmodified other than the allowable addition of one or more +# copyright notices. +# 2. Redistributions in binary form must reproduce the above copyright +# notice(s), this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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. +# +# $FreeBSD$ +# +# Test thread stack guard functionality. The C test program needs to be driven +# by this script because it segfaults when the stack guard is hit. +# + +print "1..30\n"; + +$i = 0; +# Iterates 10 times. +for ($stacksize = 65536; $stacksize < 131072; $stacksize += 7168) +{ + # Iterates 3 times (1024, 4096, 7168). + for ($guardsize = 1024; $guardsize < 8192; $guardsize += 3072) + { + $i++; + + print "stacksize: $stacksize, guardsize: $guardsize\n"; + + `./guard_b $stacksize $guardsize >guard_b.out 2>&1`; + + if (! -f "./guard_b.out") + { + print "not ok $i\n"; + } + else + { + `diff guard_b.exp guard_b.out >guard_b.diff 2>&1`; + if ($?) + { + # diff returns non-zero if there is a difference. + print "not ok $i\n"; + } + else + { + print "ok $i\n"; + } + } + } +} diff --git a/lib/libpthread/test/hello_b.c b/lib/libpthread/test/hello_b.c new file mode 100644 index 0000000..2eefa7f --- /dev/null +++ b/lib/libpthread/test/hello_b.c @@ -0,0 +1,13 @@ +/**************************************************************************** + * + * Back end C programs can be anything compilable. + * + * $FreeBSD$ + * + ****************************************************************************/ + +int +main() +{ + return 0; +} diff --git a/lib/libpthread/test/hello_d.c b/lib/libpthread/test/hello_d.c new file mode 100644 index 0000000..6d77526 --- /dev/null +++ b/lib/libpthread/test/hello_d.c @@ -0,0 +1,38 @@ +/**************************************************************************** + * + * Simple diff mode test. + * + * $FreeBSD$ + * + ****************************************************************************/ + +#include <stdio.h> +#include <string.h> +#include <pthread.h> + +void * +entry(void * a_arg) +{ + fprintf(stderr, "Hello world\n"); + + return NULL; +} + +int +main() +{ + pthread_t thread; + int error; + + error = pthread_create(&thread, NULL, entry, NULL); + if (error) + fprintf(stderr, "Error in pthread_create(): %s\n", + strerror(error)); + + error = pthread_join(thread, NULL); + if (error) + fprintf(stderr, "Error in pthread_join(): %s\n", + strerror(error)); + + return 0; +} diff --git a/lib/libpthread/test/hello_d.exp b/lib/libpthread/test/hello_d.exp new file mode 100644 index 0000000..802992c --- /dev/null +++ b/lib/libpthread/test/hello_d.exp @@ -0,0 +1 @@ +Hello world diff --git a/lib/libpthread/test/hello_s.c b/lib/libpthread/test/hello_s.c new file mode 100644 index 0000000..942bf2d --- /dev/null +++ b/lib/libpthread/test/hello_s.c @@ -0,0 +1,47 @@ +/**************************************************************************** + * + * Simple sequence mode test. + * + * $FreeBSD$ + * + ****************************************************************************/ + +#include <stdio.h> +#include <string.h> +#include <pthread.h> + +void * +entry(void * a_arg) +{ + fprintf(stderr, "ok 1\n"); + fprintf(stderr, "ok \n"); + fprintf(stderr, "ok 3\n"); + + return NULL; +} + +int +main() +{ + pthread_t thread; + int error; + + fprintf(stderr, "1..3\n"); + + fprintf(stderr, "Some random text\n"); + + error = pthread_create(&thread, NULL, entry, NULL); + fprintf(stderr, "More unimportant text\n"); + if (error) + fprintf(stderr,"Error in pthread_create(): %s\n", + strerror(error)); + + error = pthread_join(thread, NULL); + if (error) + fprintf(stderr, "Error in pthread_join(): %s\n", + strerror(error)); + + fprintf(stderr, "Hello world\n"); + + return 0; +} diff --git a/lib/libpthread/test/join_leak_d.c b/lib/libpthread/test/join_leak_d.c new file mode 100644 index 0000000..6532ca5 --- /dev/null +++ b/lib/libpthread/test/join_leak_d.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2001 Jason Evans <jasone@freebsd.org>. + * 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(s), this list of conditions and the following disclaimer as + * the first lines of this file unmodified other than the possible + * addition of one or more copyright notices. + * 2. Redistributions in binary form must reproduce the above copyright + * notice(s), this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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. + * + * $FreeBSD$ + * + * Test for leaked joined threads. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <errno.h> +#include <string.h> +#include <pthread.h> + +#define NITERATIONS 16384 +#define MAXGROWTH 16384 + +void * +thread_entry(void *a_arg) +{ + return NULL; +} + +int +main(void) +{ + pthread_t thread; + int i, error; + char *brk, *nbrk; + unsigned growth; + + fprintf(stderr, "Test begin\n"); + + /* Get an initial brk value. */ + brk = sbrk(0); + + /* Create threads and join them, one at a time. */ + for (i = 0; i < NITERATIONS; i++) { + if ((error = pthread_create(&thread, NULL, thread_entry, NULL)) + != 0) { + fprintf(stderr, "Error in pthread_create(): %s\n", + strerror(error)); + exit(1); + } + if ((error = pthread_join(thread, NULL)) != 0) { + fprintf(stderr, "Error in pthread_join(): %s\n", + strerror(error)); + exit(1); + } + } + + /* Get a final brk value. */ + nbrk = sbrk(0); + + /* + * Check that the amount of heap space allocated is below an acceptable + * threshold. We could just compare brk and nbrk, but the test could + * conceivably break if the internals of the threads library changes. + */ + if (nbrk > brk) { + /* Heap grows up. */ + growth = nbrk - brk; + } else if (nbrk <= brk) { + /* Heap grows down, or no growth. */ + growth = brk - nbrk; + } + + if (growth > MAXGROWTH) { + fprintf(stderr, "Heap growth exceeded maximum (%u > %u)\n", + growth, MAXGROWTH); + } +#if (0) + else { + fprintf(stderr, "Heap growth acceptable (%u <= %u)\n", + growth, MAXGROWTH); + } +#endif + + fprintf(stderr, "Test end\n"); + return 0; +} diff --git a/lib/libpthread/test/join_leak_d.exp b/lib/libpthread/test/join_leak_d.exp new file mode 100644 index 0000000..369a88d --- /dev/null +++ b/lib/libpthread/test/join_leak_d.exp @@ -0,0 +1,2 @@ +Test begin +Test end diff --git a/lib/libpthread/test/mutex_d.c b/lib/libpthread/test/mutex_d.c new file mode 100644 index 0000000..2aa3b1d --- /dev/null +++ b/lib/libpthread/test/mutex_d.c @@ -0,0 +1,1558 @@ +/* + * Copyright (c) 1998 Daniel M. Eischen <eischen@vigrid.com> + * 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 Daniel M. Eischen. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL M. EISCHEN 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include <sys/time.h> +#include <sys/ioctl.h> +#include <stdlib.h> +#include <unistd.h> +#include <assert.h> +#include <errno.h> +#include <sched.h> +#include <signal.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <sysexits.h> +#include "pthread.h" + +#if defined(_LIBC_R_) +#include <pthread_np.h> +#endif + +#ifndef NELEMENTS +#define NELEMENTS(arr) (sizeof (arr) / sizeof (arr[0])) +#endif + +#ifndef NUM_THREADS +#define NUM_THREADS 10 +#endif + +#define MAX_THREAD_CMDS 10 + +static void log_error(const char *, ...) __printflike(1, 2); +static void log_trace (const char *, ...) __printflike(1, 2); +static void log (const char *, ...) __printflike(1, 2); + +/*------------------------------------------------------------ + * Types + *----------------------------------------------------------*/ + +typedef enum { + STAT_INITIAL, /* initial state */ + STAT_WAITCONDVAR, /* waiting for condition variable signal */ + STAT_WAITMUTEX /* waiting for mutex lock */ +} thread_status_t; + +typedef enum { + FLAGS_REPORT_WAITCONDMUTEX = 0x01, + FLAGS_REPORT_WAITCONDVAR = 0x02, + FLAGS_REPORT_WAITMUTEX = 0x04, + FLAGS_REPORT_BUSY_LOOP = 0x08, + FLAGS_IS_BUSY = 0x10, + FLAGS_WAS_BUSY = 0x20 +} thread_flags_t; + +typedef enum { + CMD_NONE, + CMD_TAKE_MUTEX, + CMD_RELEASE_MUTEX, + CMD_WAIT_FOR_SIGNAL, + CMD_BUSY_LOOP, + CMD_PROTECTED_OP, + CMD_RELEASE_ALL +} thread_cmd_id_t; + +typedef struct { + thread_cmd_id_t cmd_id; + pthread_mutex_t *mutex; + pthread_cond_t *cond; +} thread_cmd_t; + +typedef struct { + pthread_cond_t cond_var; + thread_status_t status; + thread_cmd_t cmd; + int flags; + int priority; + int ret; + pthread_t tid; + u_int8_t id; +} thread_state_t; + +typedef enum { + M_POSIX, + M_SS2_DEFAULT, + M_SS2_ERRORCHECK, + M_SS2_NORMAL, + M_SS2_RECURSIVE +} mutex_kind_t; + + +/*------------------------------------------------------------ + * Constants + *----------------------------------------------------------*/ + +const char *protocol_strs[] = { + "PTHREAD_PRIO_NONE", + "PTHREAD_PRIO_INHERIT", + "PTHREAD_PRIO_PROTECT" +}; + +const int protocols[] = { + PTHREAD_PRIO_NONE, + PTHREAD_PRIO_INHERIT, + PTHREAD_PRIO_PROTECT +}; + +const char *mutextype_strs[] = { + "POSIX (type not specified)", + "SS2 PTHREAD_MUTEX_DEFAULT", + "SS2 PTHREAD_MUTEX_ERRORCHECK", + "SS2 PTHREAD_MUTEX_NORMAL", + "SS2 PTHREAD_MUTEX_RECURSIVE" +}; + +const int mutex_types[] = { + 0, /* M_POSIX */ + PTHREAD_MUTEX_DEFAULT, /* M_SS2_DEFAULT */ + PTHREAD_MUTEX_ERRORCHECK, /* M_SS2_ERRORCHECK */ + PTHREAD_MUTEX_NORMAL, /* M_SS2_NORMAL */ + PTHREAD_MUTEX_RECURSIVE /* M_SS2_RECURSIVE */ +}; + + +/*------------------------------------------------------------ + * Objects + *----------------------------------------------------------*/ + +static int done = 0; +static int trace_enabled = 0; +static int use_global_condvar = 0; +static thread_state_t states[NUM_THREADS]; +static int pipefd[2]; + +static pthread_mutex_t waiter_mutex; +static pthread_mutex_t cond_mutex; +static pthread_cond_t cond_var; + +static FILE *logfile; +static int error_count = 0, pass_count = 0, total = 0; + + +/*------------------------------------------------------------ + * Prototypes + *----------------------------------------------------------*/ +extern char *strtok_r(char *str, const char *sep, char **last); + + +/*------------------------------------------------------------ + * Functions + *----------------------------------------------------------*/ + +#if defined(_LIBC_R_) && defined(DEBUG) +static void +kern_switch (pthread_t pthread_out, pthread_t pthread_in) +{ + if (pthread_out != NULL) + printf ("Swapping out thread 0x%lx, ", (long) pthread_out); + else + printf ("Swapping out kernel thread, "); + + if (pthread_in != NULL) + printf ("swapping in thread 0x%lx\n", (long) pthread_in); + else + printf ("swapping in kernel thread.\n"); +} +#endif + + +static void +log_error (const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + fprintf (logfile, "FAIL: "); + vfprintf (logfile, fmt, ap); + error_count = error_count + 1; + total = total + 1; +} + + +static void +log_pass (void) +{ + fprintf (logfile, "PASS\n"); + pass_count = pass_count + 1; + total = total + 1; +} + + +static void +log_trace (const char *fmt, ...) +{ + va_list ap; + + if (trace_enabled) { + va_start (ap, fmt); + vfprintf (logfile, fmt, ap); + } +} + + +static void +log (const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + vfprintf (logfile, fmt, ap); +} + + +static void +check_result (int expected, int actual) +{ + if (expected != actual) + log_error ("expected %d, returned %d\n", expected, actual); + else + log_pass (); +} + + +/* + * Check to see that the threads ran in the specified order. + */ +static void +check_run_order (char *order) +{ + const char *sep = ":,"; + char *tok, *last, *idstr, *endptr; + int expected_id, bytes, count = 0, errors = 0; + u_int8_t id; + + assert ((tok = (char *) malloc (strlen(order) + 1)) != NULL); + strcpy (tok, order); /* tok has to be larger than order */ + assert (ioctl (pipefd[0], FIONREAD, &bytes) == 0); + log_trace ("%d bytes read from FIFO.\n", bytes); + + for (idstr = strtok_r (tok, sep, &last); + (idstr != NULL) && (count < bytes); + idstr = strtok_r (NULL, sep, &last)) { + + /* Get the expected id: */ + expected_id = (int) strtol (idstr, &endptr, 10); + assert ((endptr != NULL) && (*endptr == '\0')); + + /* Read the actual id from the pipe: */ + assert (read (pipefd[0], &id, sizeof (id)) == sizeof (id)); + count = count + sizeof (id); + + if (id != expected_id) { + log_trace ("Thread %d ran out of order.\n", id); + errors = errors + 1; + } + else { + log_trace ("Thread %d at priority %d reporting.\n", + (int) id, states[id].priority); + } + } + + if (count < bytes) { + /* Clear the pipe: */ + while (count < bytes) { + read (pipefd[0], &id, sizeof (id)); + count = count + 1; + errors = errors + 1; + } + } + else if (bytes < count) + errors = errors + count - bytes; + + if (errors == 0) + log_pass (); + else + log_error ("%d threads ran out of order", errors); +} + + +static void * +waiter (void *arg) +{ + thread_state_t *statep = (thread_state_t *) arg; + pthread_mutex_t *held_mutex[MAX_THREAD_CMDS]; + int held_mutex_owned[MAX_THREAD_CMDS]; + sigset_t mask; + struct timeval tv1, tv2; + thread_cmd_t cmd; + int i, mutex_count = 0; + + statep->status = STAT_INITIAL; + + /* Block all signals except for interrupt.*/ + sigfillset (&mask); + sigdelset (&mask, SIGINT); + sigprocmask (SIG_BLOCK, &mask, NULL); + + while (done == 0) { + /* Wait for signal from the main thread to continue. */ + statep->status = STAT_WAITMUTEX; + log_trace ("Thread %d: locking cond_mutex.\n", + (int) statep->id); + pthread_mutex_lock (&cond_mutex); + + /* Do we report our status. */ + if (statep->flags & FLAGS_REPORT_WAITCONDMUTEX) + write (pipefd[1], &statep->id, sizeof (statep->id)); + log_trace ("Thread %d: waiting for cond_var.\n", + (int) statep->id); + + /* Wait for a command. */ + statep->status = STAT_WAITCONDVAR; + + /* + * The threads are allowed commanded to wait either on + * their own unique condition variable (so they may be + * separately signaled) or on one global condition variable + * (so they may be signaled together). + */ + if (use_global_condvar != 0) + pthread_cond_wait (&cond_var, &cond_mutex); + else + pthread_cond_wait (&statep->cond_var, &cond_mutex); + + /* Do we report our status? */ + if (statep->flags & FLAGS_REPORT_WAITCONDVAR) { + write (pipefd[1], &statep->id, sizeof (statep->id)); + log_trace ("Thread %d: wrote to pipe.\n", + (int) statep->id); + } + log_trace ("Thread %d: received cond_var signal.\n", + (int) statep->id); + + /* Get a copy of the command before releasing the mutex. */ + cmd = statep->cmd; + + /* Clear the command after copying it. */ + statep->cmd.cmd_id = CMD_NONE; + + /* Unlock the condition variable mutex. */ + assert (pthread_mutex_unlock (&cond_mutex) == 0); + + /* Peform the command.*/ + switch (cmd.cmd_id) { + case CMD_TAKE_MUTEX: + statep->ret = pthread_mutex_lock (cmd.mutex); + if (statep->ret == 0) { + assert (mutex_count < sizeof (held_mutex)); + held_mutex[mutex_count] = cmd.mutex; + held_mutex_owned[mutex_count] = 1; + mutex_count++; + } + else { + held_mutex_owned[mutex_count] = 0; + log_trace ("Thread id %d unable to lock mutex, " + "error = %d\n", (int) statep->id, + statep->ret); + } + break; + + case CMD_RELEASE_MUTEX: + assert ((mutex_count <= sizeof (held_mutex)) && + (mutex_count > 0)); + mutex_count--; + if (held_mutex_owned[mutex_count] != 0) + assert (pthread_mutex_unlock + (held_mutex[mutex_count]) == 0); + break; + + case CMD_WAIT_FOR_SIGNAL: + assert (pthread_mutex_lock (cmd.mutex) == 0); + assert (pthread_cond_wait (cmd.cond, cmd.mutex) == 0); + assert (pthread_mutex_unlock (cmd.mutex) == 0); + break; + + case CMD_BUSY_LOOP: + log_trace ("Thread %d: Entering busy loop.\n", + (int) statep->id); + /* Spin for 15 seconds. */ + assert (gettimeofday (&tv2, NULL) == 0); + tv1.tv_sec = tv2.tv_sec + 5; + tv1.tv_usec = tv2.tv_usec; + statep->flags |= FLAGS_IS_BUSY; + while (timercmp (&tv2, &tv1,<)) { + assert (gettimeofday (&tv2, NULL) == 0); + } + statep->flags &= ~FLAGS_IS_BUSY; + statep->flags |= FLAGS_WAS_BUSY; + + /* Do we report our status? */ + if (statep->flags & FLAGS_REPORT_BUSY_LOOP) + write (pipefd[1], &statep->id, + sizeof (statep->id)); + + log_trace ("Thread %d: Leaving busy loop.\n", + (int) statep->id); + break; + + case CMD_PROTECTED_OP: + assert (pthread_mutex_lock (cmd.mutex) == 0); + statep->flags |= FLAGS_WAS_BUSY; + /* Do we report our status? */ + if (statep->flags & FLAGS_REPORT_BUSY_LOOP) + write (pipefd[1], &statep->id, + sizeof (statep->id)); + + assert (pthread_mutex_unlock (cmd.mutex) == 0); + break; + + case CMD_RELEASE_ALL: + assert ((mutex_count <= sizeof (held_mutex)) && + (mutex_count > 0)); + for (i = mutex_count - 1; i >= 0; i--) { + if (held_mutex_owned[i] != 0) + assert (pthread_mutex_unlock + (held_mutex[i]) == 0); + } + mutex_count = 0; + break; + + case CMD_NONE: + default: + break; + } + + /* Wait for the big giant waiter lock. */ + statep->status = STAT_WAITMUTEX; + log_trace ("Thread %d: waiting for big giant lock.\n", + (int) statep->id); + pthread_mutex_lock (&waiter_mutex); + if (statep->flags & FLAGS_REPORT_WAITMUTEX) + write (pipefd[1], &statep->id, sizeof (statep->id)); + log_trace ("Thread %d: got big giant lock.\n", + (int) statep->id); + statep->status = STAT_INITIAL; + pthread_mutex_unlock (&waiter_mutex); + } + + log_trace ("Thread %ld: Exiting thread 0x%lx\n", (long) statep->id, + (long) pthread_self()); + pthread_exit (arg); + return (NULL); +} + + +static void * +lock_twice (void *arg) +{ + thread_state_t *statep = (thread_state_t *) arg; + sigset_t mask; + + statep->status = STAT_INITIAL; + + /* Block all signals except for interrupt.*/ + sigfillset (&mask); + sigdelset (&mask, SIGINT); + sigprocmask (SIG_BLOCK, &mask, NULL); + + /* Wait for a signal to continue. */ + log_trace ("Thread %d: locking cond_mutex.\n", (int) statep->id); + pthread_mutex_lock (&cond_mutex); + + log_trace ("Thread %d: waiting for cond_var.\n", (int) statep->id); + statep->status = STAT_WAITCONDVAR; + pthread_cond_wait (&cond_var, &cond_mutex); + + log_trace ("Thread %d: received cond_var signal.\n", (int) statep->id); + + /* Unlock the condition variable mutex. */ + assert (pthread_mutex_unlock (&cond_mutex) == 0); + + statep->status = STAT_WAITMUTEX; + /* Lock the mutex once. */ + assert (pthread_mutex_lock (statep->cmd.mutex) == 0); + + /* Lock it again and capture the error. */ + statep->ret = pthread_mutex_lock (statep->cmd.mutex); + statep->status = 0; + + assert (pthread_mutex_unlock (statep->cmd.mutex) == 0); + + /* Unlock it again if it is locked recursively. */ + if (statep->ret == 0) + pthread_mutex_unlock (statep->cmd.mutex); + + log_trace ("Thread %ld: Exiting thread 0x%lx\n", (long) statep->id, + (long) pthread_self()); + pthread_exit (arg); + return (NULL); +} + + +static void +sighandler (int signo) +{ + log ("Signal handler caught signal %d, thread id 0x%lx\n", + signo, (long) pthread_self()); + + if (signo == SIGINT) + done = 1; +} + + +static void +send_cmd (int id, thread_cmd_id_t cmd) +{ + assert (pthread_mutex_lock (&cond_mutex) == 0); + assert (states[id].status == STAT_WAITCONDVAR); + states[id].cmd.cmd_id = cmd; + states[id].cmd.mutex = NULL; + states[id].cmd.cond = NULL; + /* Clear the busy flags. */ + states[id].flags &= ~(FLAGS_WAS_BUSY | FLAGS_IS_BUSY); + assert (pthread_cond_signal (&states[id].cond_var) == 0); + assert (pthread_mutex_unlock (&cond_mutex) == 0); +} + + +static void +send_mutex_cmd (int id, thread_cmd_id_t cmd, pthread_mutex_t *m) +{ + assert (pthread_mutex_lock (&cond_mutex) == 0); + assert (states[id].status == STAT_WAITCONDVAR); + states[id].cmd.cmd_id = cmd; + states[id].cmd.mutex = m; + states[id].cmd.cond = NULL; + /* Clear the busy flags. */ + states[id].flags &= ~(FLAGS_WAS_BUSY | FLAGS_IS_BUSY); + assert (pthread_cond_signal (&states[id].cond_var) == 0); + assert (pthread_mutex_unlock (&cond_mutex) == 0); +} + + +static void +send_mutex_cv_cmd (int id, thread_cmd_id_t cmd, pthread_mutex_t *m, + pthread_cond_t *cv) +{ + assert (pthread_mutex_lock (&cond_mutex) == 0); + assert (states[id].status == STAT_WAITCONDVAR); + states[id].cmd.cmd_id = cmd; + states[id].cmd.mutex = m; + states[id].cmd.cond = cv; + /* Clear the busy flags. */ + states[id].flags &= ~(FLAGS_WAS_BUSY | FLAGS_IS_BUSY); + assert (pthread_cond_signal (&states[id].cond_var) == 0); + assert (pthread_mutex_unlock (&cond_mutex) == 0); +} + + +static void +mutex_init_test (void) +{ + pthread_mutexattr_t mattr; + pthread_mutex_t mutex; + mutex_kind_t mkind; + int mproto, ret; + + /* + * Initialize a mutex attribute. + * + * pthread_mutexattr_init not tested for: ENOMEM + */ + assert (pthread_mutexattr_init (&mattr) == 0); + + /* + * Initialize a mutex. + * + * pthread_mutex_init not tested for: EAGAIN ENOMEM EPERM EBUSY + */ + log ("Testing pthread_mutex_init\n"); + log ("--------------------------\n"); + + for (mproto = 0; mproto < NELEMENTS(protocols); mproto++) { + for (mkind = M_POSIX; mkind <= M_SS2_RECURSIVE; mkind++) { + /* Initialize the mutex attribute. */ + assert (pthread_mutexattr_init (&mattr) == 0); + assert (pthread_mutexattr_setprotocol (&mattr, + protocols[mproto]) == 0); + + /* + * Ensure that the first mutex type is a POSIX + * compliant mutex. + */ + if (mkind != M_POSIX) { + assert (pthread_mutexattr_settype (&mattr, + mutex_types[mkind]) == 0); + } + + log (" Protocol %s, Type %s - ", + protocol_strs[mproto], mutextype_strs[mkind]); + ret = pthread_mutex_init (&mutex, &mattr); + check_result (/* expected */ 0, ret); + assert (pthread_mutex_destroy (&mutex) == 0); + + /* + * Destroy a mutex attribute. + * + * XXX - There should probably be a magic number + * associated with a mutex attribute so that + * destroy can be reasonably sure the attribute + * is valid. + * + * pthread_mutexattr_destroy not tested for: EINVAL + */ + assert (pthread_mutexattr_destroy (&mattr) == 0); + } + } +} + + +static void +mutex_destroy_test (void) +{ + pthread_mutexattr_t mattr; + pthread_mutex_t mutex; + pthread_condattr_t cattr; + pthread_cond_t cv; + pthread_attr_t pattr; + int mproto, ret; + mutex_kind_t mkind; + thread_state_t state; + + /* + * Destroy a mutex. + * + * XXX - There should probably be a magic number associated + * with a mutex so that destroy can be reasonably sure + * the mutex is valid. + * + * pthread_mutex_destroy not tested for: + */ + log ("Testing pthread_mutex_destroy\n"); + log ("-----------------------------\n"); + + assert (pthread_attr_init (&pattr) == 0); + assert (pthread_attr_setdetachstate (&pattr, + PTHREAD_CREATE_DETACHED) == 0); + state.flags = 0; /* No flags yet. */ + + for (mproto = 0; mproto < NELEMENTS(protocols); mproto++) { + for (mkind = M_POSIX; mkind <= M_SS2_RECURSIVE; mkind++) { + /* Initialize the mutex attribute. */ + assert (pthread_mutexattr_init (&mattr) == 0); + assert (pthread_mutexattr_setprotocol (&mattr, + protocols[mproto]) == 0); + + /* + * Ensure that the first mutex type is a POSIX + * compliant mutex. + */ + if (mkind != M_POSIX) { + assert (pthread_mutexattr_settype (&mattr, + mutex_types[mkind]) == 0); + } + + /* Create the mutex. */ + assert (pthread_mutex_init (&mutex, &mattr) == 0); + + log (" Protocol %s, Type %s\n", + protocol_strs[mproto], mutextype_strs[mkind]); + + log (" Destruction of unused mutex - "); + assert (pthread_mutex_init (&mutex, &mattr) == 0); + ret = pthread_mutex_destroy (&mutex); + check_result (/* expected */ 0, ret); + + log (" Destruction of mutex locked by self - "); + assert (pthread_mutex_init (&mutex, &mattr) == 0); + assert (pthread_mutex_lock (&mutex) == 0); + ret = pthread_mutex_destroy (&mutex); + check_result (/* expected */ EBUSY, ret); + assert (pthread_mutex_unlock (&mutex) == 0); + assert (pthread_mutex_destroy (&mutex) == 0); + + log (" Destruction of mutex locked by another " + "thread - "); + assert (pthread_mutex_init (&mutex, &mattr) == 0); + send_mutex_cmd (0, CMD_TAKE_MUTEX, &mutex); + sleep (1); + ret = pthread_mutex_destroy (&mutex); + check_result (/* expected */ EBUSY, ret); + send_cmd (0, CMD_RELEASE_ALL); + sleep (1); + assert (pthread_mutex_destroy (&mutex) == 0); + + log (" Destruction of mutex while being used in " + "cond_wait - "); + assert (pthread_mutex_init (&mutex, &mattr) == 0); + assert (pthread_condattr_init (&cattr) == 0); + assert (pthread_cond_init (&cv, &cattr) == 0); + send_mutex_cv_cmd (0, CMD_WAIT_FOR_SIGNAL, &mutex, &cv); + sleep (1); + ret = pthread_mutex_destroy (&mutex); + check_result (/* expected */ EBUSY, ret); + pthread_cond_signal (&cv); + sleep (1); + assert (pthread_mutex_destroy (&mutex) == 0); + } + } +} + + +static void +mutex_lock_test (void) +{ + pthread_mutexattr_t mattr; + pthread_mutex_t mutex; + pthread_attr_t pattr; + int mproto, ret; + mutex_kind_t mkind; + thread_state_t state; + + /* + * Lock a mutex. + * + * pthread_lock not tested for: + */ + log ("Testing pthread_mutex_lock\n"); + log ("--------------------------\n"); + + assert (pthread_attr_init (&pattr) == 0); + assert (pthread_attr_setdetachstate (&pattr, + PTHREAD_CREATE_DETACHED) == 0); + state.flags = 0; /* No flags yet. */ + + for (mproto = 0; mproto < NELEMENTS(protocols); mproto++) { + for (mkind = M_POSIX; mkind <= M_SS2_RECURSIVE; mkind++) { + /* Initialize the mutex attribute. */ + assert (pthread_mutexattr_init (&mattr) == 0); + assert (pthread_mutexattr_setprotocol (&mattr, + protocols[mproto]) == 0); + + /* + * Ensure that the first mutex type is a POSIX + * compliant mutex. + */ + if (mkind != M_POSIX) { + assert (pthread_mutexattr_settype (&mattr, + mutex_types[mkind]) == 0); + } + + /* Create the mutex. */ + assert (pthread_mutex_init (&mutex, &mattr) == 0); + + log (" Protocol %s, Type %s\n", + protocol_strs[mproto], mutextype_strs[mkind]); + + log (" Lock on unlocked mutex - "); + ret = pthread_mutex_lock (&mutex); + check_result (/* expected */ 0, ret); + pthread_mutex_unlock (&mutex); + + log (" Lock on invalid mutex - "); + ret = pthread_mutex_lock (NULL); + check_result (/* expected */ EINVAL, ret); + + log (" Lock on mutex held by self - "); + assert (pthread_create (&state.tid, &pattr, lock_twice, + (void *) &state) == 0); + /* Let the thread start. */ + sleep (1); + state.cmd.mutex = &mutex; + state.ret = 0xdeadbeef; + assert (pthread_mutex_lock (&cond_mutex) == 0); + assert (pthread_cond_signal (&cond_var) == 0); + assert (pthread_mutex_unlock (&cond_mutex) == 0); + /* Let the thread receive and process the command. */ + sleep (1); + + switch (mkind) { + case M_POSIX: + check_result (/* expected */ EDEADLK, + state.ret); + break; + case M_SS2_DEFAULT: + check_result (/* expected */ EDEADLK, + state.ret); + break; + case M_SS2_ERRORCHECK: + check_result (/* expected */ EDEADLK, + state.ret); + break; + case M_SS2_NORMAL: + check_result (/* expected */ 0xdeadbeef, + state.ret); + break; + case M_SS2_RECURSIVE: + check_result (/* expected */ 0, state.ret); + break; + } + pthread_mutex_destroy (&mutex); + pthread_mutexattr_destroy (&mattr); + } + } +} + + +static void +mutex_unlock_test (void) +{ + const int test_thread_id = 0; /* ID of test thread */ + pthread_mutexattr_t mattr; + pthread_mutex_t mutex; + int mproto, ret; + mutex_kind_t mkind; + + /* + * Unlock a mutex. + * + * pthread_unlock not tested for: + */ + log ("Testing pthread_mutex_unlock\n"); + log ("----------------------------\n"); + + for (mproto = 0; mproto < NELEMENTS(protocols); mproto++) { + for (mkind = M_POSIX; mkind <= M_SS2_RECURSIVE; mkind++) { + /* Initialize the mutex attribute. */ + assert (pthread_mutexattr_init (&mattr) == 0); + assert (pthread_mutexattr_setprotocol (&mattr, + protocols[mproto]) == 0); + + /* + * Ensure that the first mutex type is a POSIX + * compliant mutex. + */ + if (mkind != M_POSIX) { + assert (pthread_mutexattr_settype (&mattr, + mutex_types[mkind]) == 0); + } + + /* Create the mutex. */ + assert (pthread_mutex_init (&mutex, &mattr) == 0); + + log (" Protocol %s, Type %s\n", + protocol_strs[mproto], mutextype_strs[mkind]); + + log (" Unlock on mutex held by self - "); + assert (pthread_mutex_lock (&mutex) == 0); + ret = pthread_mutex_unlock (&mutex); + check_result (/* expected */ 0, ret); + + log (" Unlock on invalid mutex - "); + ret = pthread_mutex_unlock (NULL); + check_result (/* expected */ EINVAL, ret); + + log (" Unlock on mutex locked by another thread - "); + send_mutex_cmd (test_thread_id, CMD_TAKE_MUTEX, &mutex); + sleep (1); + ret = pthread_mutex_unlock (&mutex); + switch (mkind) { + case M_POSIX: + check_result (/* expected */ EPERM, ret); + break; + case M_SS2_DEFAULT: + check_result (/* expected */ EPERM, ret); + break; + case M_SS2_ERRORCHECK: + check_result (/* expected */ EPERM, ret); + break; + case M_SS2_NORMAL: + check_result (/* expected */ EPERM, ret); + break; + case M_SS2_RECURSIVE: + check_result (/* expected */ EPERM, ret); + break; + } + if (ret == 0) { + /* + * If for some reason we were able to unlock + * the mutex, relock it so that the test + * thread has no problems releasing the mutex. + */ + pthread_mutex_lock (&mutex); + } + send_cmd (test_thread_id, CMD_RELEASE_ALL); + sleep (1); + + pthread_mutex_destroy (&mutex); + pthread_mutexattr_destroy (&mattr); + } + } +} + + +static void +queueing_order_test (void) +{ + int i; + + log ("Testing queueing order\n"); + log ("----------------------\n"); + assert (pthread_mutex_lock (&waiter_mutex) == 0); + /* + * Tell the threads to report when they take the waiters mutex. + */ + assert (pthread_mutex_lock (&cond_mutex) == 0); + for (i = 0; i < NUM_THREADS; i++) { + states[i].flags = FLAGS_REPORT_WAITMUTEX; + assert (pthread_cond_signal (&states[i].cond_var) == 0); + } + assert (pthread_mutex_unlock (&cond_mutex) == 0); + + /* Signal the threads to continue. */ + sleep (1); + + /* Use the global condition variable next time. */ + use_global_condvar = 1; + + /* Release the waiting threads and allow them to run again. */ + assert (pthread_mutex_unlock (&waiter_mutex) == 0); + sleep (1); + + log (" Queueing order on a mutex - "); + check_run_order ("9,8,7,6,5,4,3,2,1,0"); + for (i = 0; i < NUM_THREADS; i = i + 1) { + /* Tell the threads to report when they've been signaled. */ + states[i].flags = FLAGS_REPORT_WAITCONDVAR; + } + + /* + * Prevent the threads from continuing their loop after we + * signal them. + */ + assert (pthread_mutex_lock (&waiter_mutex) == 0); + + + log (" Queueing order on a condition variable - "); + /* + * Signal one thread to run and see that the highest priority + * thread executes. + */ + assert (pthread_mutex_lock (&cond_mutex) == 0); + assert (pthread_cond_signal (&cond_var) == 0); + assert (pthread_mutex_unlock (&cond_mutex) == 0); + sleep (1); + if (states[NUM_THREADS - 1].status != STAT_WAITMUTEX) + log_error ("highest priority thread does not run.\n"); + + /* Signal the remaining threads. */ + assert (pthread_mutex_lock (&cond_mutex) == 0); + assert (pthread_cond_broadcast (&cond_var) == 0); + assert (pthread_mutex_unlock (&cond_mutex) == 0); + sleep (1); + + check_run_order ("9,8,7,6,5,4,3,2,1,0"); + for (i = 0; i < NUM_THREADS; i = i + 1) { + /* Tell the threads not to report anything. */ + states[i].flags = 0; + } + + /* Use the thread unique condition variable next time. */ + use_global_condvar = 0; + + /* Allow the threads to continue their loop. */ + assert (pthread_mutex_unlock (&waiter_mutex) == 0); + sleep (1); +} + + +static void +mutex_prioceiling_test (void) +{ + const int test_thread_id = 0; /* ID of test thread */ + pthread_mutexattr_t mattr; + struct sched_param param; + pthread_mutex_t m[3]; + mutex_kind_t mkind; + int i, ret, policy, my_prio, old_ceiling; + + log ("Testing priority ceilings\n"); + log ("-------------------------\n"); + for (mkind = M_POSIX; mkind <= M_SS2_RECURSIVE; mkind++) { + + log (" Protype PTHREAD_PRIO_PROTECT, Type %s\n", + mutextype_strs[mkind]); + + /* + * Initialize and create a mutex. + */ + assert (pthread_mutexattr_init (&mattr) == 0); + + /* Get this threads current priority. */ + assert (pthread_getschedparam (pthread_self(), &policy, + ¶m) == 0); + my_prio = param.sched_priority; /* save for later use */ + log_trace ("Current scheduling policy %d, priority %d\n", + policy, my_prio); + + /* + * Initialize and create 3 priority protection mutexes with + * default (max priority) ceilings. + */ + assert (pthread_mutexattr_setprotocol(&mattr, + PTHREAD_PRIO_PROTECT) == 0); + + /* + * Ensure that the first mutex type is a POSIX + * compliant mutex. + */ + if (mkind != M_POSIX) { + assert (pthread_mutexattr_settype (&mattr, + mutex_types[mkind]) == 0); + } + + for (i = 0; i < 3; i++) + assert (pthread_mutex_init (&m[i], &mattr) == 0); + + /* + * Set the ceiling priorities for the 3 priority protection + * mutexes to, 5 less than, equal to, and 5 greater than, + * this threads current priority. + */ + for (i = 0; i < 3; i++) + assert (pthread_mutex_setprioceiling (&m[i], + my_prio - 5 + 5*i, &old_ceiling) == 0); + + /* + * Check that if we attempt to take a mutex whose priority + * ceiling is lower than our priority, we get an error. + */ + log (" Lock with ceiling priority < thread priority - "); + ret = pthread_mutex_lock (&m[0]); + check_result (/* expected */ EINVAL, ret); + if (ret == 0) + pthread_mutex_unlock (&m[0]); + + /* + * Check that we can take a mutex whose priority ceiling + * is equal to our priority. + */ + log (" Lock with ceiling priority = thread priority - "); + ret = pthread_mutex_lock (&m[1]); + check_result (/* expected */ 0, ret); + if (ret == 0) + pthread_mutex_unlock (&m[1]); + + /* + * Check that we can take a mutex whose priority ceiling + * is higher than our priority. + */ + log (" Lock with ceiling priority > thread priority - "); + ret = pthread_mutex_lock (&m[2]); + check_result (/* expected */ 0, ret); + if (ret == 0) + pthread_mutex_unlock (&m[2]); + + /* + * Have the test thread go into a busy loop for 5 seconds + * and see that it doesn't block this thread (since the + * priority ceiling of mutex 0 and the priority of the test + * thread are both less than the priority of this thread). + */ + log (" Preemption with ceiling priority < thread " + "priority - "); + /* Have the test thread take mutex 0. */ + send_mutex_cmd (test_thread_id, CMD_TAKE_MUTEX, &m[0]); + sleep (1); + + log_trace ("Sending busy command.\n"); + send_cmd (test_thread_id, CMD_BUSY_LOOP); + log_trace ("Busy sent, yielding\n"); + pthread_yield (); + log_trace ("Returned from yield.\n"); + if (states[test_thread_id].flags & + (FLAGS_IS_BUSY | FLAGS_WAS_BUSY)) + log_error ("test thread inproperly preempted us.\n"); + else { + /* Let the thread finish its busy loop. */ + sleep (6); + if ((states[test_thread_id].flags & FLAGS_WAS_BUSY) == 0) + log_error ("test thread never finished.\n"); + else + log_pass (); + } + states[test_thread_id].flags &= ~FLAGS_WAS_BUSY; + + /* Have the test thread release mutex 0. */ + send_cmd (test_thread_id, CMD_RELEASE_ALL); + sleep (1); + + /* + * Have the test thread go into a busy loop for 5 seconds + * and see that it preempts this thread (since the priority + * ceiling of mutex 1 is the same as the priority of this + * thread). The test thread should not run to completion + * as its time quantum should expire before the 5 seconds + * are up. + */ + log (" Preemption with ceiling priority = thread " + "priority - "); + + /* Have the test thread take mutex 1. */ + send_mutex_cmd (test_thread_id, CMD_TAKE_MUTEX, &m[1]); + sleep (1); + + log_trace ("Sending busy\n"); + send_cmd (test_thread_id, CMD_BUSY_LOOP); + log_trace ("Busy sent, yielding\n"); + pthread_yield (); + log_trace ("Returned from yield.\n"); + if ((states[test_thread_id].flags & FLAGS_IS_BUSY) == 0) + log_error ("test thread did not switch in on yield.\n"); + else if (states[test_thread_id].flags & FLAGS_WAS_BUSY) + log_error ("test thread ran to completion.\n"); + else { + /* Let the thread finish its busy loop. */ + sleep (6); + if ((states[test_thread_id].flags & FLAGS_WAS_BUSY) == 0) + log_error ("test thread never finished.\n"); + else + log_pass (); + } + states[test_thread_id].flags &= ~FLAGS_WAS_BUSY; + + /* Have the test thread release mutex 1. */ + send_cmd (test_thread_id, CMD_RELEASE_ALL); + sleep (1); + + /* + * Set the scheduling policy of the test thread to SCHED_FIFO + * and have it go into a busy loop for 5 seconds. This + * thread is SCHED_RR, and since the priority ceiling of + * mutex 1 is the same as the priority of this thread, the + * test thread should run to completion once it is switched + * in. + */ + log (" SCHED_FIFO scheduling and ceiling priority = " + "thread priority - "); + param.sched_priority = states[test_thread_id].priority; + assert (pthread_setschedparam (states[test_thread_id].tid, + SCHED_FIFO, ¶m) == 0); + + /* Have the test thread take mutex 1. */ + send_mutex_cmd (test_thread_id, CMD_TAKE_MUTEX, &m[1]); + sleep (1); + + log_trace ("Sending busy\n"); + send_cmd (test_thread_id, CMD_BUSY_LOOP); + log_trace ("Busy sent, yielding\n"); + pthread_yield (); + log_trace ("Returned from yield.\n"); + if ((states[test_thread_id].flags & FLAGS_WAS_BUSY) == 0) { + log_error ("test thread did not run to completion.\n"); + /* Let the thread finish it's busy loop. */ + sleep (6); + } + else + log_pass (); + states[test_thread_id].flags &= ~FLAGS_WAS_BUSY; + + /* Restore the test thread scheduling parameters. */ + param.sched_priority = states[test_thread_id].priority; + assert (pthread_setschedparam (states[test_thread_id].tid, + SCHED_RR, ¶m) == 0); + + /* Have the test thread release mutex 1. */ + send_cmd (test_thread_id, CMD_RELEASE_ALL); + sleep (1); + + /* + * Have the test thread go into a busy loop for 5 seconds + * and see that it preempts this thread (since the priority + * ceiling of mutex 2 is the greater than the priority of + * this thread). The test thread should run to completion + * and block this thread because its active priority is + * higher. + */ + log (" SCHED_FIFO scheduling and ceiling priority > " + "thread priority - "); + /* Have the test thread take mutex 2. */ + send_mutex_cmd (test_thread_id, CMD_TAKE_MUTEX, &m[2]); + sleep (1); + + log_trace ("Sending busy\n"); + send_cmd (test_thread_id, CMD_BUSY_LOOP); + log_trace ("Busy sent, yielding\n"); + pthread_yield (); + log_trace ("Returned from yield.\n"); + if ((states[test_thread_id].flags & FLAGS_IS_BUSY) != 0) { + log_error ("test thread did not run to completion.\n"); + /* Let the thread finish it's busy loop. */ + sleep (6); + } + else if ((states[test_thread_id].flags & FLAGS_WAS_BUSY) == 0) + log_error ("test thread never finished.\n"); + else + log_pass (); + states[test_thread_id].flags &= ~FLAGS_WAS_BUSY; + + /* Have the test thread release mutex 2. */ + send_cmd (test_thread_id, CMD_RELEASE_ALL); + sleep (1); + + /* Destroy the mutexes. */ + for (i = 0; i < 3; i++) + assert (pthread_mutex_destroy (&m[i]) == 0); + } +} + + +static void +mutex_prioinherit_test (void) +{ + pthread_mutexattr_t mattr; + struct sched_param param; + pthread_mutex_t m[3]; + mutex_kind_t mkind; + int i, policy, my_prio; + + /* Get this threads current priority. */ + assert (pthread_getschedparam (pthread_self(), &policy, + ¶m) == 0); + my_prio = param.sched_priority; /* save for later use */ + log_trace ("Current scheduling policy %d, priority %d\n", + policy, my_prio); + + log ("Testing priority inheritence\n"); + log ("----------------------------\n"); + for (mkind = M_POSIX; mkind <= M_SS2_RECURSIVE; mkind++) { + + log (" Protype PTHREAD_PRIO_INHERIT, Type %s\n", + mutextype_strs[mkind]); + + /* + * Initialize and create a mutex. + */ + assert (pthread_mutexattr_init (&mattr) == 0); + + /* + * Initialize and create 3 priority inheritence mutexes with + * default (max priority) ceilings. + */ + assert (pthread_mutexattr_setprotocol(&mattr, + PTHREAD_PRIO_INHERIT) == 0); + + /* + * Ensure that the first mutex type is a POSIX + * compliant mutex. + */ + if (mkind != M_POSIX) { + assert (pthread_mutexattr_settype (&mattr, + mutex_types[mkind]) == 0); + } + + for (i = 0; i < 3; i++) + assert (pthread_mutex_init (&m[i], &mattr) == 0); + + /* + * Test setup: + * Thread 4 - take mutex 0, 1 + * Thread 2 - enter protected busy loop with mutex 0 + * Thread 3 - enter protected busy loop with mutex 1 + * Thread 4 - enter protected busy loop with mutex 2 + * Thread 5 - enter busy loop + * Thread 6 - enter protected busy loop with mutex 0 + * Thread 4 - releases mutexes 1 and 0. + * + * Expected results: + * Threads complete in order 4, 6, 5, 3, 2 + */ + log (" Simple inheritence test - "); + + /* + * Command thread 4 to take mutexes 0 and 1. + */ + send_mutex_cmd (4, CMD_TAKE_MUTEX, &m[0]); + sleep (1); /* Allow command to be received. */ + send_mutex_cmd (4, CMD_TAKE_MUTEX, &m[1]); + sleep (1); + + /* + * Tell the threads to report themselves when they are + * at the bottom of their loop (waiting on wait_mutex). + */ + for (i = 0; i < NUM_THREADS; i++) + states[i].flags |= FLAGS_REPORT_WAITMUTEX; + + /* + * Command thread 2 to take mutex 0 and thread 3 to take + * mutex 1, both via a protected operation command. Since + * thread 4 owns mutexes 0 and 1, both threads 2 and 3 + * will block until the mutexes are released by thread 4. + */ + log_trace ("Commanding protected operation to thread 2.\n"); + send_mutex_cmd (2, CMD_PROTECTED_OP, &m[0]); + log_trace ("Commanding protected operation to thread 3.\n"); + send_mutex_cmd (3, CMD_PROTECTED_OP, &m[1]); + sleep (1); + + /* + * Command thread 4 to take mutex 2 via a protected operation + * and thread 5 to enter a busy loop for 5 seconds. Since + * thread 5 has higher priority than thread 4, thread 5 will + * enter the busy loop before thread 4 is activated. + */ + log_trace ("Commanding protected operation to thread 4.\n"); + send_mutex_cmd (4, CMD_PROTECTED_OP, &m[2]); + log_trace ("Commanding busy loop to thread 5.\n"); + send_cmd (5, CMD_BUSY_LOOP); + sleep (1); + if ((states[5].flags & FLAGS_IS_BUSY) == 0) + log_error ("thread 5 is not running.\n"); + log_trace ("Commanding protected operation thread 6.\n"); + send_mutex_cmd (6, CMD_PROTECTED_OP, &m[0]); + sleep (1); + if ((states[4].flags & FLAGS_WAS_BUSY) == 0) + log_error ("thread 4 failed to inherit priority.\n"); + states[4].flags = 0; + send_cmd (4, CMD_RELEASE_ALL); + sleep (5); + check_run_order ("4,6,5,3,2"); + + /* + * Clear the flags. + */ + for (i = 0; i < NUM_THREADS; i++) + states[i].flags = 0; + + /* + * Test setup: + * Thread 2 - enter busy loop (SCHED_FIFO) + * Thread 4 - take mutex 0 + * Thread 4 - priority change to same priority as thread 2 + * Thread 4 - release mutex 0 + * + * Expected results: + * Since thread 4 owns a priority mutex, it should be + * placed at the front of the run queue (for its new + * priority slot) when its priority is lowered to the + * same priority as thread 2. If thread 4 did not own + * a priority mutex, then it would have been added to + * the end of the run queue and thread 2 would have + * executed until it blocked (because it's scheduling + * policy is SCHED_FIFO). + * + */ + log (" Inheritence test with change of priority - "); + + /* + * Change threads 2 and 4 scheduling policies to be + * SCHED_FIFO. + */ + param.sched_priority = states[2].priority; + assert (pthread_setschedparam (states[2].tid, SCHED_FIFO, + ¶m) == 0); + param.sched_priority = states[4].priority; + assert (pthread_setschedparam (states[4].tid, SCHED_FIFO, + ¶m) == 0); + + /* + * Command thread 4 to take mutex 0. + */ + send_mutex_cmd (4, CMD_TAKE_MUTEX, &m[0]); + sleep (1); + + /* + * Command thread 2 to enter busy loop. + */ + send_cmd (2, CMD_BUSY_LOOP); + sleep (1); /* Allow command to be received. */ + + /* + * Command thread 4 to enter busy loop. + */ + send_cmd (4, CMD_BUSY_LOOP); + sleep (1); /* Allow command to be received. */ + + /* Have threads 2 and 4 report themselves. */ + states[2].flags = FLAGS_REPORT_WAITMUTEX; + states[4].flags = FLAGS_REPORT_WAITMUTEX; + + /* Change the priority of thread 4. */ + param.sched_priority = states[2].priority; + assert (pthread_setschedparam (states[4].tid, SCHED_FIFO, + ¶m) == 0); + sleep (5); + check_run_order ("4,2"); + + /* Clear the flags */ + states[2].flags = 0; + states[4].flags = 0; + + /* Reset the policies. */ + param.sched_priority = states[2].priority; + assert (pthread_setschedparam (states[2].tid, SCHED_RR, + ¶m) == 0); + param.sched_priority = states[4].priority; + assert (pthread_setschedparam (states[4].tid, SCHED_RR, + ¶m) == 0); + + send_cmd (4, CMD_RELEASE_MUTEX); + sleep (1); + + /* Destroy the mutexes. */ + for (i = 0; i < 3; i++) + assert (pthread_mutex_destroy (&m[i]) == 0); + } +} + + +int main (int argc, char *argv[]) +{ + pthread_mutexattr_t mattr; + pthread_condattr_t cattr; + pthread_attr_t pattr; + int i, policy, main_prio; + void * exit_status; + sigset_t mask; + struct sigaction act; + struct sched_param param; + + logfile = stdout; + + assert (pthread_getschedparam (pthread_self (), &policy, ¶m) == 0); + main_prio = param.sched_priority; + + /* Setupt our signal mask. */ + sigfillset (&mask); + sigdelset (&mask, SIGINT); + sigprocmask (SIG_SETMASK, &mask, NULL); + + /* Install a signal handler for SIGINT */ + sigemptyset (&act.sa_mask); + sigaddset (&act.sa_mask, SIGINT); + act.sa_handler = sighandler; + act.sa_flags = SA_RESTART; + sigaction (SIGINT, &act, NULL); + + /* This test relies on the concurrency level being 1. */ + pthread_setconcurrency(1); + + /* + * Initialize the thread attribute. + */ + assert (pthread_attr_init (&pattr) == 0); + assert (pthread_attr_setdetachstate (&pattr, + PTHREAD_CREATE_JOINABLE) == 0); + + /* + * Initialize and create the waiter and condvar mutexes. + */ + assert (pthread_mutexattr_init (&mattr) == 0); + assert (pthread_mutex_init (&waiter_mutex, &mattr) == 0); + assert (pthread_mutex_init (&cond_mutex, &mattr) == 0); + + /* + * Initialize and create a condition variable. + */ + assert (pthread_condattr_init (&cattr) == 0); + assert (pthread_cond_init (&cond_var, &cattr) == 0); + + /* Create a pipe to catch the results of thread wakeups. */ + assert (pipe (pipefd) == 0); + +#if defined(_LIBC_R_) && defined(DEBUG) + assert (pthread_switch_add_np (kern_switch) == 0); +#endif + + /* + * Create the waiting threads. + */ + for (i = 0; i < NUM_THREADS; i++) { + assert (pthread_cond_init (&states[i].cond_var, &cattr) == 0); + states[i].id = (u_int8_t) i; /* NUM_THREADS must be <= 256 */ + states[i].status = 0; + states[i].cmd.cmd_id = CMD_NONE; + states[i].flags = 0; /* No flags yet. */ + assert (pthread_create (&states[i].tid, &pattr, waiter, + (void *) &states[i]) == 0); + param.sched_priority = main_prio - 10 + i; + states[i].priority = param.sched_priority; + assert (pthread_setschedparam (states[i].tid, SCHED_OTHER, + ¶m) == 0); +#if defined(_LIBC_R_) + { + char buf[30]; + + snprintf (buf, sizeof(buf), "waiter_%d", i); + pthread_set_name_np (states[i].tid, buf); + } +#endif + } + + /* Allow the threads to start. */ + sleep (1); + log_trace ("Done creating threads.\n"); + + log ("\n"); + mutex_init_test (); + log ("\n"); + mutex_destroy_test (); + log ("\n"); + mutex_lock_test (); + log ("\n"); + mutex_unlock_test (); + log ("\n"); + queueing_order_test (); + log ("\n"); + mutex_prioinherit_test (); + log ("\n"); + mutex_prioceiling_test (); + log ("\n"); + + log ("Total tests %d, passed %d, failed %d\n", + total, pass_count, error_count); + + /* Set the done flag and signal the threads to exit. */ + log_trace ("Setting done flag.\n"); + done = 1; + + /* + * Wait for the threads to finish. + */ + log_trace ("Trying to join threads.\n"); + for (i = 0; i < NUM_THREADS; i++) { + send_cmd (i, CMD_NONE); + assert (pthread_join (states[i].tid, &exit_status) == 0); + } + + /* Clean up after ourselves. */ + close (pipefd[0]); + close (pipefd[1]); + + if (error_count != 0) + exit (EX_OSERR); /* any better ideas??? */ + else + exit (EX_OK); +} diff --git a/lib/libpthread/test/mutex_d.exp b/lib/libpthread/test/mutex_d.exp new file mode 100644 index 0000000..de8a4e4 --- /dev/null +++ b/lib/libpthread/test/mutex_d.exp @@ -0,0 +1,290 @@ + +Testing pthread_mutex_init +-------------------------- + Protocol PTHREAD_PRIO_NONE, Type POSIX (type not specified) - PASS + Protocol PTHREAD_PRIO_NONE, Type SS2 PTHREAD_MUTEX_DEFAULT - PASS + Protocol PTHREAD_PRIO_NONE, Type SS2 PTHREAD_MUTEX_ERRORCHECK - PASS + Protocol PTHREAD_PRIO_NONE, Type SS2 PTHREAD_MUTEX_NORMAL - PASS + Protocol PTHREAD_PRIO_NONE, Type SS2 PTHREAD_MUTEX_RECURSIVE - PASS + Protocol PTHREAD_PRIO_INHERIT, Type POSIX (type not specified) - PASS + Protocol PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_DEFAULT - PASS + Protocol PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_ERRORCHECK - PASS + Protocol PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_NORMAL - PASS + Protocol PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_RECURSIVE - PASS + Protocol PTHREAD_PRIO_PROTECT, Type POSIX (type not specified) - PASS + Protocol PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_DEFAULT - PASS + Protocol PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_ERRORCHECK - PASS + Protocol PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_NORMAL - PASS + Protocol PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_RECURSIVE - PASS + +Testing pthread_mutex_destroy +----------------------------- + Protocol PTHREAD_PRIO_NONE, Type POSIX (type not specified) + Destruction of unused mutex - PASS + Destruction of mutex locked by self - PASS + Destruction of mutex locked by another thread - PASS + Destruction of mutex while being used in cond_wait - PASS + Protocol PTHREAD_PRIO_NONE, Type SS2 PTHREAD_MUTEX_DEFAULT + Destruction of unused mutex - PASS + Destruction of mutex locked by self - PASS + Destruction of mutex locked by another thread - PASS + Destruction of mutex while being used in cond_wait - PASS + Protocol PTHREAD_PRIO_NONE, Type SS2 PTHREAD_MUTEX_ERRORCHECK + Destruction of unused mutex - PASS + Destruction of mutex locked by self - PASS + Destruction of mutex locked by another thread - PASS + Destruction of mutex while being used in cond_wait - PASS + Protocol PTHREAD_PRIO_NONE, Type SS2 PTHREAD_MUTEX_NORMAL + Destruction of unused mutex - PASS + Destruction of mutex locked by self - PASS + Destruction of mutex locked by another thread - PASS + Destruction of mutex while being used in cond_wait - PASS + Protocol PTHREAD_PRIO_NONE, Type SS2 PTHREAD_MUTEX_RECURSIVE + Destruction of unused mutex - PASS + Destruction of mutex locked by self - PASS + Destruction of mutex locked by another thread - PASS + Destruction of mutex while being used in cond_wait - PASS + Protocol PTHREAD_PRIO_INHERIT, Type POSIX (type not specified) + Destruction of unused mutex - PASS + Destruction of mutex locked by self - PASS + Destruction of mutex locked by another thread - PASS + Destruction of mutex while being used in cond_wait - PASS + Protocol PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_DEFAULT + Destruction of unused mutex - PASS + Destruction of mutex locked by self - PASS + Destruction of mutex locked by another thread - PASS + Destruction of mutex while being used in cond_wait - PASS + Protocol PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_ERRORCHECK + Destruction of unused mutex - PASS + Destruction of mutex locked by self - PASS + Destruction of mutex locked by another thread - PASS + Destruction of mutex while being used in cond_wait - PASS + Protocol PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_NORMAL + Destruction of unused mutex - PASS + Destruction of mutex locked by self - PASS + Destruction of mutex locked by another thread - PASS + Destruction of mutex while being used in cond_wait - PASS + Protocol PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_RECURSIVE + Destruction of unused mutex - PASS + Destruction of mutex locked by self - PASS + Destruction of mutex locked by another thread - PASS + Destruction of mutex while being used in cond_wait - PASS + Protocol PTHREAD_PRIO_PROTECT, Type POSIX (type not specified) + Destruction of unused mutex - PASS + Destruction of mutex locked by self - PASS + Destruction of mutex locked by another thread - PASS + Destruction of mutex while being used in cond_wait - PASS + Protocol PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_DEFAULT + Destruction of unused mutex - PASS + Destruction of mutex locked by self - PASS + Destruction of mutex locked by another thread - PASS + Destruction of mutex while being used in cond_wait - PASS + Protocol PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_ERRORCHECK + Destruction of unused mutex - PASS + Destruction of mutex locked by self - PASS + Destruction of mutex locked by another thread - PASS + Destruction of mutex while being used in cond_wait - PASS + Protocol PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_NORMAL + Destruction of unused mutex - PASS + Destruction of mutex locked by self - PASS + Destruction of mutex locked by another thread - PASS + Destruction of mutex while being used in cond_wait - PASS + Protocol PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_RECURSIVE + Destruction of unused mutex - PASS + Destruction of mutex locked by self - PASS + Destruction of mutex locked by another thread - PASS + Destruction of mutex while being used in cond_wait - PASS + +Testing pthread_mutex_lock +-------------------------- + Protocol PTHREAD_PRIO_NONE, Type POSIX (type not specified) + Lock on unlocked mutex - PASS + Lock on invalid mutex - PASS + Lock on mutex held by self - PASS + Protocol PTHREAD_PRIO_NONE, Type SS2 PTHREAD_MUTEX_DEFAULT + Lock on unlocked mutex - PASS + Lock on invalid mutex - PASS + Lock on mutex held by self - PASS + Protocol PTHREAD_PRIO_NONE, Type SS2 PTHREAD_MUTEX_ERRORCHECK + Lock on unlocked mutex - PASS + Lock on invalid mutex - PASS + Lock on mutex held by self - PASS + Protocol PTHREAD_PRIO_NONE, Type SS2 PTHREAD_MUTEX_NORMAL + Lock on unlocked mutex - PASS + Lock on invalid mutex - PASS + Lock on mutex held by self - PASS + Protocol PTHREAD_PRIO_NONE, Type SS2 PTHREAD_MUTEX_RECURSIVE + Lock on unlocked mutex - PASS + Lock on invalid mutex - PASS + Lock on mutex held by self - PASS + Protocol PTHREAD_PRIO_INHERIT, Type POSIX (type not specified) + Lock on unlocked mutex - PASS + Lock on invalid mutex - PASS + Lock on mutex held by self - PASS + Protocol PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_DEFAULT + Lock on unlocked mutex - PASS + Lock on invalid mutex - PASS + Lock on mutex held by self - PASS + Protocol PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_ERRORCHECK + Lock on unlocked mutex - PASS + Lock on invalid mutex - PASS + Lock on mutex held by self - PASS + Protocol PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_NORMAL + Lock on unlocked mutex - PASS + Lock on invalid mutex - PASS + Lock on mutex held by self - PASS + Protocol PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_RECURSIVE + Lock on unlocked mutex - PASS + Lock on invalid mutex - PASS + Lock on mutex held by self - PASS + Protocol PTHREAD_PRIO_PROTECT, Type POSIX (type not specified) + Lock on unlocked mutex - PASS + Lock on invalid mutex - PASS + Lock on mutex held by self - PASS + Protocol PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_DEFAULT + Lock on unlocked mutex - PASS + Lock on invalid mutex - PASS + Lock on mutex held by self - PASS + Protocol PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_ERRORCHECK + Lock on unlocked mutex - PASS + Lock on invalid mutex - PASS + Lock on mutex held by self - PASS + Protocol PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_NORMAL + Lock on unlocked mutex - PASS + Lock on invalid mutex - PASS + Lock on mutex held by self - PASS + Protocol PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_RECURSIVE + Lock on unlocked mutex - PASS + Lock on invalid mutex - PASS + Lock on mutex held by self - PASS + +Testing pthread_mutex_unlock +---------------------------- + Protocol PTHREAD_PRIO_NONE, Type POSIX (type not specified) + Unlock on mutex held by self - PASS + Unlock on invalid mutex - PASS + Unlock on mutex locked by another thread - PASS + Protocol PTHREAD_PRIO_NONE, Type SS2 PTHREAD_MUTEX_DEFAULT + Unlock on mutex held by self - PASS + Unlock on invalid mutex - PASS + Unlock on mutex locked by another thread - PASS + Protocol PTHREAD_PRIO_NONE, Type SS2 PTHREAD_MUTEX_ERRORCHECK + Unlock on mutex held by self - PASS + Unlock on invalid mutex - PASS + Unlock on mutex locked by another thread - PASS + Protocol PTHREAD_PRIO_NONE, Type SS2 PTHREAD_MUTEX_NORMAL + Unlock on mutex held by self - PASS + Unlock on invalid mutex - PASS + Unlock on mutex locked by another thread - PASS + Protocol PTHREAD_PRIO_NONE, Type SS2 PTHREAD_MUTEX_RECURSIVE + Unlock on mutex held by self - PASS + Unlock on invalid mutex - PASS + Unlock on mutex locked by another thread - PASS + Protocol PTHREAD_PRIO_INHERIT, Type POSIX (type not specified) + Unlock on mutex held by self - PASS + Unlock on invalid mutex - PASS + Unlock on mutex locked by another thread - PASS + Protocol PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_DEFAULT + Unlock on mutex held by self - PASS + Unlock on invalid mutex - PASS + Unlock on mutex locked by another thread - PASS + Protocol PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_ERRORCHECK + Unlock on mutex held by self - PASS + Unlock on invalid mutex - PASS + Unlock on mutex locked by another thread - PASS + Protocol PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_NORMAL + Unlock on mutex held by self - PASS + Unlock on invalid mutex - PASS + Unlock on mutex locked by another thread - PASS + Protocol PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_RECURSIVE + Unlock on mutex held by self - PASS + Unlock on invalid mutex - PASS + Unlock on mutex locked by another thread - PASS + Protocol PTHREAD_PRIO_PROTECT, Type POSIX (type not specified) + Unlock on mutex held by self - PASS + Unlock on invalid mutex - PASS + Unlock on mutex locked by another thread - PASS + Protocol PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_DEFAULT + Unlock on mutex held by self - PASS + Unlock on invalid mutex - PASS + Unlock on mutex locked by another thread - PASS + Protocol PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_ERRORCHECK + Unlock on mutex held by self - PASS + Unlock on invalid mutex - PASS + Unlock on mutex locked by another thread - PASS + Protocol PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_NORMAL + Unlock on mutex held by self - PASS + Unlock on invalid mutex - PASS + Unlock on mutex locked by another thread - PASS + Protocol PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_RECURSIVE + Unlock on mutex held by self - PASS + Unlock on invalid mutex - PASS + Unlock on mutex locked by another thread - PASS + +Testing queueing order +---------------------- + Queueing order on a mutex - PASS + Queueing order on a condition variable - PASS + +Testing priority inheritence +---------------------------- + Protype PTHREAD_PRIO_INHERIT, Type POSIX (type not specified) + Simple inheritence test - PASS + Inheritence test with change of priority - PASS + Protype PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_DEFAULT + Simple inheritence test - PASS + Inheritence test with change of priority - PASS + Protype PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_ERRORCHECK + Simple inheritence test - PASS + Inheritence test with change of priority - PASS + Protype PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_NORMAL + Simple inheritence test - PASS + Inheritence test with change of priority - PASS + Protype PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_RECURSIVE + Simple inheritence test - PASS + Inheritence test with change of priority - PASS + +Testing priority ceilings +------------------------- + Protype PTHREAD_PRIO_PROTECT, Type POSIX (type not specified) + Lock with ceiling priority < thread priority - PASS + Lock with ceiling priority = thread priority - PASS + Lock with ceiling priority > thread priority - PASS + Preemption with ceiling priority < thread priority - PASS + Preemption with ceiling priority = thread priority - PASS + SCHED_FIFO scheduling and ceiling priority = thread priority - PASS + SCHED_FIFO scheduling and ceiling priority > thread priority - PASS + Protype PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_DEFAULT + Lock with ceiling priority < thread priority - PASS + Lock with ceiling priority = thread priority - PASS + Lock with ceiling priority > thread priority - PASS + Preemption with ceiling priority < thread priority - PASS + Preemption with ceiling priority = thread priority - PASS + SCHED_FIFO scheduling and ceiling priority = thread priority - PASS + SCHED_FIFO scheduling and ceiling priority > thread priority - PASS + Protype PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_ERRORCHECK + Lock with ceiling priority < thread priority - PASS + Lock with ceiling priority = thread priority - PASS + Lock with ceiling priority > thread priority - PASS + Preemption with ceiling priority < thread priority - PASS + Preemption with ceiling priority = thread priority - PASS + SCHED_FIFO scheduling and ceiling priority = thread priority - PASS + SCHED_FIFO scheduling and ceiling priority > thread priority - PASS + Protype PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_NORMAL + Lock with ceiling priority < thread priority - PASS + Lock with ceiling priority = thread priority - PASS + Lock with ceiling priority > thread priority - PASS + Preemption with ceiling priority < thread priority - PASS + Preemption with ceiling priority = thread priority - PASS + SCHED_FIFO scheduling and ceiling priority = thread priority - PASS + SCHED_FIFO scheduling and ceiling priority > thread priority - PASS + Protype PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_RECURSIVE + Lock with ceiling priority < thread priority - PASS + Lock with ceiling priority = thread priority - PASS + Lock with ceiling priority > thread priority - PASS + Preemption with ceiling priority < thread priority - PASS + Preemption with ceiling priority = thread priority - PASS + SCHED_FIFO scheduling and ceiling priority = thread priority - PASS + SCHED_FIFO scheduling and ceiling priority > thread priority - PASS + +Total tests 212, passed 212, failed 0 diff --git a/lib/libpthread/test/propagate_s.pl b/lib/libpthread/test/propagate_s.pl new file mode 100755 index 0000000..6b85090 --- /dev/null +++ b/lib/libpthread/test/propagate_s.pl @@ -0,0 +1,74 @@ +#!/usr/bin/perl -w +# +# Copyright (C) 2000 Jason Evans <jasone@freebsd.org>. +# 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(s), this list of conditions and the following disclaimer as +# the first lines of this file unmodified other than the possible +# addition of one or more copyright notices. +# 2. Redistributions in binary form must reproduce the above copyright +# notice(s), this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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. +# +########################################################################### +# +# Verify that no cancellation points are propagated inside of libpthread. +# +# $FreeBSD$ +# + +@CPOINTS = ("aio_suspend", "close", "creat", "fcntl", "fsync", "mq_receive", + "mq_send", "msync", "nanosleep", "open", "pause", + "pthread_cond_timedwait", "pthread_cond_wait", "pthread_join", + "pthread_testcancel", "read", "sem_wait", "sigsuspend", + "sigtimedwait", "sigwait", "sigwaitinfo", "sleep", "system", + "tcdrain", "wait", "waitpid", "write"); + +print "1..1\n"; + +$cpoints = join '\|', @CPOINTS; +$regexp = "\" U \\(" . $cpoints . "\\\)\$\""; + +`nm -a /usr/lib/libc.a |grep $regexp >propagate_s.out`; +if (!open (NMOUT, "<./propagate_s.out")) +{ + print "not ok 1\n"; +} +else +{ + $propagations = 0; + + while (<NMOUT>) + { + $propagations++; + print "$_\n"; + } + if ($propagations != 0) + { + print "$propagations propagation(s)\n"; + print "not ok 1\n"; + } + else + { + print "ok 1\n"; + } + close NMOUT; + unlink "propagate_s.out"; +} diff --git a/lib/libpthread/test/sem_d.c b/lib/libpthread/test/sem_d.c new file mode 100644 index 0000000..b834591 --- /dev/null +++ b/lib/libpthread/test/sem_d.c @@ -0,0 +1,133 @@ +/**************************************************************************** + * + * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>. + * 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(s), this list of conditions and the following disclaimer as + * the first lines of this file unmodified other than the possible + * addition of one or more copyright notices. + * 2. Redistributions in binary form must reproduce the above copyright + * notice(s), this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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. + * + **************************************************************************** + * + * sem test. + * + * $FreeBSD$ + * + ****************************************************************************/ + +#include <assert.h> +#include <stdio.h> +#include <fcntl.h> +#include <errno.h> +#include <semaphore.h> +#include <pthread.h> + +#define NTHREADS 10 + +void * +entry(void * a_arg) +{ + sem_t * sem = (sem_t *) a_arg; + + sem_wait(sem); + fprintf(stderr, "Got semaphore\n"); + + return NULL; +} + +int +main() +{ + sem_t sem_a, sem_b; + pthread_t threads[NTHREADS]; + unsigned i; + int val; + + fprintf(stderr, "Test begin\n"); + +#ifdef _LIBC_R_ + assert(-1 == sem_init(&sem_b, 1, 0)); + assert(EPERM == errno); +#endif + + assert(0 == sem_init(&sem_b, 0, 0)); + assert(0 == sem_getvalue(&sem_b, &val)); + assert(0 == val); + + assert(0 == sem_post(&sem_b)); + assert(0 == sem_getvalue(&sem_b, &val)); + assert(1 == val); + + assert(0 == sem_wait(&sem_b)); + assert(-1 == sem_trywait(&sem_b)); + assert(EAGAIN == errno); + assert(0 == sem_post(&sem_b)); + assert(0 == sem_trywait(&sem_b)); + assert(0 == sem_post(&sem_b)); + assert(0 == sem_wait(&sem_b)); + assert(0 == sem_post(&sem_b)); + +#ifdef _LIBC_R_ + assert(SEM_FAILED == sem_open("/foo", O_CREAT | O_EXCL, 0644, 0)); + assert(ENOSYS == errno); + + assert(-1 == sem_close(&sem_b)); + assert(ENOSYS == errno); + + assert(-1 == sem_unlink("/foo")); + assert(ENOSYS == errno); +#endif + + assert(0 == sem_destroy(&sem_b)); + + assert(0 == sem_init(&sem_a, 0, 0)); + + for (i = 0; i < NTHREADS; i++) { + pthread_create(&threads[i], NULL, entry, (void *) &sem_a); + } + + for (i = 0; i < NTHREADS; i++) { + assert(0 == sem_post(&sem_a)); + } + + for (i = 0; i < NTHREADS; i++) { + pthread_join(threads[i], NULL); + } + + for (i = 0; i < NTHREADS; i++) { + pthread_create(&threads[i], NULL, entry, (void *) &sem_a); + } + + for (i = 0; i < NTHREADS; i++) { + assert(0 == sem_post(&sem_a)); + } + + for (i = 0; i < NTHREADS; i++) { + pthread_join(threads[i], NULL); + } + + assert(0 == sem_destroy(&sem_a)); + + fprintf(stderr, "Test end\n"); + return 0; +} diff --git a/lib/libpthread/test/sem_d.exp b/lib/libpthread/test/sem_d.exp new file mode 100644 index 0000000..b0de3da --- /dev/null +++ b/lib/libpthread/test/sem_d.exp @@ -0,0 +1,22 @@ +Test begin +Got semaphore +Got semaphore +Got semaphore +Got semaphore +Got semaphore +Got semaphore +Got semaphore +Got semaphore +Got semaphore +Got semaphore +Got semaphore +Got semaphore +Got semaphore +Got semaphore +Got semaphore +Got semaphore +Got semaphore +Got semaphore +Got semaphore +Got semaphore +Test end diff --git a/lib/libpthread/test/sigsuspend_d.c b/lib/libpthread/test/sigsuspend_d.c new file mode 100644 index 0000000..d405e3d --- /dev/null +++ b/lib/libpthread/test/sigsuspend_d.c @@ -0,0 +1,290 @@ +/* + * Copyright (c) 1998 Daniel M. Eischen <eischen@vigrid.com> + * 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 Daniel M. Eischen. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL M. EISCHEN 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. + * + * $FreeBSD$ + */ +#include <stdlib.h> +#include <unistd.h> + +#include <errno.h> +#include <pthread.h> +#include <signal.h> +#include <stdio.h> +#include <string.h> + +#if defined(_LIBC_R_) +#include <pthread_np.h> +#endif + +static int sigcounts[NSIG + 1]; +static int sigfifo[NSIG + 1]; +static int fifo_depth = 0; +static sigset_t suspender_mask; +static pthread_t suspender_tid; + + +static void * +sigsuspender (void *arg) +{ + int save_count, status, i; + sigset_t run_mask; + + /* Run with all signals blocked. */ + sigfillset (&run_mask); + sigprocmask (SIG_SETMASK, &run_mask, NULL); + + /* Allow these signals to wake us up during a sigsuspend. */ + sigfillset (&suspender_mask); /* Default action */ + sigdelset (&suspender_mask, SIGKILL); /* Cannot catch */ + sigdelset (&suspender_mask, SIGSTOP); /* Cannot catch */ + sigdelset (&suspender_mask, SIGINT); /* terminate */ + sigdelset (&suspender_mask, SIGHUP); /* terminate */ + sigdelset (&suspender_mask, SIGQUIT); /* create core image */ + sigdelset (&suspender_mask, SIGURG); /* ignore */ + sigdelset (&suspender_mask, SIGIO); /* ignore */ + sigdelset (&suspender_mask, SIGUSR2); /* terminate */ + + while (sigcounts[SIGINT] == 0) { + save_count = sigcounts[SIGUSR2]; + + status = sigsuspend (&suspender_mask); + if ((status == 0) || (errno != EINTR)) { + fprintf (stderr, "Unable to suspend for signals, " + "errno %d, return value %d\n", + errno, status); + exit (1); + } + for (i = 0; i < fifo_depth; i++) + fprintf (stderr, "Sigsuspend woke up by signal %d\n", + sigfifo[i]); + fifo_depth = 0; + } + + pthread_exit (arg); + return (NULL); +} + + +static void +sighandler (int signo) +{ + sigset_t set, suspend_set; + pthread_t self; + + if ((signo >= 0) && (signo <= NSIG)) + sigcounts[signo]++; + + /* + * If we are running on behalf of the suspender thread, + * ensure that we have the correct mask set. + */ + self = pthread_self (); + if (self == suspender_tid) { + sigfifo[fifo_depth] = signo; + fifo_depth++; + fprintf (stderr, + " -> Suspender thread signal handler caught signal %d\n", + signo); + + /* Get the current signal mask. */ + sigprocmask (SIG_SETMASK, NULL, &set); + + /* The handler should run with the current signal masked. */ + suspend_set = suspender_mask; + sigaddset(&suspend_set, signo); + + if (memcmp(&set, &suspend_set, sizeof(set))) + fprintf (stderr, + " >>> FAIL: sigsuspender signal handler running " + "with incorrect mask.\n"); + } + else + fprintf (stderr, + " -> Main thread signal handler caught signal %d\n", + signo); +} + + +static void +send_thread_signal (pthread_t tid, int signo) +{ + if (pthread_kill (tid, signo) != 0) { + fprintf (stderr, "Unable to send thread signal, errno %d.\n", + errno); + exit (1); + } +} + + +static void +send_process_signal (int signo) +{ + if (kill (getpid (), signo) != 0) { + fprintf (stderr, "Unable to send process signal, errno %d.\n", + errno); + exit (1); + } +} + + +int main (int argc, char *argv[]) +{ + pthread_attr_t pattr; + void * exit_status; + struct sigaction act; + sigset_t oldset; + sigset_t newset; + + /* Initialize our signal counts. */ + memset ((void *) sigcounts, 0, NSIG * sizeof (int)); + + /* Ignore signal SIGIO. */ + sigemptyset (&act.sa_mask); + sigaddset (&act.sa_mask, SIGIO); + act.sa_handler = SIG_IGN; + act.sa_flags = 0; + sigaction (SIGIO, &act, NULL); + + /* Install a signal handler for SIGURG. */ + sigemptyset (&act.sa_mask); + sigaddset (&act.sa_mask, SIGURG); + act.sa_handler = sighandler; + act.sa_flags = SA_RESTART; + sigaction (SIGURG, &act, NULL); + + /* Install a signal handler for SIGXCPU */ + sigemptyset (&act.sa_mask); + sigaddset (&act.sa_mask, SIGXCPU); + sigaction (SIGXCPU, &act, NULL); + + /* Get our current signal mask. */ + sigprocmask (SIG_SETMASK, NULL, &oldset); + + /* Mask out SIGUSR1 and SIGUSR2. */ + newset = oldset; + sigaddset (&newset, SIGUSR1); + sigaddset (&newset, SIGUSR2); + sigprocmask (SIG_SETMASK, &newset, NULL); + + /* Install a signal handler for SIGUSR1 */ + sigemptyset (&act.sa_mask); + sigaddset (&act.sa_mask, SIGUSR1); + sigaction (SIGUSR1, &act, NULL); + + /* Install a signal handler for SIGUSR2 */ + sigemptyset (&act.sa_mask); + sigaddset (&act.sa_mask, SIGUSR2); + sigaction (SIGUSR2, &act, NULL); + + /* + * Initialize the thread attribute. + */ + if ((pthread_attr_init (&pattr) != 0) || + (pthread_attr_setdetachstate (&pattr, + PTHREAD_CREATE_JOINABLE) != 0)) { + fprintf (stderr, "Unable to initialize thread attributes.\n"); + exit (1); + } + + /* + * Create the sigsuspender thread. + */ + if (pthread_create (&suspender_tid, &pattr, sigsuspender, NULL) != 0) { + fprintf (stderr, "Unable to create thread, errno %d.\n", errno); + exit (1); + } +#if defined(_LIBC_R_) + pthread_set_name_np (suspender_tid, "sigsuspender"); +#endif + + /* + * Verify that an ignored signal doesn't cause a wakeup. + * We don't have a handler installed for SIGIO. + */ + send_thread_signal (suspender_tid, SIGIO); + sleep (1); + send_process_signal (SIGIO); + sleep (1); + if (sigcounts[SIGIO] != 0) + fprintf (stderr, "FAIL: sigsuspend wakes up for ignored signal " + "SIGIO.\n"); + + /* + * Verify that a signal with a default action of ignore, for + * which we have a signal handler installed, will release a + * sigsuspend. + */ + send_thread_signal (suspender_tid, SIGURG); + sleep (1); + send_process_signal (SIGURG); + sleep (1); + if (sigcounts[SIGURG] != 2) + fprintf (stderr, + "FAIL: sigsuspend doesn't wake up for SIGURG.\n"); + + /* + * Verify that a SIGUSR2 signal will release a sigsuspended + * thread. + */ + send_thread_signal (suspender_tid, SIGUSR2); + sleep (1); + send_process_signal (SIGUSR2); + sleep (1); + if (sigcounts[SIGUSR2] != 2) + fprintf (stderr, + "FAIL: sigsuspend doesn't wake up for SIGUSR2.\n"); + + /* + * Verify that a signal, blocked in both the main and + * sigsuspender threads, does not cause the signal handler + * to be called. + */ + send_thread_signal (suspender_tid, SIGUSR1); + sleep (1); + send_process_signal (SIGUSR1); + sleep (1); + if (sigcounts[SIGUSR1] != 0) + fprintf (stderr, "FAIL: signal hander called for SIGUSR1.\n"); + + /* + * Verify that we can still kill the process for a signal + * not being waited on by sigwait. + */ + send_process_signal (SIGPIPE); + fprintf (stderr, "FAIL: SIGPIPE did not terminate process.\n"); + + /* + * Wait for the thread to finish. + */ + pthread_join (suspender_tid, &exit_status); + + return (0); +} diff --git a/lib/libpthread/test/sigsuspend_d.exp b/lib/libpthread/test/sigsuspend_d.exp new file mode 100644 index 0000000..901fa50 --- /dev/null +++ b/lib/libpthread/test/sigsuspend_d.exp @@ -0,0 +1,8 @@ + -> Suspender thread signal handler caught signal 16 +Sigsuspend woke up by signal 16 + -> Suspender thread signal handler caught signal 16 +Sigsuspend woke up by signal 16 + -> Suspender thread signal handler caught signal 31 +Sigsuspend woke up by signal 31 + -> Suspender thread signal handler caught signal 31 +Sigsuspend woke up by signal 31 diff --git a/lib/libpthread/test/sigwait_d.c b/lib/libpthread/test/sigwait_d.c new file mode 100644 index 0000000..f3ccd6b --- /dev/null +++ b/lib/libpthread/test/sigwait_d.c @@ -0,0 +1,304 @@ +/* + * Copyright (c) 1998 Daniel M. Eischen <eischen@vigrid.com> + * 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 Daniel M. Eischen. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL M. EISCHEN 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. + * + * $FreeBSD$ + */ +#include <stdlib.h> +#include <unistd.h> + +#include <errno.h> +#include <pthread.h> +#include <signal.h> +#include <stdio.h> +#include <string.h> + +#if defined(_LIBC_R_) +#include <pthread_np.h> +#endif + +static int sigcounts[NSIG + 1]; +static sigset_t wait_mask; +static pthread_mutex_t waiter_mutex; + + +static void * +sigwaiter (void *arg) +{ + int signo; + sigset_t mask; + + /* Block SIGHUP */ + sigemptyset (&mask); + sigaddset (&mask, SIGHUP); + sigprocmask (SIG_BLOCK, &mask, NULL); + + while (sigcounts[SIGINT] == 0) { + if (sigwait (&wait_mask, &signo) != 0) { + fprintf (stderr, + "Unable to wait for signal, errno %d\n", + errno); + exit (1); + } + sigcounts[signo]++; + fprintf (stderr, "Sigwait caught signal %d\n", signo); + + /* Allow the main thread to prevent the sigwait. */ + pthread_mutex_lock (&waiter_mutex); + pthread_mutex_unlock (&waiter_mutex); + } + + pthread_exit (arg); + return (NULL); +} + + +static void +sighandler (int signo) +{ + fprintf (stderr, " -> Signal handler caught signal %d\n", signo); + + if ((signo >= 0) && (signo <= NSIG)) + sigcounts[signo]++; +} + +static void +send_thread_signal (pthread_t tid, int signo) +{ + if (pthread_kill (tid, signo) != 0) { + fprintf (stderr, "Unable to send thread signal, errno %d.\n", + errno); + exit (1); + } +} + +static void +send_process_signal (int signo) +{ + if (kill (getpid (), signo) != 0) { + fprintf (stderr, "Unable to send process signal, errno %d.\n", + errno); + exit (1); + } +} + + +int main (int argc, char *argv[]) +{ + pthread_mutexattr_t mattr; + pthread_attr_t pattr; + pthread_t tid; + void * exit_status; + struct sigaction act; + + /* Initialize our signal counts. */ + memset ((void *) sigcounts, 0, NSIG * sizeof (int)); + + /* Setup our wait mask. */ + sigemptyset (&wait_mask); /* Default action */ + sigaddset (&wait_mask, SIGHUP); /* terminate */ + sigaddset (&wait_mask, SIGINT); /* terminate */ + sigaddset (&wait_mask, SIGQUIT); /* create core image */ + sigaddset (&wait_mask, SIGURG); /* ignore */ + sigaddset (&wait_mask, SIGIO); /* ignore */ + sigaddset (&wait_mask, SIGUSR1); /* terminate */ + + /* Ignore signals SIGHUP and SIGIO. */ + sigemptyset (&act.sa_mask); + sigaddset (&act.sa_mask, SIGHUP); + sigaddset (&act.sa_mask, SIGIO); + act.sa_handler = SIG_IGN; + act.sa_flags = 0; + sigaction (SIGHUP, &act, NULL); + sigaction (SIGIO, &act, NULL); + + /* Install a signal handler for SIGURG */ + sigemptyset (&act.sa_mask); + sigaddset (&act.sa_mask, SIGURG); + act.sa_handler = sighandler; + act.sa_flags = SA_RESTART; + sigaction (SIGURG, &act, NULL); + + /* Install a signal handler for SIGXCPU */ + sigemptyset (&act.sa_mask); + sigaddset (&act.sa_mask, SIGXCPU); + sigaction (SIGXCPU, &act, NULL); + + /* + * Initialize the thread attribute. + */ + if ((pthread_attr_init (&pattr) != 0) || + (pthread_attr_setdetachstate (&pattr, + PTHREAD_CREATE_JOINABLE) != 0)) { + fprintf (stderr, "Unable to initialize thread attributes.\n"); + exit (1); + } + + /* + * Initialize and create a mutex. + */ + if ((pthread_mutexattr_init (&mattr) != 0) || + (pthread_mutex_init (&waiter_mutex, &mattr) != 0)) { + fprintf (stderr, "Unable to create waiter mutex.\n"); + exit (1); + } + + /* + * Create the sigwaiter thread. + */ + if (pthread_create (&tid, &pattr, sigwaiter, NULL) != 0) { + fprintf (stderr, "Unable to create thread.\n"); + exit (1); + } +#if defined(_LIBC_R_) + pthread_set_name_np (tid, "sigwaiter"); +#endif + + /* + * Verify that an ignored signal doesn't cause a wakeup. + * We don't have a handler installed for SIGIO. + */ + send_thread_signal (tid, SIGIO); + sleep (1); + send_process_signal (SIGIO); + sleep (1); + if (sigcounts[SIGIO] != 0) + fprintf (stderr, + "FAIL: sigwait wakes up for ignored signal SIGIO.\n"); + + /* + * Verify that a signal with a default action of ignore, for + * which we have a signal handler installed, will release a sigwait. + */ + send_thread_signal (tid, SIGURG); + sleep (1); + send_process_signal (SIGURG); + sleep (1); + if (sigcounts[SIGURG] != 2) + fprintf (stderr, "FAIL: sigwait doesn't wake up for SIGURG.\n"); + + /* + * Verify that a signal with a default action that terminates + * the process will release a sigwait. + */ + send_thread_signal (tid, SIGUSR1); + sleep (1); + send_process_signal (SIGUSR1); + sleep (1); + if (sigcounts[SIGUSR1] != 2) + fprintf (stderr, + "FAIL: sigwait doesn't wake up for SIGUSR1.\n"); + + /* + * Verify that if we install a signal handler for a previously + * ignored signal, an occurrence of this signal will release + * the (already waiting) sigwait. + */ + + /* Install a signal handler for SIGHUP. */ + sigemptyset (&act.sa_mask); + sigaddset (&act.sa_mask, SIGHUP); + act.sa_handler = sighandler; + act.sa_flags = SA_RESTART; + sigaction (SIGHUP, &act, NULL); + + /* Sending SIGHUP should release the sigwait. */ + send_process_signal (SIGHUP); + sleep (1); + send_thread_signal (tid, SIGHUP); + sleep (1); + if (sigcounts[SIGHUP] != 2) + fprintf (stderr, "FAIL: sigwait doesn't wake up for SIGHUP.\n"); + + /* + * Verify that a pending signal in the waiters mask will + * cause sigwait to return the pending signal. We do this + * by taking the waiters mutex and signaling the waiter to + * release him from the sigwait. The waiter will block + * on taking the mutex, and we can then send the waiter a + * signal which should be added to his pending signals. + * The next time the waiter does a sigwait, he should + * return with the pending signal. + */ + sigcounts[SIGHUP] = 0; + pthread_mutex_lock (&waiter_mutex); + /* Release the waiter from sigwait. */ + send_process_signal (SIGHUP); + sleep (1); + if (sigcounts[SIGHUP] != 1) + fprintf (stderr, "FAIL: sigwait doesn't wake up for SIGHUP.\n"); + /* + * Add SIGHUP to the process pending signals. Since there is + * a signal handler installed for SIGHUP and this signal is + * blocked from the waiter thread and unblocked in the main + * thread, the signal handler should be called once for SIGHUP. + */ + send_process_signal (SIGHUP); + /* Release the waiter thread and allow him to run. */ + pthread_mutex_unlock (&waiter_mutex); + sleep (1); + if (sigcounts[SIGHUP] != 2) + fprintf (stderr, + "FAIL: sigwait doesn't return for pending SIGHUP.\n"); + + /* + * Repeat the above test using pthread_kill and SIGUSR1. + */ + sigcounts[SIGUSR1] = 0; + pthread_mutex_lock (&waiter_mutex); + /* Release the waiter from sigwait. */ + send_thread_signal (tid, SIGUSR1); + sleep (1); + if (sigcounts[SIGUSR1] != 1) + fprintf (stderr, + "FAIL: sigwait doesn't wake up for SIGUSR1.\n"); + /* Add SIGUSR1 to the waiters pending signals. */ + send_thread_signal (tid, SIGUSR1); + /* Release the waiter thread and allow him to run. */ + pthread_mutex_unlock (&waiter_mutex); + sleep (1); + if (sigcounts[SIGUSR1] != 2) + fprintf (stderr, + "FAIL: sigwait doesn't return for pending SIGUSR1.\n"); + + /* + * Verify that we can still kill the process for a signal + * not being waited on by sigwait. + */ + send_process_signal (SIGPIPE); + fprintf (stderr, "FAIL: SIGPIPE did not terminate process.\n"); + + /* + * Wait for the thread to finish. + */ + pthread_join (tid, &exit_status); + + return (0); +} diff --git a/lib/libpthread/test/sigwait_d.exp b/lib/libpthread/test/sigwait_d.exp new file mode 100644 index 0000000..2e9b2c4 --- /dev/null +++ b/lib/libpthread/test/sigwait_d.exp @@ -0,0 +1,10 @@ +Sigwait caught signal 16 +Sigwait caught signal 16 +Sigwait caught signal 30 +Sigwait caught signal 30 +Sigwait caught signal 1 +Sigwait caught signal 1 +Sigwait caught signal 1 + -> Signal handler caught signal 1 +Sigwait caught signal 30 +Sigwait caught signal 30 diff --git a/lib/libpthread/test/verify b/lib/libpthread/test/verify new file mode 100755 index 0000000..2863e5c --- /dev/null +++ b/lib/libpthread/test/verify @@ -0,0 +1,474 @@ +#!/usr/bin/perl -w +#-*-mode:perl-*- +############################################################################# +# +# Copyright (C) 1999-2001 Jason Evans <jasone@freebsd.org>. +# 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(s), this list of conditions and the following disclaimer as +# the first lines of this file unmodified other than the possible +# addition of one or more copyright notices. +# 2. Redistributions in binary form must reproduce the above copyright +# notice(s), this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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. +# +############################################################################# +# +# Test harness. +# +# $FreeBSD$ +# +############################################################################# + +# Shut off buffering. +select(STDOUT); +$| = 1; + +# +# Parse command-line arguments. +# +use Getopt::Long; +Getopt::Long::config("bundling"); # Allow -hv rather than forcing -h -v. + +# Set option defaults for optional arguments. +$opt_help = 0; +$opt_verbose = 0; +$opt_quiet = 0; +$opt_srcdir = "."; +$opt_objdir = "."; +$opt_ustats = 0; +$opt_zero = 0; + +$opt_retval = +&GetOptions("h|help" => \$opt_help, + "v|verbose" => \$opt_verbose, + "q|quiet" => \$opt_quiet, + "s|srcdir=s" => \$opt_srcdir, + "o|objdir=s" => \$opt_objdir, + "u|ustats" => \$opt_ustats, + "z|zero" => \$opt_zero + ); + +if ($opt_help) +{ + &usage(); + exit(0); +} + +if ($opt_retval == 0) +{ + &usage(); + exit 1; +} + +if ($opt_verbose && $opt_quiet) +{ + print STDERR "-v and -q are incompatible\n"; + &usage(); + exit 1; +} + +if ($#ARGV + 1 == 0) +{ + print STDERR "No tests specified\n"; + &usage(); + exit 1; +} + +if ($opt_verbose) +{ + print STDERR "Option values: h:$opt_help, v:$opt_verbose, " + . "s:\"$opt_srcdir\", o:\"$opt_objdir\" " + . "q:$opt_quiet, u:$opt_ustats, z:$opt_zero\n"; + printf STDERR "Tests (%d total): @ARGV\n", $#ARGV + 1; +} + +# +# Create and print header. +# +@TSTATS = +( + "--------------------------------------------------------------------------\n", + "Test c_user c_system c_total chng\n", + " passed/FAILED h_user h_system h_total %% chng\n" + ); + +if (!$opt_quiet) +{ + foreach $line (@TSTATS) + { + printf STDOUT "$line"; + } +} + +# +# Run sequence test(s). +# +$total_utime = 0.0; # Total user time. +$total_stime = 0.0; # Total system time. +$total_hutime = 0.0; # Total historical user time. +$total_hstime = 0.0; # Total historical system time. +$total_ntime = 0.0; # Total time for tests that have historical data. + +foreach $test (@ARGV) +{ + # Strip out any whitespace in $test. + $test =~ s/^\s*(.*)\s*$/$1/; + + $okay = 1; + + if (-e "$opt_srcdir/$test.exp") + { + # Diff mode. + + ($okay, $utime, $stime) = &run_test($test); + + if (-e "$opt_objdir/$test.out") + { + `diff $opt_srcdir/$test.exp $opt_objdir/$test.out > $opt_objdir/$test.diff 2>&1`; + if ($?) + { + # diff returns non-zero if there is a difference. + $okay = 0; + } + } + else + { + $okay = 0; + if ($opt_verbose) + { + print STDERR + "Nonexistent output file \"$opt_objdir/$test.out\"\n"; + } + } + + ($hutime, $hstime) = &print_stats($test, $okay, 0, 0, $utime, $stime); + } + else + { + # Sequence mode. + + ($okay, $utime, $stime) = &run_test($test); + + if (open (STEST_OUT, "<$opt_objdir/$test.out")) + { + $num_subtests = 0; + $num_failed_subtests = 0; + + while (defined($line = <STEST_OUT>)) + { + if ($line =~ /1\.\.(\d+)/) + { + $num_subtests = $1; + last; + } + } + if ($num_subtests == 0) + { + $okay = 0; + if ($opt_verbose) + { + print STDERR "Malformed or missing 1..n line\n"; + } + } + else + { + for ($subtest = 1; $subtest <= $num_subtests; $subtest++) + { + while (defined($line = <STEST_OUT>)) + { + if ($line =~ /^not\s+ok\s+(\d+)?/) + { + $not = 1; + $test_num = $1; + last; + } + elsif ($line =~ /^ok\s+(\d+)?/) + { + $not = 0; + $test_num = $1; + last; + } + } + if (defined($line)) + { + if (defined($test_num) && ($test_num != $subtest)) + { + # There was no output printed for one or more tests. + for (; $subtest < $test_num; $subtest++) + { + $num_failed_subtests++; + } + } + if ($not) + { + $num_failed_subtests++; + } + } + else + { + for (; $subtest <= $num_subtests; $subtest++) + { + $num_failed_subtests++; + } + } + } + + if (0 < $num_failed_subtests) + { + $okay = 0; + } + } + } + else + { + if (!$opt_quiet) + { + print STDERR "Cannot open output file \"$opt_objdir/$test.out\"\n"; + } + exit 1; + } + + ($hutime, $hstime) = &print_stats($test, $okay, + $num_failed_subtests, $num_subtests, + $utime, $stime); + } + + $total_hutime += $hutime; + $total_hstime += $hstime; + + if ($okay) + { + $total_utime += $utime; + $total_stime += $stime; + } + else + { + @FAILED_TESTS = (@FAILED_TESTS, $test); + } + + # If there were historical data, add the run time to the total time to + # compare against the historical run time. + if (0 < ($hutime + $hstime)) + { + $total_ntime += $utime + $stime; + } +} + +# Print summary stats. +$tt_str = sprintf ("%d / %d passed (%5.2f%%%%)", + ($#ARGV + 1) - ($#FAILED_TESTS + 1), + $#ARGV + 1, + (($#ARGV + 1) - ($#FAILED_TESTS + 1)) + / ($#ARGV + 1) * 100); + +$t_str = sprintf ("Totals %7.2f %7.2f %7.2f" + . " %7.2f\n" + . " %s %7.2f %7.2f %7.2f %7.2f%%%%\n", + $total_utime, $total_stime, $total_utime + $total_stime, + ($total_ntime - ($total_hutime + $total_hstime)), + $tt_str . ' ' x (40 - length($tt_str)), + $total_hutime, $total_hstime, $total_hutime + $total_hstime, + ($total_hutime + $total_hstime == 0.0) ? 0.0 : + (($total_ntime + - ($total_hutime + $total_hstime)) + / ($total_hutime + $total_hstime) * 100)); + +@TSTATS = ("--------------------------------------------------------------------------\n", + $t_str, + "--------------------------------------------------------------------------\n" + ); +if (!$opt_quiet) +{ + foreach $line (@TSTATS) + { + printf STDOUT "$line"; + } +} + +if ($#FAILED_TESTS >= 0) +{ + # One or more tests failed, so return an error. + exit 1; +} +# End of main execution. + +sub run_test +{ + my ($test) = @_; + my ($okay) = 1; + my ($tutime, $tstime); + my ($utime, $stime, $cutime, $cstime); + my (@TSTATS, @TPATH); + my ($t_str); + my ($srcdir, $objdir); + + # Get the path component of $test, if any. + @TPATH = split(/\//, $test); + pop(@TPATH); + $srcdir = join('/', ($opt_srcdir, @TPATH)); + $objdir = join('/', ($opt_objdir, @TPATH)); + + @TSTATS = ("--------------------------------------------------------------------------\n"); + + $t_str = sprintf ("%s%s", $test, ' ' x (40 - length($test))); + @TSTATS = (@TSTATS, $t_str); + @STATS = (@STATS, @TSTATS); + if (!$opt_quiet) + { + foreach $line (@TSTATS) + { + printf STDOUT "$line"; + } + } + + ($utime, $stime, $cutime, $cstime) = times; + `$opt_objdir/$test $srcdir $objdir > $opt_objdir/$test.out 2>&1`; + ($utime, $stime, $tutime, $tstime) = times; + + # Subtract the before time from the after time. + $tutime -= $cutime; + $tstime -= $cstime; + + if ($opt_zero) + { + if ($?) + { + $okay = 0; + if ($opt_verbose) + { + print STDERR + "\"$opt_objdir/$test > $opt_objdir/$test.out 2>&1\" returned $?\n"; + } + } + } + + return ($okay, $tutime, $tstime); +} + +sub print_stats +{ + my ($test, $okay, $failed_subtests, $subtests, $utime, $stime) = @_; + my ($hutime, $hstime); +# my (TEST_PERF); + my (@TSTATS); + my ($t_str, $pass_str); + + $pass_str = $okay ? "passed" : "*** FAILED ***"; + if ((0 != $subtests) && (!$okay)) + { + $pass_str = $pass_str . " ($failed_subtests/$subtests failed)"; + } + $pass_str = $pass_str . ' ' x (39 - length($pass_str)); + + if (-r "$test.perf") + { + if (!open (TEST_PERF, "<$opt_objdir/$test.perf")) + { + print STDERR "Unable to open \"$opt_objdir/$test.perf\"\n"; + exit 1; + } + $_ = <TEST_PERF>; + + ($hutime, $hstime) = split; + close TEST_PERF; + + $t_str = sprintf (" %7.2f %7.2f %7.2f %7.2f\n" + . " %s %7.2f %7.2f %7.2f %7.2f%%%%\n", + $utime, $stime, $utime + $stime, + ($utime + $stime) - ($hutime + $hstime), + $pass_str, + $hutime, $hstime, $hutime + $hstime, + (($hutime + $hstime) == 0.0) ? 0.0 : + ((($utime + $stime) - ($hutime + $hstime)) + / ($hutime + $hstime) * 100)); + } + else + { + $hutime = 0.0; + $hstime = 0.0; + + $t_str = sprintf (" %7.2f %7.2f %7.2f \n" + . " %s\n", + $utime, $stime, $utime + $stime, + $pass_str); + } + @TSTATS = ($t_str); + if (!$opt_quiet) + { + foreach $line (@TSTATS) + { + printf STDOUT "$line"; + } + } + + if ($okay && $opt_ustats) + { + if (!open (TEST_PERF, ">$opt_objdir/$test.perf")) + { + if (!$opt_quiet) + { + print STDERR "Unable to update \"$opt_objdir/$test.perf\"\n"; + } + } + else + { + print TEST_PERF "$utime $stime\n"; + close TEST_PERF; + } + } + + return ($hutime, $hstime); +} + +sub usage +{ + print <<EOF; +$0 usage: + $0 [<options>] <test>+ + + Option | Description + --------------+------------------------------------------------------------- + -h --help | Print usage and exit. + -v --verbose | Verbose (incompatible with quiet). + -q --quiet | Quiet (incompatible with verbose). + -s --srcdir | Path to source tree (default is "."). + -o --objdir | Path to object tree (default is "."). + -u --ustats | Update historical statistics (stored in "<test>.perf". + -z --zero | Consider non-zero exit code to be an error. + --------------+------------------------------------------------------------- + + If <test>.exp exists, <test>'s output is diff'ed with <test>.exp. Any + difference is considered failure. + + If <test>.exp does not exist, output to stdout of the following form is + expected: + + 1..<n> + {not }ok[ 1] + {not }ok[ 2] + ... + {not }ok[ n] + + 1 <= <n> < 2^31 + + Lines which do not match the patterns shown above are ignored. +EOF +} diff --git a/lib/libpthread/thread/Makefile.inc b/lib/libpthread/thread/Makefile.inc new file mode 100644 index 0000000..561be93 --- /dev/null +++ b/lib/libpthread/thread/Makefile.inc @@ -0,0 +1,117 @@ +# $FreeBSD$ + +# thr sources +.PATH: ${.CURDIR}/thread + +SRCS+= \ + thr_accept.c \ + thr_aio_suspend.c \ + thr_atfork.c \ + thr_attr_destroy.c \ + thr_attr_init.c \ + thr_attr_get_np.c \ + thr_attr_getdetachstate.c \ + thr_attr_getguardsize.c \ + thr_attr_getinheritsched.c \ + thr_attr_getschedparam.c \ + thr_attr_getschedpolicy.c \ + thr_attr_getscope.c \ + thr_attr_getstack.c \ + thr_attr_getstackaddr.c \ + thr_attr_getstacksize.c \ + thr_attr_setcreatesuspend_np.c \ + thr_attr_setdetachstate.c \ + thr_attr_setguardsize.c \ + thr_attr_setinheritsched.c \ + thr_attr_setschedparam.c \ + thr_attr_setschedpolicy.c \ + thr_attr_setscope.c \ + thr_attr_setstack.c \ + thr_attr_setstackaddr.c \ + thr_attr_setstacksize.c \ + thr_autoinit.c \ + thr_barrier.c \ + thr_barrierattr.c \ + thr_cancel.c \ + thr_clean.c \ + thr_close.c \ + thr_concurrency.c \ + thr_cond.c \ + thr_condattr_destroy.c \ + thr_condattr_init.c \ + thr_condattr_pshared.c \ + thr_connect.c \ + thr_creat.c \ + thr_create.c \ + thr_detach.c \ + thr_equal.c \ + thr_execve.c \ + thr_exit.c \ + thr_fcntl.c \ + thr_find_thread.c \ + thr_fork.c \ + thr_fsync.c \ + thr_getprio.c \ + thr_getschedparam.c \ + thr_info.c \ + thr_init.c \ + thr_join.c \ + thr_kern.c \ + thr_kill.c \ + thr_main_np.c \ + thr_mattr_init.c \ + thr_mattr_kind_np.c \ + thr_mattr_pshared.c \ + thr_msync.c \ + thr_multi_np.c \ + thr_mutex.c \ + thr_mutex_prioceiling.c \ + thr_mutex_protocol.c \ + thr_mutexattr_destroy.c \ + thr_nanosleep.c \ + thr_once.c \ + thr_open.c \ + thr_pause.c \ + thr_poll.c \ + thr_printf.c \ + thr_priority_queue.c \ + thr_pselect.c \ + thr_pspinlock.c \ + thr_raise.c \ + thr_read.c \ + thr_readv.c \ + thr_resume_np.c \ + thr_rtld.c \ + thr_rwlock.c \ + thr_rwlockattr.c \ + thr_select.c \ + thr_self.c \ + thr_sem.c \ + thr_seterrno.c \ + thr_setprio.c \ + thr_setschedparam.c \ + thr_sig.c \ + thr_sigaction.c \ + thr_sigaltstack.c \ + thr_sigmask.c \ + thr_sigpending.c \ + thr_sigprocmask.c \ + thr_sigsuspend.c \ + thr_sigwait.c \ + thr_single_np.c \ + thr_sleep.c \ + thr_spec.c \ + thr_spinlock.c \ + thr_stack.c \ + thr_suspend_np.c \ + thr_switch_np.c \ + thr_system.c \ + thr_symbols.c \ + thr_tcdrain.c \ + thr_vfork.c \ + thr_wait.c \ + thr_wait4.c \ + thr_waitpid.c \ + thr_write.c \ + thr_writev.c \ + thr_yield.c diff --git a/lib/libpthread/thread/thr_accept.c b/lib/libpthread/thread/thr_accept.c new file mode 100644 index 0000000..fc60fb4 --- /dev/null +++ b/lib/libpthread/thread/thr_accept.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2003 Daniel Eischen <deischen@freebsd.org>. + * 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. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/socket.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(__accept); +LT10_COMPAT_DEFAULT(accept); + +__weak_reference(__accept, accept); + +int +__accept(int s, struct sockaddr *addr, socklen_t *addrlen) +{ + struct pthread *curthread; + int ret; + + curthread = _get_curthread(); + _thr_cancel_enter(curthread); + ret = __sys_accept(s, addr, addrlen); + _thr_cancel_leave(curthread, ret == -1); + + return (ret); +} diff --git a/lib/libpthread/thread/thr_aio_suspend.c b/lib/libpthread/thread/thr_aio_suspend.c new file mode 100644 index 0000000..43a2414 --- /dev/null +++ b/lib/libpthread/thread/thr_aio_suspend.c @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>. + * 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(s), this list of conditions and the following disclaimer as + * the first lines of this file unmodified other than the possible + * addition of one or more copyright notices. + * 2. Redistributions in binary form must reproduce the above copyright + * notice(s), this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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. + * + * $FreeBSD$ + */ + +#include <aio.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_aio_suspend); +LT10_COMPAT_DEFAULT(aio_suspend); + +__weak_reference(_aio_suspend, aio_suspend); + +int +_aio_suspend(const struct aiocb * const iocbs[], int niocb, const struct + timespec *timeout) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_cancel_enter(curthread); + ret = __sys_aio_suspend(iocbs, niocb, timeout); + _thr_cancel_leave(curthread, 1); + + return (ret); +} + diff --git a/lib/libpthread/thread/thr_atfork.c b/lib/libpthread/thread/thr_atfork.c new file mode 100644 index 0000000..a741329 --- /dev/null +++ b/lib/libpthread/thread/thr_atfork.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2003 Daniel Eischen <deischen@freebsd.org> + * 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. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <errno.h> +#include <stdlib.h> +#include <pthread.h> +#include <sys/queue.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_atfork); +LT10_COMPAT_DEFAULT(pthread_atfork); + +__weak_reference(_pthread_atfork, pthread_atfork); + +int +_pthread_atfork(void (*prepare)(void), void (*parent)(void), + void (*child)(void)) +{ + struct pthread_atfork *af; + + if (_thr_initial == NULL) + _libpthread_init(NULL); + + if ((af = malloc(sizeof(struct pthread_atfork))) == NULL) + return (ENOMEM); + + af->prepare = prepare; + af->parent = parent; + af->child = child; + _pthread_mutex_lock(&_thr_atfork_mutex); + TAILQ_INSERT_TAIL(&_thr_atfork_list, af, qe); + _pthread_mutex_unlock(&_thr_atfork_mutex); + return (0); +} + diff --git a/lib/libpthread/thread/thr_attr_destroy.c b/lib/libpthread/thread/thr_attr_destroy.c new file mode 100644 index 0000000..4442584 --- /dev/null +++ b/lib/libpthread/thread/thr_attr_destroy.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 1996 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <stdlib.h> +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_attr_destroy); +LT10_COMPAT_DEFAULT(pthread_attr_destroy); + +__weak_reference(_pthread_attr_destroy, pthread_attr_destroy); + +int +_pthread_attr_destroy(pthread_attr_t *attr) +{ + int ret; + + /* Check for invalid arguments: */ + if (attr == NULL || *attr == NULL) + /* Invalid argument: */ + ret = EINVAL; + else { + /* Free the memory allocated to the attribute object: */ + free(*attr); + + /* + * Leave the attribute pointer NULL now that the memory + * has been freed: + */ + *attr = NULL; + ret = 0; + } + return(ret); +} diff --git a/lib/libpthread/thread/thr_attr_get_np.c b/lib/libpthread/thread/thr_attr_get_np.c new file mode 100644 index 0000000..a63088f --- /dev/null +++ b/lib/libpthread/thread/thr_attr_get_np.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2002,2003 Alexey Zelkin <phantom@FreeBSD.org> + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <errno.h> +#include <string.h> +#include <pthread.h> +#include <pthread_np.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_attr_get_np); +LT10_COMPAT_DEFAULT(pthread_attr_get_np); + +__weak_reference(_pthread_attr_get_np, pthread_attr_get_np); + +int +_pthread_attr_get_np(pthread_t pid, pthread_attr_t *dst) +{ + struct pthread *curthread; + struct pthread_attr attr; + int ret; + + if (pid == NULL || dst == NULL || *dst == NULL) + return (EINVAL); + + curthread = _get_curthread(); + if ((ret = _thr_ref_add(curthread, pid, /*include dead*/0)) != 0) + return (ret); + attr = pid->attr; + _thr_ref_delete(curthread, pid); + memcpy(*dst, &attr, sizeof(struct pthread_attr)); + + return (0); +} diff --git a/lib/libpthread/thread/thr_attr_getdetachstate.c b/lib/libpthread/thread/thr_attr_getdetachstate.c new file mode 100644 index 0000000..e2ff6bd --- /dev/null +++ b/lib/libpthread/thread/thr_attr_getdetachstate.c @@ -0,0 +1,62 @@ +/* + * Copyright (c) 1997 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_attr_getdetachstate); +LT10_COMPAT_DEFAULT(pthread_attr_getdetachstate); + +__weak_reference(_pthread_attr_getdetachstate, pthread_attr_getdetachstate); + +int +_pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate) +{ + int ret; + + /* Check for invalid arguments: */ + if (attr == NULL || *attr == NULL || detachstate == NULL) + ret = EINVAL; + else { + /* Check if the detached flag is set: */ + if ((*attr)->flags & PTHREAD_DETACHED) + /* Return detached: */ + *detachstate = PTHREAD_CREATE_DETACHED; + else + /* Return joinable: */ + *detachstate = PTHREAD_CREATE_JOINABLE; + ret = 0; + } + return(ret); +} diff --git a/lib/libpthread/thread/thr_attr_getguardsize.c b/lib/libpthread/thread/thr_attr_getguardsize.c new file mode 100644 index 0000000..351015c --- /dev/null +++ b/lib/libpthread/thread/thr_attr_getguardsize.c @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2001 Jason Evans <jasone@freebsd.org>. + * 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(s), this list of conditions and the following disclaimer + * unmodified other than the allowable addition of one or more + * copyright notices. + * 2. Redistributions in binary form must reproduce the above copyright + * notice(s), this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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. + * + * $FreeBSD$ + */ + +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_attr_getguardsize); +LT10_COMPAT_DEFAULT(pthread_attr_getguardsize); + +__weak_reference(_pthread_attr_getguardsize, pthread_attr_getguardsize); + +int +_pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize) +{ + int ret; + + /* Check for invalid arguments: */ + if (attr == NULL || *attr == NULL || guardsize == NULL) + ret = EINVAL; + else { + /* Return the guard size: */ + *guardsize = (*attr)->guardsize_attr; + ret = 0; + } + return(ret); +} diff --git a/lib/libpthread/thread/thr_attr_getinheritsched.c b/lib/libpthread/thread/thr_attr_getinheritsched.c new file mode 100644 index 0000000..eab19e3 --- /dev/null +++ b/lib/libpthread/thread/thr_attr_getinheritsched.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>. + * 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 Daniel Eischen. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_attr_getinheritsched); +LT10_COMPAT_DEFAULT(pthread_attr_getinheritsched); + +__weak_reference(_pthread_attr_getinheritsched, pthread_attr_getinheritsched); + +int +_pthread_attr_getinheritsched(const pthread_attr_t *attr, int *sched_inherit) +{ + int ret = 0; + + if ((attr == NULL) || (*attr == NULL)) + ret = EINVAL; + else + *sched_inherit = (*attr)->sched_inherit; + + return(ret); +} diff --git a/lib/libpthread/thread/thr_attr_getschedparam.c b/lib/libpthread/thread/thr_attr_getschedparam.c new file mode 100644 index 0000000..ca2eed3 --- /dev/null +++ b/lib/libpthread/thread/thr_attr_getschedparam.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>. + * 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 Daniel Eischen. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_attr_getschedparam); +LT10_COMPAT_DEFAULT(pthread_attr_getschedparam); + +__weak_reference(_pthread_attr_getschedparam, pthread_attr_getschedparam); + +int +_pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param) +{ + int ret = 0; + + if ((attr == NULL) || (*attr == NULL) || (param == NULL)) + ret = EINVAL; + else + param->sched_priority = (*attr)->prio; + + return(ret); +} diff --git a/lib/libpthread/thread/thr_attr_getschedpolicy.c b/lib/libpthread/thread/thr_attr_getschedpolicy.c new file mode 100644 index 0000000..dc952bd --- /dev/null +++ b/lib/libpthread/thread/thr_attr_getschedpolicy.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>. + * 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 Daniel Eischen. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_attr_getschedpolicy); +LT10_COMPAT_DEFAULT(pthread_attr_getschedpolicy); + +__weak_reference(_pthread_attr_getschedpolicy, pthread_attr_getschedpolicy); + +int +_pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy) +{ + int ret = 0; + + if ((attr == NULL) || (*attr == NULL) || (policy == NULL)) + ret = EINVAL; + else + *policy = (*attr)->sched_policy; + + return(ret); +} diff --git a/lib/libpthread/thread/thr_attr_getscope.c b/lib/libpthread/thread/thr_attr_getscope.c new file mode 100644 index 0000000..ceb198f --- /dev/null +++ b/lib/libpthread/thread/thr_attr_getscope.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>. + * 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 Daniel Eischen. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_attr_getscope); +LT10_COMPAT_DEFAULT(pthread_attr_getscope); + +__weak_reference(_pthread_attr_getscope, pthread_attr_getscope); + +int +_pthread_attr_getscope(const pthread_attr_t *attr, int *contentionscope) +{ + int ret = 0; + + if ((attr == NULL) || (*attr == NULL) || (contentionscope == NULL)) + /* Return an invalid argument: */ + ret = EINVAL; + + else + *contentionscope = (*attr)->flags & PTHREAD_SCOPE_SYSTEM ? + PTHREAD_SCOPE_SYSTEM : PTHREAD_SCOPE_PROCESS; + + return(ret); +} diff --git a/lib/libpthread/thread/thr_attr_getstack.c b/lib/libpthread/thread/thr_attr_getstack.c new file mode 100644 index 0000000..79a92cb --- /dev/null +++ b/lib/libpthread/thread/thr_attr_getstack.c @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2003 Craig Rodrigues <rodrigc@attbi.com>. + * 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 Craig Rodrigues. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CRAIG RODRIGUES 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_attr_getstack); +LT10_COMPAT_DEFAULT(pthread_attr_getstack); + +__weak_reference(_pthread_attr_getstack, pthread_attr_getstack); + +int +_pthread_attr_getstack(const pthread_attr_t * __restrict attr, + void ** __restrict stackaddr, + size_t * __restrict stacksize) +{ + int ret; + + /* Check for invalid arguments: */ + if (attr == NULL || *attr == NULL || stackaddr == NULL + || stacksize == NULL ) + ret = EINVAL; + else { + /* Return the stack address and size */ + *stackaddr = (*attr)->stackaddr_attr; + *stacksize = (*attr)->stacksize_attr; + ret = 0; + } + return(ret); +} + diff --git a/lib/libpthread/thread/thr_attr_getstackaddr.c b/lib/libpthread/thread/thr_attr_getstackaddr.c new file mode 100644 index 0000000..0d5c87c --- /dev/null +++ b/lib/libpthread/thread/thr_attr_getstackaddr.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 1997 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_attr_getstackaddr); +LT10_COMPAT_DEFAULT(pthread_attr_getstackaddr); + +__weak_reference(_pthread_attr_getstackaddr, pthread_attr_getstackaddr); + +int +_pthread_attr_getstackaddr(const pthread_attr_t *attr, void **stackaddr) +{ + int ret; + + /* Check for invalid arguments: */ + if (attr == NULL || *attr == NULL || stackaddr == NULL) + ret = EINVAL; + else { + /* Return the stack address: */ + *stackaddr = (*attr)->stackaddr_attr; + ret = 0; + } + return(ret); +} diff --git a/lib/libpthread/thread/thr_attr_getstacksize.c b/lib/libpthread/thread/thr_attr_getstacksize.c new file mode 100644 index 0000000..2636e97 --- /dev/null +++ b/lib/libpthread/thread/thr_attr_getstacksize.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 1997 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_attr_getstacksize); +LT10_COMPAT_DEFAULT(pthread_attr_getstacksize); + +__weak_reference(_pthread_attr_getstacksize, pthread_attr_getstacksize); + +int +_pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize) +{ + int ret; + + /* Check for invalid arguments: */ + if (attr == NULL || *attr == NULL || stacksize == NULL) + ret = EINVAL; + else { + /* Return the stack size: */ + *stacksize = (*attr)->stacksize_attr; + ret = 0; + } + return(ret); +} diff --git a/lib/libpthread/thread/thr_attr_init.c b/lib/libpthread/thread/thr_attr_init.c new file mode 100644 index 0000000..77f3f24 --- /dev/null +++ b/lib/libpthread/thread/thr_attr_init.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 1996 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_attr_init); +LT10_COMPAT_DEFAULT(pthread_attr_init); + +__weak_reference(_pthread_attr_init, pthread_attr_init); + +int +_pthread_attr_init(pthread_attr_t *attr) +{ + int ret; + pthread_attr_t pattr; + + /* Allocate memory for the attribute object: */ + if ((pattr = (pthread_attr_t) malloc(sizeof(struct pthread_attr))) == NULL) + /* Insufficient memory: */ + ret = ENOMEM; + else { + /* Initialise the attribute object with the defaults: */ + memcpy(pattr, &_pthread_attr_default, + sizeof(struct pthread_attr)); + pattr->guardsize_attr = _thr_guard_default; + pattr->stacksize_attr = _thr_stack_default; + + /* Return a pointer to the attribute object: */ + *attr = pattr; + ret = 0; + } + return(ret); +} diff --git a/lib/libpthread/thread/thr_attr_setcreatesuspend_np.c b/lib/libpthread/thread/thr_attr_setcreatesuspend_np.c new file mode 100644 index 0000000..aa5f878 --- /dev/null +++ b/lib/libpthread/thread/thr_attr_setcreatesuspend_np.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_attr_setcreatesuspend_np); +LT10_COMPAT_DEFAULT(pthread_attr_setcreatesuspend_np); + +__weak_reference(_pthread_attr_setcreatesuspend_np, pthread_attr_setcreatesuspend_np); + +int +_pthread_attr_setcreatesuspend_np(pthread_attr_t *attr) +{ + int ret; + + if (attr == NULL || *attr == NULL) { + ret = EINVAL; + } else { + (*attr)->suspend = THR_CREATE_SUSPENDED; + ret = 0; + } + return(ret); +} diff --git a/lib/libpthread/thread/thr_attr_setdetachstate.c b/lib/libpthread/thread/thr_attr_setdetachstate.c new file mode 100644 index 0000000..b17680b --- /dev/null +++ b/lib/libpthread/thread/thr_attr_setdetachstate.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 1997 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_attr_setdetachstate); +LT10_COMPAT_DEFAULT(pthread_attr_setdetachstate); + +__weak_reference(_pthread_attr_setdetachstate, pthread_attr_setdetachstate); + +int +_pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate) +{ + int ret; + + /* Check for invalid arguments: */ + if (attr == NULL || *attr == NULL || + (detachstate != PTHREAD_CREATE_DETACHED && + detachstate != PTHREAD_CREATE_JOINABLE)) + ret = EINVAL; + else { + /* Check if detached state: */ + if (detachstate == PTHREAD_CREATE_DETACHED) + /* Set the detached flag: */ + (*attr)->flags |= PTHREAD_DETACHED; + else + /* Reset the detached flag: */ + (*attr)->flags &= ~PTHREAD_DETACHED; + ret = 0; + } + return(ret); +} diff --git a/lib/libpthread/thread/thr_attr_setguardsize.c b/lib/libpthread/thread/thr_attr_setguardsize.c new file mode 100644 index 0000000..dedee8b --- /dev/null +++ b/lib/libpthread/thread/thr_attr_setguardsize.c @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2001 Jason Evans <jasone@freebsd.org>. + * 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(s), this list of conditions and the following disclaimer + * unmodified other than the allowable addition of one or more + * copyright notices. + * 2. Redistributions in binary form must reproduce the above copyright + * notice(s), this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_attr_setguardsize); +LT10_COMPAT_DEFAULT(pthread_attr_setguardsize); + +__weak_reference(_pthread_attr_setguardsize, pthread_attr_setguardsize); + +int +_pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize) +{ + int ret; + + /* Check for invalid arguments. */ + if (attr == NULL || *attr == NULL) + ret = EINVAL; + else { + /* Save the stack size. */ + (*attr)->guardsize_attr = guardsize; + ret = 0; + } + return(ret); +} diff --git a/lib/libpthread/thread/thr_attr_setinheritsched.c b/lib/libpthread/thread/thr_attr_setinheritsched.c new file mode 100644 index 0000000..5182b7d --- /dev/null +++ b/lib/libpthread/thread/thr_attr_setinheritsched.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>. + * 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 Daniel Eischen. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_attr_setinheritsched); +LT10_COMPAT_DEFAULT(pthread_attr_setinheritsched); + +__weak_reference(_pthread_attr_setinheritsched, pthread_attr_setinheritsched); + +int +_pthread_attr_setinheritsched(pthread_attr_t *attr, int sched_inherit) +{ + int ret = 0; + + if ((attr == NULL) || (*attr == NULL)) + ret = EINVAL; + else if (sched_inherit != PTHREAD_INHERIT_SCHED && + sched_inherit != PTHREAD_EXPLICIT_SCHED) + ret = ENOTSUP; + else + (*attr)->sched_inherit = sched_inherit; + + return(ret); +} diff --git a/lib/libpthread/thread/thr_attr_setschedparam.c b/lib/libpthread/thread/thr_attr_setschedparam.c new file mode 100644 index 0000000..61d741d --- /dev/null +++ b/lib/libpthread/thread/thr_attr_setschedparam.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>. + * 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 Daniel Eischen. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_attr_setschedparam); +LT10_COMPAT_DEFAULT(pthread_attr_setschedparam); + +__weak_reference(_pthread_attr_setschedparam, pthread_attr_setschedparam); + +int +_pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param) +{ + int ret = 0; + + if ((attr == NULL) || (*attr == NULL)) + ret = EINVAL; + else if (param == NULL) { + ret = ENOTSUP; + } else if ((param->sched_priority < THR_MIN_PRIORITY) || + (param->sched_priority > THR_MAX_PRIORITY)) { + /* Return an unsupported value error. */ + ret = ENOTSUP; + } else + (*attr)->prio = param->sched_priority; + + return(ret); +} diff --git a/lib/libpthread/thread/thr_attr_setschedpolicy.c b/lib/libpthread/thread/thr_attr_setschedpolicy.c new file mode 100644 index 0000000..fb24cb1 --- /dev/null +++ b/lib/libpthread/thread/thr_attr_setschedpolicy.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>. + * 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 Daniel Eischen. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_attr_setschedpolicy); +LT10_COMPAT_DEFAULT(pthread_attr_setschedpolicy); + +__weak_reference(_pthread_attr_setschedpolicy, pthread_attr_setschedpolicy); + +int +_pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy) +{ + int ret = 0; + + if ((attr == NULL) || (*attr == NULL)) + ret = EINVAL; + else if ((policy < SCHED_FIFO) || (policy > SCHED_RR)) { + ret = ENOTSUP; + } else + (*attr)->sched_policy = policy; + + return(ret); +} diff --git a/lib/libpthread/thread/thr_attr_setscope.c b/lib/libpthread/thread/thr_attr_setscope.c new file mode 100644 index 0000000..3cc7f16 --- /dev/null +++ b/lib/libpthread/thread/thr_attr_setscope.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>. + * 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 Daniel Eischen. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_attr_setscope); +LT10_COMPAT_DEFAULT(pthread_attr_setscope); + +__weak_reference(_pthread_attr_setscope, pthread_attr_setscope); + +int +_pthread_attr_setscope(pthread_attr_t *attr, int contentionscope) +{ + int ret = 0; + + if ((attr == NULL) || (*attr == NULL)) { + /* Return an invalid argument: */ + ret = EINVAL; + } else if ((contentionscope != PTHREAD_SCOPE_PROCESS) && + (contentionscope != PTHREAD_SCOPE_SYSTEM)) { + ret = EINVAL; + } else if (contentionscope == PTHREAD_SCOPE_SYSTEM) { + (*attr)->flags |= contentionscope; + } else { + (*attr)->flags &= ~PTHREAD_SCOPE_SYSTEM; + } + return (ret); +} diff --git a/lib/libpthread/thread/thr_attr_setstack.c b/lib/libpthread/thread/thr_attr_setstack.c new file mode 100644 index 0000000..bdccfd8 --- /dev/null +++ b/lib/libpthread/thread/thr_attr_setstack.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2003 Craig Rodrigues <rodrigc@attbi.com>. + * 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 Craig Rodrigues. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CRAIG RODRIGUES 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_attr_setstack); +LT10_COMPAT_DEFAULT(pthread_attr_setstack); + +__weak_reference(_pthread_attr_setstack, pthread_attr_setstack); + +int +_pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, + size_t stacksize) +{ + int ret; + + /* Check for invalid arguments: */ + if (attr == NULL || *attr == NULL || stackaddr == NULL + || stacksize < PTHREAD_STACK_MIN ) + ret = EINVAL; + else { + /* Save the stack address and stack size */ + (*attr)->stackaddr_attr = stackaddr; + (*attr)->stacksize_attr = stacksize; + ret = 0; + } + return(ret); +} + diff --git a/lib/libpthread/thread/thr_attr_setstackaddr.c b/lib/libpthread/thread/thr_attr_setstackaddr.c new file mode 100644 index 0000000..5f0a903 --- /dev/null +++ b/lib/libpthread/thread/thr_attr_setstackaddr.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 1997 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_attr_setstackaddr); +LT10_COMPAT_DEFAULT(pthread_attr_setstackaddr); + +__weak_reference(_pthread_attr_setstackaddr, pthread_attr_setstackaddr); + +int +_pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr) +{ + int ret; + + /* Check for invalid arguments: */ + if (attr == NULL || *attr == NULL || stackaddr == NULL) + ret = EINVAL; + else { + /* Save the stack address: */ + (*attr)->stackaddr_attr = stackaddr; + ret = 0; + } + return(ret); +} diff --git a/lib/libpthread/thread/thr_attr_setstacksize.c b/lib/libpthread/thread/thr_attr_setstacksize.c new file mode 100644 index 0000000..6fa9760 --- /dev/null +++ b/lib/libpthread/thread/thr_attr_setstacksize.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 1996 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_attr_setstacksize); +LT10_COMPAT_DEFAULT(pthread_attr_setstacksize); + +__weak_reference(_pthread_attr_setstacksize, pthread_attr_setstacksize); + +int +_pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize) +{ + int ret; + + /* Check for invalid arguments: */ + if (attr == NULL || *attr == NULL || stacksize < PTHREAD_STACK_MIN) + ret = EINVAL; + else { + /* Save the stack size: */ + (*attr)->stacksize_attr = stacksize; + ret = 0; + } + return(ret); +} diff --git a/lib/libpthread/thread/thr_autoinit.c b/lib/libpthread/thread/thr_autoinit.c new file mode 100644 index 0000000..95b2a85 --- /dev/null +++ b/lib/libpthread/thread/thr_autoinit.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2002 Alfred Perlstein <alfred@freebsd.org>. + * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +/* + * This module uses GCC extentions to initialize the + * threads package at program start-up time. + */ + +#include <pthread.h> +#include "thr_private.h" + +void _thread_init_hack(void) __attribute__ ((constructor)); + +void +_thread_init_hack(void) +{ + + _libpthread_init(NULL); +} + +/* + * For the shared version of the threads library, the above is sufficient. + * But for the archive version of the library, we need a little bit more. + * Namely, we must arrange for this particular module to be pulled in from + * the archive library at link time. To accomplish that, we define and + * initialize a variable, "_thread_autoinit_dummy_decl". This variable is + * referenced (as an extern) from libc/stdlib/exit.c. This will always + * create a need for this module, ensuring that it is present in the + * executable. + */ +extern int _thread_autoinit_dummy_decl; +int _thread_autoinit_dummy_decl = 0; diff --git a/lib/libpthread/thread/thr_barrier.c b/lib/libpthread/thread/thr_barrier.c new file mode 100644 index 0000000..c8be343 --- /dev/null +++ b/lib/libpthread/thread/thr_barrier.c @@ -0,0 +1,129 @@ +/*- + * Copyright (c) 2003 David Xu <davidxu@freebsd.org> + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include <errno.h> +#include <stdlib.h> +#include "namespace.h" +#include <pthread.h> +#include "un-namespace.h" +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_barrier_init); +LT10_COMPAT_DEFAULT(pthread_barrier_init); +LT10_COMPAT_PRIVATE(_pthread_barrier_wait); +LT10_COMPAT_DEFAULT(pthread_barrier_wait); +LT10_COMPAT_PRIVATE(_pthread_barrier_destroy); +LT10_COMPAT_DEFAULT(pthread_barrier_destroy); + +__weak_reference(_pthread_barrier_init, pthread_barrier_init); +__weak_reference(_pthread_barrier_wait, pthread_barrier_wait); +__weak_reference(_pthread_barrier_destroy, pthread_barrier_destroy); + +int +_pthread_barrier_destroy(pthread_barrier_t *barrier) +{ + pthread_barrier_t bar; + int ret, ret2; + + if (barrier == NULL || *barrier == NULL) + return (EINVAL); + + bar = *barrier; + if (bar->b_waiters > 0) + return (EBUSY); + *barrier = NULL; + ret = _pthread_mutex_destroy(&bar->b_lock); + ret2 = _pthread_cond_destroy(&bar->b_cond); + free(bar); + return (ret ? ret : ret2); +} + +int +_pthread_barrier_init(pthread_barrier_t *barrier, + const pthread_barrierattr_t *attr, unsigned count) +{ + pthread_barrier_t bar; + int ret; + + if (barrier == NULL || count <= 0) + return (EINVAL); + + bar = malloc(sizeof(struct pthread_barrier)); + if (bar == NULL) + return (ENOMEM); + + if ((ret = _pthread_mutex_init(&bar->b_lock, NULL)) != 0) { + free(bar); + return (ret); + } + + if ((ret = _pthread_cond_init(&bar->b_cond, NULL)) != 0) { + _pthread_mutex_destroy(&bar->b_lock); + free(bar); + return (ret); + } + + bar->b_waiters = 0; + bar->b_count = count; + bar->b_generation = 0; + *barrier = bar; + + return (0); +} + +int +_pthread_barrier_wait(pthread_barrier_t *barrier) +{ + int ret, gen; + pthread_barrier_t bar; + + if (barrier == NULL || *barrier == NULL) + return (EINVAL); + + bar = *barrier; + if ((ret = _pthread_mutex_lock(&bar->b_lock)) != 0) + return (ret); + + if (++bar->b_waiters == bar->b_count) { + /* Current thread is lastest thread */ + bar->b_generation++; + bar->b_waiters = 0; + ret = _pthread_cond_broadcast(&bar->b_cond); + if (ret == 0) + ret = PTHREAD_BARRIER_SERIAL_THREAD; + } else { + gen = bar->b_generation; + do { + ret = _pthread_cond_wait( + &bar->b_cond, &bar->b_lock); + /* test generation to avoid bogus wakeup */ + } while (ret == 0 && gen == bar->b_generation); + } + _pthread_mutex_unlock(&bar->b_lock); + return (ret); +} diff --git a/lib/libpthread/thread/thr_barrierattr.c b/lib/libpthread/thread/thr_barrierattr.c new file mode 100644 index 0000000..3384aee --- /dev/null +++ b/lib/libpthread/thread/thr_barrierattr.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2003 David Xu <davidxu@freebsd.org>. + * 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(s), this list of conditions and the following disclaimer as + * the first lines of this file unmodified other than the possible + * addition of one or more copyright notices. + * 2. Redistributions in binary form must reproduce the above copyright + * notice(s), this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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. + * + * $FreeBSD$ + */ + +#include <errno.h> +#include <stdlib.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_barrierattr_destroy); +LT10_COMPAT_DEFAULT(pthread_barrierattr_destroy); +LT10_COMPAT_PRIVATE(_pthread_barrierattr_init); +LT10_COMPAT_DEFAULT(pthread_barrierattr_init); +LT10_COMPAT_PRIVATE(_pthread_barrierattr_setpshared); +LT10_COMPAT_DEFAULT(pthread_barrierattr_setpshared); +LT10_COMPAT_PRIVATE(_pthread_barrierattr_getpshared); +LT10_COMPAT_DEFAULT(pthread_barrierattr_getpshared); + +__weak_reference(_pthread_barrierattr_destroy, pthread_barrierattr_destroy); +__weak_reference(_pthread_barrierattr_init, pthread_barrierattr_init); +__weak_reference(_pthread_barrierattr_setpshared, + pthread_barrierattr_setpshared); +__weak_reference(_pthread_barrierattr_getpshared, + pthread_barrierattr_getpshared); + +int +_pthread_barrierattr_destroy(pthread_barrierattr_t *attr) +{ + + if (attr == NULL || *attr == NULL) + return (EINVAL); + + free(*attr); + return (0); +} + +int +_pthread_barrierattr_getpshared(const pthread_barrierattr_t *attr, + int *pshared) +{ + + if (attr == NULL || *attr == NULL) + return (EINVAL); + + *pshared = (*attr)->pshared; + return (0); +} + +int +_pthread_barrierattr_init(pthread_barrierattr_t *attr) +{ + + if (attr == NULL) + return (EINVAL); + + if ((*attr = malloc(sizeof(struct pthread_barrierattr))) == NULL) + return (ENOMEM); + + (*attr)->pshared = PTHREAD_PROCESS_PRIVATE; + return (0); +} + +int +_pthread_barrierattr_setpshared(pthread_barrierattr_t *attr, int pshared) +{ + + if (attr == NULL || *attr == NULL) + return (EINVAL); + + /* Only PTHREAD_PROCESS_PRIVATE is supported. */ + if (pshared != PTHREAD_PROCESS_PRIVATE) + return (EINVAL); + + (*attr)->pshared = pshared; + return (0); +} diff --git a/lib/libpthread/thread/thr_cancel.c b/lib/libpthread/thread/thr_cancel.c new file mode 100644 index 0000000..bbf6fdf --- /dev/null +++ b/lib/libpthread/thread/thr_cancel.c @@ -0,0 +1,311 @@ +/* + * David Leonard <d@openbsd.org>, 1999. Public domain. + * $FreeBSD$ + */ +#include <sys/errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_cancel); +LT10_COMPAT_DEFAULT(pthread_cancel); +LT10_COMPAT_PRIVATE(_pthread_setcancelstate); +LT10_COMPAT_DEFAULT(pthread_setcancelstate); +LT10_COMPAT_PRIVATE(_pthread_setcanceltype); +LT10_COMPAT_DEFAULT(pthread_setcanceltype); +LT10_COMPAT_PRIVATE(_pthread_testcancel); +LT10_COMPAT_DEFAULT(pthread_testcancel); + +__weak_reference(_pthread_cancel, pthread_cancel); +__weak_reference(_pthread_setcancelstate, pthread_setcancelstate); +__weak_reference(_pthread_setcanceltype, pthread_setcanceltype); +__weak_reference(_pthread_testcancel, pthread_testcancel); + +static inline int +checkcancel(struct pthread *curthread) +{ + if ((curthread->cancelflags & THR_CANCELLING) != 0) { + /* + * It is possible for this thread to be swapped out + * while performing cancellation; do not allow it + * to be cancelled again. + */ + if ((curthread->flags & THR_FLAGS_EXITING) != 0) { + /* + * This may happen once, but after this, it + * shouldn't happen again. + */ + curthread->cancelflags &= ~THR_CANCELLING; + return (0); + } + if ((curthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) { + curthread->cancelflags &= ~THR_CANCELLING; + return (1); + } + } + return (0); +} + +static inline void +testcancel(struct pthread *curthread) +{ + if (checkcancel(curthread) != 0) { + /* Unlock before exiting: */ + THR_THREAD_UNLOCK(curthread, curthread); + + _thr_exit_cleanup(); + pthread_exit(PTHREAD_CANCELED); + PANIC("cancel"); + } +} + +int +_pthread_cancel(pthread_t pthread) +{ + struct pthread *curthread = _get_curthread(); + struct pthread *joinee = NULL; + struct kse_mailbox *kmbx = NULL; + int ret; + + if ((ret = _thr_ref_add(curthread, pthread, /*include dead*/0)) == 0) { + /* + * Take the thread's lock while we change the cancel flags. + */ + THR_THREAD_LOCK(curthread, pthread); + THR_SCHED_LOCK(curthread, pthread); + if (pthread->flags & THR_FLAGS_EXITING) { + THR_SCHED_UNLOCK(curthread, pthread); + THR_THREAD_UNLOCK(curthread, pthread); + _thr_ref_delete(curthread, pthread); + return (ESRCH); + } + if (((pthread->cancelflags & PTHREAD_CANCEL_DISABLE) != 0) || + (((pthread->cancelflags & THR_AT_CANCEL_POINT) == 0) && + ((pthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) == 0))) + /* Just mark it for cancellation: */ + pthread->cancelflags |= THR_CANCELLING; + else { + /* + * Check if we need to kick it back into the + * run queue: + */ + switch (pthread->state) { + case PS_RUNNING: + /* No need to resume: */ + pthread->cancelflags |= THR_CANCELLING; + break; + + case PS_LOCKWAIT: + /* + * These can't be removed from the queue. + * Just mark it as cancelling and tell it + * to yield once it leaves the critical + * region. + */ + pthread->cancelflags |= THR_CANCELLING; + pthread->critical_yield = 1; + break; + + case PS_SLEEP_WAIT: + case PS_SIGSUSPEND: + case PS_SIGWAIT: + /* Interrupt and resume: */ + pthread->interrupted = 1; + pthread->cancelflags |= THR_CANCELLING; + kmbx = _thr_setrunnable_unlocked(pthread); + break; + + case PS_JOIN: + /* Disconnect the thread from the joinee: */ + joinee = pthread->join_status.thread; + pthread->join_status.thread = NULL; + pthread->cancelflags |= THR_CANCELLING; + kmbx = _thr_setrunnable_unlocked(pthread); + if ((joinee != NULL) && + (pthread->kseg == joinee->kseg)) { + /* Remove the joiner from the joinee. */ + joinee->joiner = NULL; + joinee = NULL; + } + break; + + case PS_SUSPENDED: + case PS_MUTEX_WAIT: + case PS_COND_WAIT: + /* + * Threads in these states may be in queues. + * In order to preserve queue integrity, the + * cancelled thread must remove itself from the + * queue. Mark the thread as interrupted and + * needing cancellation, and set the state to + * running. When the thread resumes, it will + * remove itself from the queue and call the + * cancellation completion routine. + */ + pthread->interrupted = 1; + pthread->cancelflags |= THR_CANCEL_NEEDED; + kmbx = _thr_setrunnable_unlocked(pthread); + pthread->continuation = + _thr_finish_cancellation; + break; + + case PS_DEAD: + case PS_DEADLOCK: + case PS_STATE_MAX: + /* Ignore - only here to silence -Wall: */ + break; + } + if ((pthread->cancelflags & THR_AT_CANCEL_POINT) && + (pthread->blocked != 0 || + pthread->attr.flags & PTHREAD_SCOPE_SYSTEM)) + kse_thr_interrupt(&pthread->tcb->tcb_tmbx, + KSE_INTR_INTERRUPT, 0); + } + + /* + * Release the thread's lock and remove the + * reference: + */ + THR_SCHED_UNLOCK(curthread, pthread); + THR_THREAD_UNLOCK(curthread, pthread); + _thr_ref_delete(curthread, pthread); + if (kmbx != NULL) + kse_wakeup(kmbx); + + if ((joinee != NULL) && + (_thr_ref_add(curthread, joinee, /* include dead */1) == 0)) { + /* Remove the joiner from the joinee. */ + THR_SCHED_LOCK(curthread, joinee); + joinee->joiner = NULL; + THR_SCHED_UNLOCK(curthread, joinee); + _thr_ref_delete(curthread, joinee); + } + } + return (ret); +} + +int +_pthread_setcancelstate(int state, int *oldstate) +{ + struct pthread *curthread = _get_curthread(); + int ostate; + int ret; + int need_exit = 0; + + /* Take the thread's lock while fiddling with the state: */ + THR_THREAD_LOCK(curthread, curthread); + + ostate = curthread->cancelflags & PTHREAD_CANCEL_DISABLE; + + switch (state) { + case PTHREAD_CANCEL_ENABLE: + curthread->cancelflags &= ~PTHREAD_CANCEL_DISABLE; + if ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0) + need_exit = checkcancel(curthread); + ret = 0; + break; + case PTHREAD_CANCEL_DISABLE: + curthread->cancelflags |= PTHREAD_CANCEL_DISABLE; + ret = 0; + break; + default: + ret = EINVAL; + } + + THR_THREAD_UNLOCK(curthread, curthread); + if (need_exit != 0) { + _thr_exit_cleanup(); + pthread_exit(PTHREAD_CANCELED); + PANIC("cancel"); + } + if (ret == 0 && oldstate != NULL) + *oldstate = ostate; + + return (ret); +} + +int +_pthread_setcanceltype(int type, int *oldtype) +{ + struct pthread *curthread = _get_curthread(); + int otype; + int ret; + int need_exit = 0; + + /* Take the thread's lock while fiddling with the state: */ + THR_THREAD_LOCK(curthread, curthread); + + otype = curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS; + switch (type) { + case PTHREAD_CANCEL_ASYNCHRONOUS: + curthread->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS; + need_exit = checkcancel(curthread); + ret = 0; + break; + case PTHREAD_CANCEL_DEFERRED: + curthread->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS; + ret = 0; + break; + default: + ret = EINVAL; + } + + THR_THREAD_UNLOCK(curthread, curthread); + if (need_exit != 0) { + _thr_exit_cleanup(); + pthread_exit(PTHREAD_CANCELED); + PANIC("cancel"); + } + if (ret == 0 && oldtype != NULL) + *oldtype = otype; + + return (ret); +} + +void +_pthread_testcancel(void) +{ + struct pthread *curthread = _get_curthread(); + + THR_THREAD_LOCK(curthread, curthread); + testcancel(curthread); + THR_THREAD_UNLOCK(curthread, curthread); +} + +void +_thr_cancel_enter(struct pthread *thread) +{ + /* Look for a cancellation before we block: */ + THR_THREAD_LOCK(thread, thread); + testcancel(thread); + thread->cancelflags |= THR_AT_CANCEL_POINT; + THR_THREAD_UNLOCK(thread, thread); +} + +void +_thr_cancel_leave(struct pthread *thread, int check) +{ + THR_THREAD_LOCK(thread, thread); + thread->cancelflags &= ~THR_AT_CANCEL_POINT; + /* Look for a cancellation after we unblock: */ + if (check) + testcancel(thread); + THR_THREAD_UNLOCK(thread, thread); +} + +void +_thr_finish_cancellation(void *arg) +{ + struct pthread *curthread = _get_curthread(); + + curthread->continuation = NULL; + curthread->interrupted = 0; + + THR_THREAD_LOCK(curthread, curthread); + if ((curthread->cancelflags & THR_CANCEL_NEEDED) != 0) { + curthread->cancelflags &= ~THR_CANCEL_NEEDED; + THR_THREAD_UNLOCK(curthread, curthread); + _thr_exit_cleanup(); + pthread_exit(PTHREAD_CANCELED); + } + THR_THREAD_UNLOCK(curthread, curthread); +} diff --git a/lib/libpthread/thread/thr_clean.c b/lib/libpthread/thread/thr_clean.c new file mode 100644 index 0000000..4db5c93 --- /dev/null +++ b/lib/libpthread/thread/thr_clean.c @@ -0,0 +1,79 @@ +/* + * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <signal.h> +#include <errno.h> +#include <stdlib.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_cleanup_push); +LT10_COMPAT_DEFAULT(pthread_cleanup_push); +LT10_COMPAT_PRIVATE(_pthread_cleanup_pop); +LT10_COMPAT_DEFAULT(pthread_cleanup_pop); + +__weak_reference(_pthread_cleanup_push, pthread_cleanup_push); +__weak_reference(_pthread_cleanup_pop, pthread_cleanup_pop); + +void +_pthread_cleanup_push(void (*routine) (void *), void *routine_arg) +{ + struct pthread *curthread = _get_curthread(); + struct pthread_cleanup *new; + + if ((new = (struct pthread_cleanup *) + malloc(sizeof(struct pthread_cleanup))) != NULL) { + new->routine = routine; + new->routine_arg = routine_arg; + new->onstack = 0; + new->next = curthread->cleanup; + + curthread->cleanup = new; + } +} + +void +_pthread_cleanup_pop(int execute) +{ + struct pthread *curthread = _get_curthread(); + struct pthread_cleanup *old; + + if ((old = curthread->cleanup) != NULL) { + curthread->cleanup = old->next; + if (execute) { + old->routine(old->routine_arg); + } + if (old->onstack == 0) + free(old); + } +} diff --git a/lib/libpthread/thread/thr_close.c b/lib/libpthread/thread/thr_close.c new file mode 100644 index 0000000..7b4fe72 --- /dev/null +++ b/lib/libpthread/thread/thr_close.c @@ -0,0 +1,58 @@ +/* + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(__close); +LT10_COMPAT_DEFAULT(close); + +__weak_reference(__close, close); + +int +__close(int fd) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_cancel_enter(curthread); + ret = __sys_close(fd); + _thr_cancel_leave(curthread, 1); + + return (ret); +} diff --git a/lib/libpthread/thread/thr_concurrency.c b/lib/libpthread/thread/thr_concurrency.c new file mode 100644 index 0000000..74e0e11 --- /dev/null +++ b/lib/libpthread/thread/thr_concurrency.c @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2003 Daniel M. Eischen <deischen@freebsd.org> + * Copyright (c) 2003 Sergey Osokin <osa@freebsd.org.ru>. + * 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. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <errno.h> +#include <pthread.h> +#include <sys/types.h> +#include <sys/sysctl.h> + +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_getconcurrency); +LT10_COMPAT_DEFAULT(pthread_getconcurrency); +LT10_COMPAT_PRIVATE(_pthread_setconcurrency); +LT10_COMPAT_DEFAULT(pthread_setconcurrency); + +/*#define DEBUG_CONCURRENCY */ +#ifdef DEBUG_CONCURRENCY +#define DBG_MSG stdout_debug +#else +#define DBG_MSG(x...) +#endif + +static int level = 0; + +__weak_reference(_pthread_getconcurrency, pthread_getconcurrency); +__weak_reference(_pthread_setconcurrency, pthread_setconcurrency); + +int +_pthread_getconcurrency(void) +{ + return (level); +} + +int +_pthread_setconcurrency(int new_level) +{ + int ret; + + if (new_level < 0) + ret = EINVAL; + else if (new_level == level) + ret = 0; + else if (new_level == 0) { + level = 0; + ret = 0; + } else if ((_kse_isthreaded() == 0) && (_kse_setthreaded(1) != 0)) { + DBG_MSG("Can't enable threading.\n"); + ret = EAGAIN; + } else { + ret = _thr_setconcurrency(new_level); + if (ret == 0) + level = new_level; + } + return (ret); +} + +int +_thr_setconcurrency(int new_level) +{ + struct pthread *curthread; + struct kse *newkse, *kse; + kse_critical_t crit; + int kse_count; + int i; + int ret; + + /* + * Turn on threaded mode, if failed, it is unnecessary to + * do further work. + */ + if (_kse_isthreaded() == 0 && _kse_setthreaded(1)) + return (EAGAIN); + + ret = 0; + curthread = _get_curthread(); + /* Race condition, but so what. */ + kse_count = _kse_initial->k_kseg->kg_ksecount; + if (new_level > kse_count) { + for (i = kse_count; i < new_level; i++) { + newkse = _kse_alloc(curthread, 0); + if (newkse == NULL) { + DBG_MSG("Can't alloc new KSE.\n"); + ret = EAGAIN; + break; + } + newkse->k_kseg = _kse_initial->k_kseg; + newkse->k_schedq = _kse_initial->k_schedq; + newkse->k_curthread = NULL; + crit = _kse_critical_enter(); + KSE_SCHED_LOCK(curthread->kse, newkse->k_kseg); + TAILQ_INSERT_TAIL(&newkse->k_kseg->kg_kseq, + newkse, k_kgqe); + newkse->k_kseg->kg_ksecount++; + newkse->k_flags |= KF_STARTED; + KSE_SCHED_UNLOCK(curthread->kse, newkse->k_kseg); + if (kse_create(&newkse->k_kcb->kcb_kmbx, 0) != 0) { + KSE_SCHED_LOCK(curthread->kse, newkse->k_kseg); + TAILQ_REMOVE(&newkse->k_kseg->kg_kseq, + newkse, k_kgqe); + newkse->k_kseg->kg_ksecount--; + KSE_SCHED_UNLOCK(curthread->kse, + newkse->k_kseg); + _kse_critical_leave(crit); + _kse_free(curthread, newkse); + DBG_MSG("kse_create syscall failed.\n"); + ret = EAGAIN; + break; + } else { + _kse_critical_leave(crit); + } + } + } else if (new_level < kse_count) { + kse_count = 0; + crit = _kse_critical_enter(); + KSE_SCHED_LOCK(curthread->kse, _kse_initial->k_kseg); + /* Count the number of active KSEs */ + TAILQ_FOREACH(kse, &_kse_initial->k_kseg->kg_kseq, k_kgqe) { + if ((kse->k_flags & KF_TERMINATED) == 0) + kse_count++; + } + /* Reduce the number of active KSEs appropriately. */ + kse = TAILQ_FIRST(&_kse_initial->k_kseg->kg_kseq); + while ((kse != NULL) && (kse_count > new_level)) { + if ((kse != _kse_initial) && + ((kse->k_flags & KF_TERMINATED) == 0)) { + kse->k_flags |= KF_TERMINATED; + kse_count--; + /* Wakup the KSE in case it is idle. */ + kse_wakeup(&kse->k_kcb->kcb_kmbx); + } + kse = TAILQ_NEXT(kse, k_kgqe); + } + KSE_SCHED_UNLOCK(curthread->kse, _kse_initial->k_kseg); + _kse_critical_leave(crit); + } + return (ret); +} + +int +_thr_setmaxconcurrency(void) +{ + int vcpu; + size_t len; + int ret; + + len = sizeof(vcpu); + ret = sysctlbyname("kern.threads.virtual_cpu", &vcpu, &len, NULL, 0); + if (ret == 0 && vcpu > 0) + ret = _thr_setconcurrency(vcpu); + return (ret); +} + diff --git a/lib/libpthread/thread/thr_cond.c b/lib/libpthread/thread/thr_cond.c new file mode 100644 index 0000000..e39ae63 --- /dev/null +++ b/lib/libpthread/thread/thr_cond.c @@ -0,0 +1,847 @@ +/* + * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(__pthread_cond_wait); +LT10_COMPAT_PRIVATE(_pthread_cond_wait); +LT10_COMPAT_DEFAULT(pthread_cond_wait); +LT10_COMPAT_PRIVATE(__pthread_cond_timedwait); +LT10_COMPAT_PRIVATE(_pthread_cond_timedwait); +LT10_COMPAT_DEFAULT(pthread_cond_timedwait); +LT10_COMPAT_PRIVATE(_pthread_cond_init); +LT10_COMPAT_DEFAULT(pthread_cond_init); +LT10_COMPAT_PRIVATE(_pthread_cond_destroy); +LT10_COMPAT_DEFAULT(pthread_cond_destroy); +LT10_COMPAT_PRIVATE(_pthread_cond_signal); +LT10_COMPAT_DEFAULT(pthread_cond_signal); +LT10_COMPAT_PRIVATE(_pthread_cond_broadcast); +LT10_COMPAT_DEFAULT(pthread_cond_broadcast); + +#define THR_IN_CONDQ(thr) (((thr)->sflags & THR_FLAGS_IN_SYNCQ) != 0) +#define THR_CONDQ_SET(thr) (thr)->sflags |= THR_FLAGS_IN_SYNCQ +#define THR_CONDQ_CLEAR(thr) (thr)->sflags &= ~THR_FLAGS_IN_SYNCQ + +/* + * Prototypes + */ +static inline struct pthread *cond_queue_deq(pthread_cond_t); +static inline void cond_queue_remove(pthread_cond_t, pthread_t); +static inline void cond_queue_enq(pthread_cond_t, pthread_t); +static void cond_wait_backout(void *); +static inline void check_continuation(struct pthread *, + struct pthread_cond *, pthread_mutex_t *); + +/* + * Double underscore versions are cancellation points. Single underscore + * versions are not and are provided for libc internal usage (which + * shouldn't introduce cancellation points). + */ +__weak_reference(__pthread_cond_wait, pthread_cond_wait); +__weak_reference(__pthread_cond_timedwait, pthread_cond_timedwait); + +__weak_reference(_pthread_cond_init, pthread_cond_init); +__weak_reference(_pthread_cond_destroy, pthread_cond_destroy); +__weak_reference(_pthread_cond_signal, pthread_cond_signal); +__weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast); + + +int +_pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr) +{ + enum pthread_cond_type type; + pthread_cond_t pcond; + int flags; + int rval = 0; + + if (cond == NULL) + rval = EINVAL; + else { + /* + * Check if a pointer to a condition variable attribute + * structure was passed by the caller: + */ + if (cond_attr != NULL && *cond_attr != NULL) { + /* Default to a fast condition variable: */ + type = (*cond_attr)->c_type; + flags = (*cond_attr)->c_flags; + } else { + /* Default to a fast condition variable: */ + type = COND_TYPE_FAST; + flags = 0; + } + + /* Process according to condition variable type: */ + switch (type) { + /* Fast condition variable: */ + case COND_TYPE_FAST: + /* Nothing to do here. */ + break; + + /* Trap invalid condition variable types: */ + default: + /* Return an invalid argument error: */ + rval = EINVAL; + break; + } + + /* Check for no errors: */ + if (rval == 0) { + if ((pcond = (pthread_cond_t) + malloc(sizeof(struct pthread_cond))) == NULL) { + rval = ENOMEM; + } else if (_lock_init(&pcond->c_lock, LCK_ADAPTIVE, + _thr_lock_wait, _thr_lock_wakeup) != 0) { + free(pcond); + rval = ENOMEM; + } else { + /* + * Initialise the condition variable + * structure: + */ + TAILQ_INIT(&pcond->c_queue); + pcond->c_flags = COND_FLAGS_INITED; + pcond->c_type = type; + pcond->c_mutex = NULL; + pcond->c_seqno = 0; + *cond = pcond; + } + } + } + /* Return the completion status: */ + return (rval); +} + +int +_pthread_cond_destroy(pthread_cond_t *cond) +{ + struct pthread_cond *cv; + struct pthread *curthread = _get_curthread(); + int rval = 0; + + if (cond == NULL || *cond == NULL) + rval = EINVAL; + else { + /* Lock the condition variable structure: */ + THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); + + /* + * NULL the caller's pointer now that the condition + * variable has been destroyed: + */ + cv = *cond; + *cond = NULL; + + /* Unlock the condition variable structure: */ + THR_LOCK_RELEASE(curthread, &cv->c_lock); + + /* Free the cond lock structure: */ + _lock_destroy(&cv->c_lock); + + /* + * Free the memory allocated for the condition + * variable structure: + */ + free(cv); + + } + /* Return the completion status: */ + return (rval); +} + +int +_pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) +{ + struct pthread *curthread = _get_curthread(); + int rval = 0; + int done = 0; + int mutex_locked = 1; + int seqno; + + if (cond == NULL) + return (EINVAL); + + /* + * If the condition variable is statically initialized, + * perform the dynamic initialization: + */ + if (*cond == NULL && + (rval = pthread_cond_init(cond, NULL)) != 0) + return (rval); + + if (!_kse_isthreaded()) + _kse_setthreaded(1); + + /* + * Enter a loop waiting for a condition signal or broadcast + * to wake up this thread. A loop is needed in case the waiting + * thread is interrupted by a signal to execute a signal handler. + * It is not (currently) possible to remain in the waiting queue + * while running a handler. Instead, the thread is interrupted + * and backed out of the waiting queue prior to executing the + * signal handler. + */ + + /* Lock the condition variable structure: */ + THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); + seqno = (*cond)->c_seqno; + do { + /* + * If the condvar was statically allocated, properly + * initialize the tail queue. + */ + if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) { + TAILQ_INIT(&(*cond)->c_queue); + (*cond)->c_flags |= COND_FLAGS_INITED; + } + + /* Process according to condition variable type: */ + switch ((*cond)->c_type) { + /* Fast condition variable: */ + case COND_TYPE_FAST: + if ((mutex == NULL) || (((*cond)->c_mutex != NULL) && + ((*cond)->c_mutex != *mutex))) { + /* Return invalid argument error: */ + rval = EINVAL; + } else { + /* Reset the timeout and interrupted flags: */ + curthread->timeout = 0; + curthread->interrupted = 0; + + /* + * Queue the running thread for the condition + * variable: + */ + cond_queue_enq(*cond, curthread); + + /* Wait forever: */ + curthread->wakeup_time.tv_sec = -1; + + /* Unlock the mutex: */ + if (mutex_locked && + ((rval = _mutex_cv_unlock(mutex)) != 0)) { + /* + * Cannot unlock the mutex, so remove + * the running thread from the condition + * variable queue: + */ + cond_queue_remove(*cond, curthread); + } + else { + /* Remember the mutex: */ + (*cond)->c_mutex = *mutex; + + /* + * Don't unlock the mutex the next + * time through the loop (if the + * thread has to be requeued after + * handling a signal). + */ + mutex_locked = 0; + + /* + * This thread is active and is in a + * critical region (holding the cv + * lock); we should be able to safely + * set the state. + */ + THR_SCHED_LOCK(curthread, curthread); + THR_SET_STATE(curthread, PS_COND_WAIT); + + /* Remember the CV: */ + curthread->data.cond = *cond; + curthread->sigbackout = cond_wait_backout; + THR_SCHED_UNLOCK(curthread, curthread); + + /* Unlock the CV structure: */ + THR_LOCK_RELEASE(curthread, + &(*cond)->c_lock); + + /* Schedule the next thread: */ + _thr_sched_switch(curthread); + + /* + * XXX - This really isn't a good check + * since there can be more than one + * thread waiting on the CV. Signals + * sent to threads waiting on mutexes + * or CVs should really be deferred + * until the threads are no longer + * waiting, but POSIX says that signals + * should be sent "as soon as possible". + */ + done = (seqno != (*cond)->c_seqno); + if (done && !THR_IN_CONDQ(curthread)) { + /* + * The thread is dequeued, so + * it is safe to clear these. + */ + curthread->data.cond = NULL; + curthread->sigbackout = NULL; + check_continuation(curthread, + NULL, mutex); + return (_mutex_cv_lock(mutex)); + } + + /* Relock the CV structure: */ + THR_LOCK_ACQUIRE(curthread, + &(*cond)->c_lock); + + /* + * Clear these after taking the lock to + * prevent a race condition where a + * signal can arrive before dequeueing + * the thread. + */ + curthread->data.cond = NULL; + curthread->sigbackout = NULL; + done = (seqno != (*cond)->c_seqno); + + if (THR_IN_CONDQ(curthread)) { + cond_queue_remove(*cond, + curthread); + + /* Check for no more waiters: */ + if (TAILQ_EMPTY(&(*cond)->c_queue)) + (*cond)->c_mutex = NULL; + } + } + } + break; + + /* Trap invalid condition variable types: */ + default: + /* Return an invalid argument error: */ + rval = EINVAL; + break; + } + + check_continuation(curthread, *cond, + mutex_locked ? NULL : mutex); + } while ((done == 0) && (rval == 0)); + + /* Unlock the condition variable structure: */ + THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); + + if (mutex_locked == 0) + _mutex_cv_lock(mutex); + + /* Return the completion status: */ + return (rval); +} + +__strong_reference(_pthread_cond_wait, _thr_cond_wait); + +int +__pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_cancel_enter(curthread); + ret = _pthread_cond_wait(cond, mutex); + _thr_cancel_leave(curthread, 1); + return (ret); +} + +int +_pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, + const struct timespec * abstime) +{ + struct pthread *curthread = _get_curthread(); + int rval = 0; + int done = 0; + int mutex_locked = 1; + int seqno; + + THR_ASSERT(curthread->locklevel == 0, + "cv_timedwait: locklevel is not zero!"); + + if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || + abstime->tv_nsec >= 1000000000) + return (EINVAL); + /* + * If the condition variable is statically initialized, perform dynamic + * initialization. + */ + if (*cond == NULL && (rval = pthread_cond_init(cond, NULL)) != 0) + return (rval); + + if (!_kse_isthreaded()) + _kse_setthreaded(1); + + /* + * Enter a loop waiting for a condition signal or broadcast + * to wake up this thread. A loop is needed in case the waiting + * thread is interrupted by a signal to execute a signal handler. + * It is not (currently) possible to remain in the waiting queue + * while running a handler. Instead, the thread is interrupted + * and backed out of the waiting queue prior to executing the + * signal handler. + */ + + /* Lock the condition variable structure: */ + THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); + seqno = (*cond)->c_seqno; + do { + /* + * If the condvar was statically allocated, properly + * initialize the tail queue. + */ + if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) { + TAILQ_INIT(&(*cond)->c_queue); + (*cond)->c_flags |= COND_FLAGS_INITED; + } + + /* Process according to condition variable type: */ + switch ((*cond)->c_type) { + /* Fast condition variable: */ + case COND_TYPE_FAST: + if ((mutex == NULL) || (((*cond)->c_mutex != NULL) && + ((*cond)->c_mutex != *mutex))) { + /* Return invalid argument error: */ + rval = EINVAL; + } else { + /* Reset the timeout and interrupted flags: */ + curthread->timeout = 0; + curthread->interrupted = 0; + + /* + * Queue the running thread for the condition + * variable: + */ + cond_queue_enq(*cond, curthread); + + /* Unlock the mutex: */ + if (mutex_locked && + ((rval = _mutex_cv_unlock(mutex)) != 0)) { + /* + * Cannot unlock the mutex; remove the + * running thread from the condition + * variable queue: + */ + cond_queue_remove(*cond, curthread); + } else { + /* Remember the mutex: */ + (*cond)->c_mutex = *mutex; + + /* + * Don't unlock the mutex the next + * time through the loop (if the + * thread has to be requeued after + * handling a signal). + */ + mutex_locked = 0; + + /* + * This thread is active and is in a + * critical region (holding the cv + * lock); we should be able to safely + * set the state. + */ + THR_SCHED_LOCK(curthread, curthread); + /* Set the wakeup time: */ + curthread->wakeup_time.tv_sec = + abstime->tv_sec; + curthread->wakeup_time.tv_nsec = + abstime->tv_nsec; + THR_SET_STATE(curthread, PS_COND_WAIT); + + /* Remember the CV: */ + curthread->data.cond = *cond; + curthread->sigbackout = cond_wait_backout; + THR_SCHED_UNLOCK(curthread, curthread); + + /* Unlock the CV structure: */ + THR_LOCK_RELEASE(curthread, + &(*cond)->c_lock); + + /* Schedule the next thread: */ + _thr_sched_switch(curthread); + + /* + * XXX - This really isn't a good check + * since there can be more than one + * thread waiting on the CV. Signals + * sent to threads waiting on mutexes + * or CVs should really be deferred + * until the threads are no longer + * waiting, but POSIX says that signals + * should be sent "as soon as possible". + */ + done = (seqno != (*cond)->c_seqno); + if (done && !THR_IN_CONDQ(curthread)) { + /* + * The thread is dequeued, so + * it is safe to clear these. + */ + curthread->data.cond = NULL; + curthread->sigbackout = NULL; + check_continuation(curthread, + NULL, mutex); + return (_mutex_cv_lock(mutex)); + } + + /* Relock the CV structure: */ + THR_LOCK_ACQUIRE(curthread, + &(*cond)->c_lock); + + /* + * Clear these after taking the lock to + * prevent a race condition where a + * signal can arrive before dequeueing + * the thread. + */ + curthread->data.cond = NULL; + curthread->sigbackout = NULL; + + done = (seqno != (*cond)->c_seqno); + + if (THR_IN_CONDQ(curthread)) { + cond_queue_remove(*cond, + curthread); + + /* Check for no more waiters: */ + if (TAILQ_EMPTY(&(*cond)->c_queue)) + (*cond)->c_mutex = NULL; + } + + if (curthread->timeout != 0) { + /* The wait timedout. */ + rval = ETIMEDOUT; + } + } + } + break; + + /* Trap invalid condition variable types: */ + default: + /* Return an invalid argument error: */ + rval = EINVAL; + break; + } + + check_continuation(curthread, *cond, + mutex_locked ? NULL : mutex); + } while ((done == 0) && (rval == 0)); + + /* Unlock the condition variable structure: */ + THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); + + if (mutex_locked == 0) + _mutex_cv_lock(mutex); + + /* Return the completion status: */ + return (rval); +} + +int +__pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, + const struct timespec *abstime) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_cancel_enter(curthread); + ret = _pthread_cond_timedwait(cond, mutex, abstime); + _thr_cancel_leave(curthread, 1); + return (ret); +} + + +int +_pthread_cond_signal(pthread_cond_t * cond) +{ + struct pthread *curthread = _get_curthread(); + struct pthread *pthread; + struct kse_mailbox *kmbx; + int rval = 0; + + THR_ASSERT(curthread->locklevel == 0, + "cv_timedwait: locklevel is not zero!"); + if (cond == NULL) + rval = EINVAL; + /* + * If the condition variable is statically initialized, perform dynamic + * initialization. + */ + else if (*cond != NULL || (rval = pthread_cond_init(cond, NULL)) == 0) { + /* Lock the condition variable structure: */ + THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); + + /* Process according to condition variable type: */ + switch ((*cond)->c_type) { + /* Fast condition variable: */ + case COND_TYPE_FAST: + /* Increment the sequence number: */ + (*cond)->c_seqno++; + + /* + * Wakeups have to be done with the CV lock held; + * otherwise there is a race condition where the + * thread can timeout, run on another KSE, and enter + * another blocking state (including blocking on a CV). + */ + if ((pthread = TAILQ_FIRST(&(*cond)->c_queue)) + != NULL) { + THR_SCHED_LOCK(curthread, pthread); + cond_queue_remove(*cond, pthread); + pthread->sigbackout = NULL; + if ((pthread->kseg == curthread->kseg) && + (pthread->active_priority > + curthread->active_priority)) + curthread->critical_yield = 1; + kmbx = _thr_setrunnable_unlocked(pthread); + THR_SCHED_UNLOCK(curthread, pthread); + if (kmbx != NULL) + kse_wakeup(kmbx); + } + /* Check for no more waiters: */ + if (TAILQ_EMPTY(&(*cond)->c_queue)) + (*cond)->c_mutex = NULL; + break; + + /* Trap invalid condition variable types: */ + default: + /* Return an invalid argument error: */ + rval = EINVAL; + break; + } + + /* Unlock the condition variable structure: */ + THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); + } + + /* Return the completion status: */ + return (rval); +} + +__strong_reference(_pthread_cond_signal, _thr_cond_signal); + +int +_pthread_cond_broadcast(pthread_cond_t * cond) +{ + struct pthread *curthread = _get_curthread(); + struct pthread *pthread; + struct kse_mailbox *kmbx; + int rval = 0; + + THR_ASSERT(curthread->locklevel == 0, + "cv_timedwait: locklevel is not zero!"); + if (cond == NULL) + rval = EINVAL; + /* + * If the condition variable is statically initialized, perform dynamic + * initialization. + */ + else if (*cond != NULL || (rval = pthread_cond_init(cond, NULL)) == 0) { + /* Lock the condition variable structure: */ + THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); + + /* Process according to condition variable type: */ + switch ((*cond)->c_type) { + /* Fast condition variable: */ + case COND_TYPE_FAST: + /* Increment the sequence number: */ + (*cond)->c_seqno++; + + /* + * Enter a loop to bring all threads off the + * condition queue: + */ + while ((pthread = TAILQ_FIRST(&(*cond)->c_queue)) + != NULL) { + THR_SCHED_LOCK(curthread, pthread); + cond_queue_remove(*cond, pthread); + pthread->sigbackout = NULL; + if ((pthread->kseg == curthread->kseg) && + (pthread->active_priority > + curthread->active_priority)) + curthread->critical_yield = 1; + kmbx = _thr_setrunnable_unlocked(pthread); + THR_SCHED_UNLOCK(curthread, pthread); + if (kmbx != NULL) + kse_wakeup(kmbx); + } + + /* There are no more waiting threads: */ + (*cond)->c_mutex = NULL; + break; + + /* Trap invalid condition variable types: */ + default: + /* Return an invalid argument error: */ + rval = EINVAL; + break; + } + + /* Unlock the condition variable structure: */ + THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); + } + + /* Return the completion status: */ + return (rval); +} + +__strong_reference(_pthread_cond_broadcast, _thr_cond_broadcast); + +static inline void +check_continuation(struct pthread *curthread, struct pthread_cond *cond, + pthread_mutex_t *mutex) +{ + if ((curthread->interrupted != 0) && + (curthread->continuation != NULL)) { + if (cond != NULL) + /* Unlock the condition variable structure: */ + THR_LOCK_RELEASE(curthread, &cond->c_lock); + /* + * Note that even though this thread may have been + * canceled, POSIX requires that the mutex be + * reaquired prior to cancellation. + */ + if (mutex != NULL) + _mutex_cv_lock(mutex); + curthread->continuation((void *) curthread); + PANIC("continuation returned in pthread_cond_wait.\n"); + } +} + +static void +cond_wait_backout(void *arg) +{ + struct pthread *curthread = (struct pthread *)arg; + pthread_cond_t cond; + + cond = curthread->data.cond; + if (cond != NULL) { + /* Lock the condition variable structure: */ + THR_LOCK_ACQUIRE(curthread, &cond->c_lock); + + /* Process according to condition variable type: */ + switch (cond->c_type) { + /* Fast condition variable: */ + case COND_TYPE_FAST: + cond_queue_remove(cond, curthread); + + /* Check for no more waiters: */ + if (TAILQ_EMPTY(&cond->c_queue)) + cond->c_mutex = NULL; + break; + + default: + break; + } + + /* Unlock the condition variable structure: */ + THR_LOCK_RELEASE(curthread, &cond->c_lock); + } + /* No need to call this again. */ + curthread->sigbackout = NULL; +} + +/* + * Dequeue a waiting thread from the head of a condition queue in + * descending priority order. + */ +static inline struct pthread * +cond_queue_deq(pthread_cond_t cond) +{ + struct pthread *pthread; + + while ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) { + TAILQ_REMOVE(&cond->c_queue, pthread, sqe); + THR_CONDQ_CLEAR(pthread); + if ((pthread->timeout == 0) && (pthread->interrupted == 0)) + /* + * Only exit the loop when we find a thread + * that hasn't timed out or been canceled; + * those threads are already running and don't + * need their run state changed. + */ + break; + } + + return (pthread); +} + +/* + * Remove a waiting thread from a condition queue in descending priority + * order. + */ +static inline void +cond_queue_remove(pthread_cond_t cond, struct pthread *pthread) +{ + /* + * Because pthread_cond_timedwait() can timeout as well + * as be signaled by another thread, it is necessary to + * guard against removing the thread from the queue if + * it isn't in the queue. + */ + if (THR_IN_CONDQ(pthread)) { + TAILQ_REMOVE(&cond->c_queue, pthread, sqe); + THR_CONDQ_CLEAR(pthread); + } +} + +/* + * Enqueue a waiting thread to a condition queue in descending priority + * order. + */ +static inline void +cond_queue_enq(pthread_cond_t cond, struct pthread *pthread) +{ + struct pthread *tid = TAILQ_LAST(&cond->c_queue, cond_head); + + THR_ASSERT(!THR_IN_SYNCQ(pthread), + "cond_queue_enq: thread already queued!"); + + /* + * For the common case of all threads having equal priority, + * we perform a quick check against the priority of the thread + * at the tail of the queue. + */ + if ((tid == NULL) || (pthread->active_priority <= tid->active_priority)) + TAILQ_INSERT_TAIL(&cond->c_queue, pthread, sqe); + else { + tid = TAILQ_FIRST(&cond->c_queue); + while (pthread->active_priority <= tid->active_priority) + tid = TAILQ_NEXT(tid, sqe); + TAILQ_INSERT_BEFORE(tid, pthread, sqe); + } + THR_CONDQ_SET(pthread); + pthread->data.cond = cond; +} diff --git a/lib/libpthread/thread/thr_condattr_destroy.c b/lib/libpthread/thread/thr_condattr_destroy.c new file mode 100644 index 0000000..4e2c337 --- /dev/null +++ b/lib/libpthread/thread/thr_condattr_destroy.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 1997 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <stdlib.h> +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_condattr_destroy); +LT10_COMPAT_DEFAULT(pthread_condattr_destroy); + +__weak_reference(_pthread_condattr_destroy, pthread_condattr_destroy); + +int +_pthread_condattr_destroy(pthread_condattr_t *attr) +{ + int ret; + if (attr == NULL || *attr == NULL) { + ret = EINVAL; + } else { + free(*attr); + *attr = NULL; + ret = 0; + } + return(ret); +} diff --git a/lib/libpthread/thread/thr_condattr_init.c b/lib/libpthread/thread/thr_condattr_init.c new file mode 100644 index 0000000..e553839 --- /dev/null +++ b/lib/libpthread/thread/thr_condattr_init.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 1997 John Birrell <jb@cimlogic.com.au> + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_condattr_init); +LT10_COMPAT_DEFAULT(pthread_condattr_init); + +__weak_reference(_pthread_condattr_init, pthread_condattr_init); + +int +_pthread_condattr_init(pthread_condattr_t *attr) +{ + int ret; + pthread_condattr_t pattr; + + if ((pattr = (pthread_condattr_t) + malloc(sizeof(struct pthread_cond_attr))) == NULL) { + ret = ENOMEM; + } else { + memcpy(pattr, &_pthread_condattr_default, + sizeof(struct pthread_cond_attr)); + *attr = pattr; + ret = 0; + } + return (ret); +} diff --git a/lib/libpthread/thread/thr_condattr_pshared.c b/lib/libpthread/thread/thr_condattr_pshared.c new file mode 100644 index 0000000..7cef4d9 --- /dev/null +++ b/lib/libpthread/thread/thr_condattr_pshared.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2005 David Xu <davidxu@freebsd.org> + * 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 unmodified, 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. + * + * 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. + * + * $FreeBSD$ + * + */ + +#include <errno.h> +#include "thr_private.h" + +__weak_reference(_pthread_condattr_getpshared, pthread_condattr_getpshared); +__weak_reference(_pthread_condattr_setpshared, pthread_condattr_setpshared); + +int +_pthread_condattr_getpshared(const pthread_condattr_t *attr, + int *pshared) +{ + if (attr == NULL || *attr == NULL) + return (EINVAL); + + pshared = PTHREAD_PROCESS_PRIVATE; + return (0); +} + +int +_pthread_condattr_setpshared(pthread_condattr_t *attr, int pshared) +{ + if (attr == NULL || *attr == NULL) + return (EINVAL); + + if (pshared != PTHREAD_PROCESS_PRIVATE) + return (EINVAL); + return (0); +} diff --git a/lib/libpthread/thread/thr_connect.c b/lib/libpthread/thread/thr_connect.c new file mode 100644 index 0000000..225d8b9 --- /dev/null +++ b/lib/libpthread/thread/thr_connect.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2003 Daniel Eischen <deischen@freebsd.org>. + * 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. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/socket.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(__connect); +LT10_COMPAT_DEFAULT(connect); + +__weak_reference(__connect, connect); + +int +__connect(int fd, const struct sockaddr *name, socklen_t namelen) +{ + struct pthread *curthread; + int ret; + + curthread = _get_curthread(); + _thr_cancel_enter(curthread); + ret = __sys_connect(fd, name, namelen); + _thr_cancel_leave(curthread, ret == -1); + + return (ret); +} diff --git a/lib/libpthread/thread/thr_creat.c b/lib/libpthread/thread/thr_creat.c new file mode 100644 index 0000000..7528f0b --- /dev/null +++ b/lib/libpthread/thread/thr_creat.c @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>. + * 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(s), this list of conditions and the following disclaimer as + * the first lines of this file unmodified other than the possible + * addition of one or more copyright notices. + * 2. Redistributions in binary form must reproduce the above copyright + * notice(s), this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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. + * + * $FreeBSD$ + */ + +#include <fcntl.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(___creat); +LT10_COMPAT_DEFAULT(creat); + +extern int __creat(const char *, mode_t); + +__weak_reference(___creat, creat); + +int +___creat(const char *path, mode_t mode) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_cancel_enter(curthread); + ret = __creat(path, mode); + /* + * To avoid possible file handle leak, + * only check cancellation point if it is failure + */ + _thr_cancel_leave(curthread, (ret == -1)); + + return ret; +} diff --git a/lib/libpthread/thread/thr_create.c b/lib/libpthread/thread/thr_create.c new file mode 100644 index 0000000..98edb71 --- /dev/null +++ b/lib/libpthread/thread/thr_create.c @@ -0,0 +1,348 @@ +/* + * Copyright (c) 2003 Daniel M. Eischen <deischen@gdeb.com> + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <stddef.h> +#include <sys/time.h> +#include <machine/reg.h> +#include <pthread.h> +#include "thr_private.h" +#include "libc_private.h" + +LT10_COMPAT_PRIVATE(_pthread_create); +LT10_COMPAT_DEFAULT(pthread_create); + +static void free_thread(struct pthread *curthread, struct pthread *thread); +static int create_stack(struct pthread_attr *pattr); +static void free_stack(struct pthread_attr *pattr); +static void thread_start(struct pthread *curthread, + void *(*start_routine) (void *), void *arg); + +__weak_reference(_pthread_create, pthread_create); + +/* + * Some notes on new thread creation and first time initializion + * to enable multi-threading. + * + * There are basically two things that need to be done. + * + * 1) The internal library variables must be initialized. + * 2) Upcalls need to be enabled to allow multiple threads + * to be run. + * + * The first may be done as a result of other pthread functions + * being called. When _thr_initial is null, _libpthread_init is + * called to initialize the internal variables; this also creates + * or sets the initial thread. It'd be nice to automatically + * have _libpthread_init called on program execution so we don't + * have to have checks throughout the library. + * + * The second part is only triggered by the creation of the first + * thread (other than the initial/main thread). If the thread + * being created is a scope system thread, then a new KSE/KSEG + * pair needs to be allocated. Also, if upcalls haven't been + * enabled on the initial thread's KSE, they must be now that + * there is more than one thread; this could be delayed until + * the initial KSEG has more than one thread. + */ +int +_pthread_create(pthread_t * thread, const pthread_attr_t * attr, + void *(*start_routine) (void *), void *arg) +{ + struct pthread *curthread, *new_thread; + struct kse *kse = NULL; + struct kse_group *kseg = NULL; + kse_critical_t crit; + int ret = 0; + + if (_thr_initial == NULL) + _libpthread_init(NULL); + + /* + * Turn on threaded mode, if failed, it is unnecessary to + * do further work. + */ + if (_kse_isthreaded() == 0 && _kse_setthreaded(1)) { + return (EAGAIN); + } + curthread = _get_curthread(); + + /* + * Allocate memory for the thread structure. + * Some functions use malloc, so don't put it + * in a critical region. + */ + if ((new_thread = _thr_alloc(curthread)) == NULL) { + /* Insufficient memory to create a thread: */ + ret = EAGAIN; + } else { + /* Check if default thread attributes are required: */ + if (attr == NULL || *attr == NULL) + /* Use the default thread attributes: */ + new_thread->attr = _pthread_attr_default; + else { + new_thread->attr = *(*attr); + if ((*attr)->sched_inherit == PTHREAD_INHERIT_SCHED) { + /* inherit scheduling contention scop */ + if (curthread->attr.flags & PTHREAD_SCOPE_SYSTEM) + new_thread->attr.flags |= PTHREAD_SCOPE_SYSTEM; + else + new_thread->attr.flags &= ~PTHREAD_SCOPE_SYSTEM; + /* + * scheduling policy and scheduling parameters will be + * inherited in following code. + */ + } + } + if (_thread_scope_system > 0) + new_thread->attr.flags |= PTHREAD_SCOPE_SYSTEM; + else if ((_thread_scope_system < 0) + && (thread != &_thr_sig_daemon)) + new_thread->attr.flags &= ~PTHREAD_SCOPE_SYSTEM; + if (create_stack(&new_thread->attr) != 0) { + /* Insufficient memory to create a stack: */ + ret = EAGAIN; + _thr_free(curthread, new_thread); + } + else if (((new_thread->attr.flags & PTHREAD_SCOPE_SYSTEM) != 0) && + (((kse = _kse_alloc(curthread, 1)) == NULL) + || ((kseg = _kseg_alloc(curthread)) == NULL))) { + /* Insufficient memory to create a new KSE/KSEG: */ + ret = EAGAIN; + if (kse != NULL) { + kse->k_kcb->kcb_kmbx.km_flags |= KMF_DONE; + _kse_free(curthread, kse); + } + free_stack(&new_thread->attr); + _thr_free(curthread, new_thread); + } + else { + if (kseg != NULL) { + /* Add the KSE to the KSEG's list of KSEs. */ + TAILQ_INSERT_HEAD(&kseg->kg_kseq, kse, k_kgqe); + kseg->kg_ksecount = 1; + kse->k_kseg = kseg; + kse->k_schedq = &kseg->kg_schedq; + } + /* + * Write a magic value to the thread structure + * to help identify valid ones: + */ + new_thread->magic = THR_MAGIC; + + new_thread->slice_usec = -1; + new_thread->start_routine = start_routine; + new_thread->arg = arg; + new_thread->cancelflags = PTHREAD_CANCEL_ENABLE | + PTHREAD_CANCEL_DEFERRED; + + /* No thread is wanting to join to this one: */ + new_thread->joiner = NULL; + + /* + * Initialize the machine context. + * Enter a critical region to get consistent context. + */ + crit = _kse_critical_enter(); + THR_GETCONTEXT(&new_thread->tcb->tcb_tmbx.tm_context); + /* Initialize the thread for signals: */ + new_thread->sigmask = curthread->sigmask; + _kse_critical_leave(crit); + + new_thread->tcb->tcb_tmbx.tm_udata = new_thread; + new_thread->tcb->tcb_tmbx.tm_context.uc_sigmask = + new_thread->sigmask; + new_thread->tcb->tcb_tmbx.tm_context.uc_stack.ss_size = + new_thread->attr.stacksize_attr; + new_thread->tcb->tcb_tmbx.tm_context.uc_stack.ss_sp = + new_thread->attr.stackaddr_attr; + makecontext(&new_thread->tcb->tcb_tmbx.tm_context, + (void (*)(void))thread_start, 3, new_thread, + start_routine, arg); + /* + * Check if this thread is to inherit the scheduling + * attributes from its parent: + */ + if (new_thread->attr.sched_inherit == PTHREAD_INHERIT_SCHED) { + /* + * Copy the scheduling attributes. + * Lock the scheduling lock to get consistent + * scheduling parameters. + */ + THR_SCHED_LOCK(curthread, curthread); + new_thread->base_priority = + curthread->base_priority & + ~THR_SIGNAL_PRIORITY; + new_thread->attr.prio = + curthread->base_priority & + ~THR_SIGNAL_PRIORITY; + new_thread->attr.sched_policy = + curthread->attr.sched_policy; + THR_SCHED_UNLOCK(curthread, curthread); + } else { + /* + * Use just the thread priority, leaving the + * other scheduling attributes as their + * default values: + */ + new_thread->base_priority = + new_thread->attr.prio; + } + new_thread->active_priority = new_thread->base_priority; + new_thread->inherited_priority = 0; + + /* Initialize the mutex queue: */ + TAILQ_INIT(&new_thread->mutexq); + + /* Initialise hooks in the thread structure: */ + new_thread->specific = NULL; + new_thread->specific_data_count = 0; + new_thread->cleanup = NULL; + new_thread->flags = 0; + new_thread->tlflags = 0; + new_thread->sigbackout = NULL; + new_thread->continuation = NULL; + new_thread->wakeup_time.tv_sec = -1; + new_thread->lock_switch = 0; + sigemptyset(&new_thread->sigpend); + new_thread->check_pending = 0; + new_thread->locklevel = 0; + new_thread->rdlock_count = 0; + new_thread->sigstk.ss_sp = 0; + new_thread->sigstk.ss_size = 0; + new_thread->sigstk.ss_flags = SS_DISABLE; + new_thread->oldsigmask = NULL; + + if (new_thread->attr.suspend == THR_CREATE_SUSPENDED) { + new_thread->state = PS_SUSPENDED; + new_thread->flags = THR_FLAGS_SUSPENDED; + } + else + new_thread->state = PS_RUNNING; + + /* + * System scope threads have their own kse and + * kseg. Process scope threads are all hung + * off the main process kseg. + */ + if ((new_thread->attr.flags & PTHREAD_SCOPE_SYSTEM) == 0) { + new_thread->kseg = _kse_initial->k_kseg; + new_thread->kse = _kse_initial; + } + else { + kse->k_curthread = NULL; + kse->k_kseg->kg_flags |= KGF_SINGLE_THREAD; + new_thread->kse = kse; + new_thread->kseg = kse->k_kseg; + kse->k_kcb->kcb_kmbx.km_udata = kse; + kse->k_kcb->kcb_kmbx.km_curthread = NULL; + } + + /* + * Schedule the new thread starting a new KSEG/KSE + * pair if necessary. + */ + ret = _thr_schedule_add(curthread, new_thread); + if (ret != 0) + free_thread(curthread, new_thread); + else { + /* Return a pointer to the thread structure: */ + (*thread) = new_thread; + } + } + } + + /* Return the status: */ + return (ret); +} + +static void +free_thread(struct pthread *curthread, struct pthread *thread) +{ + free_stack(&thread->attr); + if ((thread->attr.flags & PTHREAD_SCOPE_SYSTEM) != 0) { + /* Free the KSE and KSEG. */ + _kseg_free(thread->kseg); + _kse_free(curthread, thread->kse); + } + _thr_free(curthread, thread); +} + +static int +create_stack(struct pthread_attr *pattr) +{ + int ret; + + /* Check if a stack was specified in the thread attributes: */ + if ((pattr->stackaddr_attr) != NULL) { + pattr->guardsize_attr = 0; + pattr->flags |= THR_STACK_USER; + ret = 0; + } + else + ret = _thr_stack_alloc(pattr); + return (ret); +} + +static void +free_stack(struct pthread_attr *pattr) +{ + struct kse *curkse; + kse_critical_t crit; + + if ((pattr->flags & THR_STACK_USER) == 0) { + crit = _kse_critical_enter(); + curkse = _get_curkse(); + KSE_LOCK_ACQUIRE(curkse, &_thread_list_lock); + /* Stack routines don't use malloc/free. */ + _thr_stack_free(pattr); + KSE_LOCK_RELEASE(curkse, &_thread_list_lock); + _kse_critical_leave(crit); + } +} + +static void +thread_start(struct pthread *curthread, void *(*start_routine) (void *), + void *arg) +{ + /* Run the current thread's start routine with argument: */ + pthread_exit(start_routine(arg)); + + /* This point should never be reached. */ + PANIC("Thread has resumed after exit"); +} diff --git a/lib/libpthread/thread/thr_detach.c b/lib/libpthread/thread/thr_detach.c new file mode 100644 index 0000000..2ae95df --- /dev/null +++ b/lib/libpthread/thread/thr_detach.c @@ -0,0 +1,120 @@ +/* + * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <sys/types.h> +#include <machine/atomic.h> +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_detach); +LT10_COMPAT_DEFAULT(pthread_detach); + +__weak_reference(_pthread_detach, pthread_detach); + +int +_pthread_detach(pthread_t pthread) +{ + struct pthread *curthread = _get_curthread(); + struct kse_mailbox *kmbx = NULL; + struct pthread *joiner; + int rval = 0; + + /* Check for invalid calling parameters: */ + if (pthread == NULL || pthread->magic != THR_MAGIC) + /* Return an invalid argument error: */ + rval = EINVAL; + + else if ((rval = _thr_ref_add(curthread, pthread, + /*include dead*/1)) != 0) { + /* Return an error: */ + } + + /* Check if the thread is already detached: */ + else if ((pthread->attr.flags & PTHREAD_DETACHED) != 0) { + /* Return an error: */ + _thr_ref_delete(curthread, pthread); + rval = EINVAL; + } else { + /* Lock the detached thread: */ + THR_SCHED_LOCK(curthread, pthread); + + /* Flag the thread as detached: */ + pthread->attr.flags |= PTHREAD_DETACHED; + + /* Retrieve any joining thread and remove it: */ + joiner = pthread->joiner; + if ((joiner != NULL) && (joiner->kseg == pthread->kseg)) { + /* + * We already own the scheduler lock for the joiner. + * Take advantage of that and make the joiner runnable. + */ + if (joiner->join_status.thread == pthread) { + /* + * Set the return value for the woken thread: + */ + joiner->join_status.error = ESRCH; + joiner->join_status.ret = NULL; + joiner->join_status.thread = NULL; + + kmbx = _thr_setrunnable_unlocked(joiner); + } + joiner = NULL; + } + THR_SCHED_UNLOCK(curthread, pthread); + /* See if there is a thread waiting in pthread_join(): */ + if ((joiner != NULL) && + (_thr_ref_add(curthread, joiner, 0) == 0)) { + /* Lock the joiner before fiddling with it. */ + THR_SCHED_LOCK(curthread, joiner); + if (joiner->join_status.thread == pthread) { + /* + * Set the return value for the woken thread: + */ + joiner->join_status.error = ESRCH; + joiner->join_status.ret = NULL; + joiner->join_status.thread = NULL; + + kmbx = _thr_setrunnable_unlocked(joiner); + } + THR_SCHED_UNLOCK(curthread, joiner); + _thr_ref_delete(curthread, joiner); + } + _thr_ref_delete(curthread, pthread); + if (kmbx != NULL) + kse_wakeup(kmbx); + } + + /* Return the completion status: */ + return (rval); +} diff --git a/lib/libpthread/thread/thr_equal.c b/lib/libpthread/thread/thr_equal.c new file mode 100644 index 0000000..95a3b65 --- /dev/null +++ b/lib/libpthread/thread/thr_equal.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_equal); +LT10_COMPAT_DEFAULT(pthread_equal); + +__weak_reference(_pthread_equal, pthread_equal); + +int +_pthread_equal(pthread_t t1, pthread_t t2) +{ + /* Compare the two thread pointers: */ + return (t1 == t2); +} diff --git a/lib/libpthread/thread/thr_execve.c b/lib/libpthread/thread/thr_execve.c new file mode 100644 index 0000000..b902981 --- /dev/null +++ b/lib/libpthread/thread/thr_execve.c @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2004 Daniel Eischen <deischen@freebsd.org> + * 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(s), this list of conditions and the following disclaimer as + * the first lines of this file unmodified other than the possible + * addition of one or more copyright notices. + * 2. Redistributions in binary form must reproduce the above copyright + * notice(s), this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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. + * + * $FreeBSD$ + */ + +#include <errno.h> +#include <pthread.h> +#include <unistd.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_execve); +LT10_COMPAT_DEFAULT(execve); + +__weak_reference(_execve, execve); + +int +_execve(const char *name, char *const *argv, char *const *envp) +{ + struct kse_execve_args args; + struct pthread *curthread = _get_curthread(); + int ret; + + if (curthread->attr.flags & PTHREAD_SCOPE_SYSTEM) + ret = __sys_execve(name, argv, envp); + else { + /* + * When exec'ing, set the kernel signal mask to the thread's + * signal mask to satisfy POSIX requirements. + */ + args.sigmask = curthread->sigmask; + args.sigpend = curthread->sigpend; + args.path = (char *)name; + args.argv = (char **)argv; + args.envp = (char **)envp; + args.reserved = NULL; + ret = kse_thr_interrupt(NULL, KSE_INTR_EXECVE, (long)&args); + } + + return (ret); +} diff --git a/lib/libpthread/thread/thr_exit.c b/lib/libpthread/thread/thr_exit.c new file mode 100644 index 0000000..1b2f84e --- /dev/null +++ b/lib/libpthread/thread/thr_exit.c @@ -0,0 +1,162 @@ +/* + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_exit); +LT10_COMPAT_DEFAULT(pthread_exit); + +void _pthread_exit(void *status); + +__weak_reference(_pthread_exit, pthread_exit); + +void +_thr_exit(char *fname, int lineno, char *msg) +{ + + /* Write an error message to the standard error file descriptor: */ + _thread_printf(2, + "Fatal error '%s' at line %d in file %s (errno = %d)\n", + msg, lineno, fname, errno); + + abort(); +} + +/* + * Only called when a thread is cancelled. It may be more useful + * to call it from pthread_exit() if other ways of asynchronous or + * abnormal thread termination can be found. + */ +void +_thr_exit_cleanup(void) +{ + struct pthread *curthread = _get_curthread(); + + /* + * POSIX states that cancellation/termination of a thread should + * not release any visible resources (such as mutexes) and that + * it is the applications responsibility. Resources that are + * internal to the threads library, including file and fd locks, + * are not visible to the application and need to be released. + */ + /* Unlock all private mutexes: */ + _mutex_unlock_private(curthread); + + /* + * This still isn't quite correct because we don't account + * for held spinlocks (see libc/stdlib/malloc.c). + */ +} + +void +_pthread_exit(void *status) +{ + struct pthread *curthread = _get_curthread(); + kse_critical_t crit; + struct kse *curkse; + + /* Check if this thread is already in the process of exiting: */ + if ((curthread->flags & THR_FLAGS_EXITING) != 0) { + char msg[128]; + snprintf(msg, sizeof(msg), "Thread %p has called " + "pthread_exit() from a destructor. POSIX 1003.1 " + "1996 s16.2.5.2 does not allow this!", curthread); + PANIC(msg); + } + + /* + * Flag this thread as exiting. Threads should now be prevented + * from joining to this thread. + */ + THR_SCHED_LOCK(curthread, curthread); + curthread->flags |= THR_FLAGS_EXITING; + THR_SCHED_UNLOCK(curthread, curthread); + + /* + * To avoid signal-lost problem, if signals had already been + * delivered to us, handle it. we have already set EXITING flag + * so no new signals should be delivered to us. + * XXX this is not enough if signal was delivered just before + * thread called sigprocmask and masked it! in this case, we + * might have to re-post the signal by kill() if the signal + * is targeting process (not for a specified thread). + * Kernel has same signal-lost problem, a signal may be delivered + * to a thread which is on the way to call sigprocmask or thr_exit()! + */ + if (curthread->check_pending) + _thr_sig_check_pending(curthread); + /* Save the return value: */ + curthread->ret = status; + while (curthread->cleanup != NULL) { + pthread_cleanup_pop(1); + } + if (curthread->attr.cleanup_attr != NULL) { + curthread->attr.cleanup_attr(curthread->attr.arg_attr); + } + /* Check if there is thread specific data: */ + if (curthread->specific != NULL) { + /* Run the thread-specific data destructors: */ + _thread_cleanupspecific(); + } + if (!_kse_isthreaded()) + exit(0); + crit = _kse_critical_enter(); + curkse = _get_curkse(); + KSE_LOCK_ACQUIRE(curkse, &_thread_list_lock); + /* Use thread_list_lock */ + _thread_active_threads--; + if ((_thread_scope_system <= 0 && _thread_active_threads == 1) || + (_thread_scope_system > 0 && _thread_active_threads == 0)) { + KSE_LOCK_RELEASE(curkse, &_thread_list_lock); + _kse_critical_leave(crit); + exit(0); + /* Never reach! */ + } + KSE_LOCK_RELEASE(curkse, &_thread_list_lock); + + /* This thread will never be re-scheduled. */ + KSE_LOCK(curkse); + THR_SET_STATE(curthread, PS_DEAD); + _thr_sched_switch_unlocked(curthread); + /* Never reach! */ + + /* This point should not be reached. */ + PANIC("Dead thread has resumed"); +} diff --git a/lib/libpthread/thread/thr_fcntl.c b/lib/libpthread/thread/thr_fcntl.c new file mode 100644 index 0000000..d59dfd7 --- /dev/null +++ b/lib/libpthread/thread/thr_fcntl.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <stdarg.h> +#include "namespace.h" +#include <fcntl.h> +#include "un-namespace.h" +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(__fcntl); +LT10_COMPAT_DEFAULT(fcntl); + +__weak_reference(__fcntl, fcntl); + +int +__fcntl(int fd, int cmd,...) +{ + struct pthread *curthread = _get_curthread(); + int ret, check = 1; + va_list ap; + + _thr_cancel_enter(curthread); + + va_start(ap, cmd); + switch (cmd) { + case F_DUPFD: + ret = __sys_fcntl(fd, cmd, va_arg(ap, int)); + /* + * To avoid possible file handle leak, + * only check cancellation point if it is failure + */ + check = (ret == -1); + break; + case F_SETFD: + case F_SETFL: + ret = __sys_fcntl(fd, cmd, va_arg(ap, int)); + break; + case F_GETFD: + case F_GETFL: + ret = __sys_fcntl(fd, cmd); + break; + default: + ret = __sys_fcntl(fd, cmd, va_arg(ap, void *)); + } + va_end(ap); + + _thr_cancel_leave(curthread, check); + + return (ret); +} diff --git a/lib/libpthread/thread/thr_find_thread.c b/lib/libpthread/thread/thr_find_thread.c new file mode 100644 index 0000000..5a64640 --- /dev/null +++ b/lib/libpthread/thread/thr_find_thread.c @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2003 Daniel Eischen <deischen@freebsd.org> + * Copyright (c) 1998 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +/* + * Find a thread in the linked list of active threads and add a reference + * to it. Threads with positive reference counts will not be deallocated + * until all references are released. + */ +int +_thr_ref_add(struct pthread *curthread, struct pthread *thread, + int include_dead) +{ + kse_critical_t crit; + struct pthread *pthread; + struct kse *curkse; + + if (thread == NULL) + /* Invalid thread: */ + return (EINVAL); + + crit = _kse_critical_enter(); + curkse = _get_curkse(); + KSE_LOCK_ACQUIRE(curkse, &_thread_list_lock); + pthread = _thr_hash_find(thread); + if (pthread) { + if ((include_dead == 0) && + ((pthread->state == PS_DEAD) || + ((pthread->state == PS_DEADLOCK) || + ((pthread->flags & THR_FLAGS_EXITING) != 0)))) + pthread = NULL; + else { + pthread->refcount++; + if (curthread != NULL) + curthread->critical_count++; + } + } + KSE_LOCK_RELEASE(curkse, &_thread_list_lock); + _kse_critical_leave(crit); + + /* Return zero if the thread exists: */ + return ((pthread != NULL) ? 0 : ESRCH); +} + +void +_thr_ref_delete(struct pthread *curthread, struct pthread *thread) +{ + kse_critical_t crit; + struct kse *curkse; + + if (thread != NULL) { + crit = _kse_critical_enter(); + curkse = _get_curkse(); + KSE_LOCK_ACQUIRE(curkse, &_thread_list_lock); + thread->refcount--; + if (curthread != NULL) + curthread->critical_count--; + if ((thread->refcount == 0) && + (thread->tlflags & TLFLAGS_GC_SAFE) != 0) + THR_GCLIST_ADD(thread); + KSE_LOCK_RELEASE(curkse, &_thread_list_lock); + _kse_critical_leave(crit); + } +} diff --git a/lib/libpthread/thread/thr_fork.c b/lib/libpthread/thread/thr_fork.c new file mode 100644 index 0000000..4dfa487 --- /dev/null +++ b/lib/libpthread/thread/thr_fork.c @@ -0,0 +1,131 @@ +/* + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <pthread.h> +#include <spinlock.h> +#include <sys/signalvar.h> + +#include "libc_private.h" +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_fork); +LT10_COMPAT_DEFAULT(fork); + +__weak_reference(_fork, fork); + +pid_t +_fork(void) +{ + sigset_t sigset, oldset; + struct pthread *curthread; + struct pthread_atfork *af; + pid_t ret; + int errsave; + + curthread = _get_curthread(); + + if (!_kse_isthreaded()) { + SIGFILLSET(sigset); + __sys_sigprocmask(SIG_SETMASK, &sigset, &oldset); + ret = __sys_fork(); + if (ret == 0) + /* Child */ + __sys_sigprocmask(SIG_SETMASK, &curthread->sigmask, + NULL); + else + __sys_sigprocmask(SIG_SETMASK, &oldset, NULL); + return (ret); + } + + /* + * Masks all signals until we reach a safe point in + * _kse_single_thread, and the signal masks will be + * restored in that function, for M:N thread, all + * signals were already masked in kernel atomically, + * we only need to do this for bound thread. + */ + if (curthread->attr.flags & PTHREAD_SCOPE_SYSTEM) { + SIGFILLSET(sigset); + __sys_sigprocmask(SIG_SETMASK, &sigset, &oldset); + } + + _pthread_mutex_lock(&_thr_atfork_mutex); + + /* Run down atfork prepare handlers. */ + TAILQ_FOREACH_REVERSE(af, &_thr_atfork_list, atfork_head, qe) { + if (af->prepare != NULL) + af->prepare(); + } + + /* Fork a new process: */ + if (_kse_isthreaded() != 0) { + _malloc_prefork(); + } + if ((ret = __sys_fork()) == 0) { + /* Child process */ + errsave = errno; + + /* Kernel signal mask is restored in _kse_single_thread */ + _kse_single_thread(curthread); + + /* Run down atfork child handlers. */ + TAILQ_FOREACH(af, &_thr_atfork_list, qe) { + if (af->child != NULL) + af->child(); + } + _thr_mutex_reinit(&_thr_atfork_mutex); + } else { + if (_kse_isthreaded() != 0) { + _malloc_postfork(); + } + errsave = errno; + if (curthread->attr.flags & PTHREAD_SCOPE_SYSTEM) { + __sys_sigprocmask(SIG_SETMASK, &oldset, NULL); + } + /* Run down atfork parent handlers. */ + TAILQ_FOREACH(af, &_thr_atfork_list, qe) { + if (af->parent != NULL) + af->parent(); + } + _pthread_mutex_unlock(&_thr_atfork_mutex); + } + errno = errsave; + + /* Return the process ID: */ + return (ret); +} diff --git a/lib/libpthread/thread/thr_fsync.c b/lib/libpthread/thread/thr_fsync.c new file mode 100644 index 0000000..fc6360a --- /dev/null +++ b/lib/libpthread/thread/thr_fsync.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <unistd.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(__fsync); +LT10_COMPAT_DEFAULT(fsync); + +__weak_reference(__fsync, fsync); + +int +__fsync(int fd) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_cancel_enter(curthread); + ret = __sys_fsync(fd); + _thr_cancel_leave(curthread, 1); + + return (ret); +} diff --git a/lib/libpthread/thread/thr_getprio.c b/lib/libpthread/thread/thr_getprio.c new file mode 100644 index 0000000..875c1b3 --- /dev/null +++ b/lib/libpthread/thread/thr_getprio.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include "namespace.h" +#include <errno.h> +#include <pthread.h> +#include "un-namespace.h" + +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_getprio); +LT10_COMPAT_DEFAULT(pthread_getprio); + +__weak_reference(_pthread_getprio, pthread_getprio); + +int +_pthread_getprio(pthread_t pthread) +{ + int policy, ret; + struct sched_param param; + + if ((ret = _pthread_getschedparam(pthread, &policy, ¶m)) == 0) + ret = param.sched_priority; + else { + /* Invalid thread: */ + errno = ret; + ret = -1; + } + + /* Return the thread priority or an error status: */ + return (ret); +} diff --git a/lib/libpthread/thread/thr_getschedparam.c b/lib/libpthread/thread/thr_getschedparam.c new file mode 100644 index 0000000..dca342f --- /dev/null +++ b/lib/libpthread/thread/thr_getschedparam.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>. + * 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 Daniel Eischen. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_getschedparam); +LT10_COMPAT_DEFAULT(pthread_getschedparam); + +__weak_reference(_pthread_getschedparam, pthread_getschedparam); + +int +_pthread_getschedparam(pthread_t pthread, int *policy, + struct sched_param *param) +{ + struct pthread *curthread = _get_curthread(); + int ret, tmp; + + if ((param == NULL) || (policy == NULL)) + /* Return an invalid argument error: */ + ret = EINVAL; + else if (pthread == curthread) { + /* + * Avoid searching the thread list when it is the current + * thread. + */ + THR_SCHED_LOCK(curthread, curthread); + param->sched_priority = + THR_BASE_PRIORITY(pthread->base_priority); + tmp = pthread->attr.sched_policy; + THR_SCHED_UNLOCK(curthread, curthread); + *policy = tmp; + ret = 0; + } + /* Find the thread in the list of active threads. */ + else if ((ret = _thr_ref_add(curthread, pthread, /*include dead*/0)) + == 0) { + THR_SCHED_LOCK(curthread, pthread); + param->sched_priority = + THR_BASE_PRIORITY(pthread->base_priority); + tmp = pthread->attr.sched_policy; + THR_SCHED_UNLOCK(curthread, pthread); + _thr_ref_delete(curthread, pthread); + *policy = tmp; + } + return (ret); +} diff --git a/lib/libpthread/thread/thr_info.c b/lib/libpthread/thread/thr_info.c new file mode 100644 index 0000000..ab5320f --- /dev/null +++ b/lib/libpthread/thread/thr_info.c @@ -0,0 +1,228 @@ +/* + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <unistd.h> +#include <pthread.h> +#include <errno.h> +#include "thr_private.h" + +#ifndef NELEMENTS +#define NELEMENTS(arr) (sizeof(arr) / sizeof(arr[0])) +#endif + +LT10_COMPAT_PRIVATE(_pthread_set_name_np); +LT10_COMPAT_DEFAULT(pthread_set_name_np); + +static void dump_thread(int fd, pthread_t pthread, int long_version); + +__weak_reference(_pthread_set_name_np, pthread_set_name_np); + +struct s_thread_info { + enum pthread_state state; + char *name; +}; + +/* Static variables: */ +static const struct s_thread_info thread_info[] = { + {PS_RUNNING , "Running"}, + {PS_LOCKWAIT , "Waiting on an internal lock"}, + {PS_MUTEX_WAIT , "Waiting on a mutex"}, + {PS_COND_WAIT , "Waiting on a condition variable"}, + {PS_SLEEP_WAIT , "Sleeping"}, + {PS_SIGSUSPEND , "Suspended, waiting for a signal"}, + {PS_SIGWAIT , "Waiting for a signal"}, + {PS_JOIN , "Waiting to join"}, + {PS_SUSPENDED , "Suspended"}, + {PS_DEAD , "Dead"}, + {PS_DEADLOCK , "Deadlocked"}, + {PS_STATE_MAX , "Not a real state!"} +}; + +void +_thread_dump_info(void) +{ + char s[512], tmpfile[128]; + pthread_t pthread; + int fd, i; + + for (i = 0; i < 100000; i++) { + snprintf(tmpfile, sizeof(tmpfile), "/tmp/pthread.dump.%u.%i", + getpid(), i); + /* Open the dump file for append and create it if necessary: */ + if ((fd = __sys_open(tmpfile, O_RDWR | O_CREAT | O_EXCL, + 0666)) < 0) { + /* Can't open the dump file. */ + if (errno == EEXIST) + continue; + /* + * We only need to continue in case of + * EEXIT error. Most other error + * codes means that we will fail all + * the times. + */ + return; + } else { + break; + } + } + if (i==100000) { + /* all 100000 possibilities are in use :( */ + return; + } else { + /* Dump the active threads. */ + strcpy(s, "\n\n========\nACTIVE THREADS\n\n"); + __sys_write(fd, s, strlen(s)); + + /* Enter a loop to report each thread in the global list: */ + TAILQ_FOREACH(pthread, &_thread_list, tle) { + if (pthread->state != PS_DEAD) + dump_thread(fd, pthread, /*long_verson*/ 1); + } + + /* + * Dump the ready threads. + * XXX - We can't easily do this because the run queues + * are per-KSEG. + */ + strcpy(s, "\n\n========\nREADY THREADS - unimplemented\n\n"); + __sys_write(fd, s, strlen(s)); + + + /* + * Dump the waiting threads. + * XXX - We can't easily do this because the wait queues + * are per-KSEG. + */ + strcpy(s, "\n\n========\nWAITING THREADS - unimplemented\n\n"); + __sys_write(fd, s, strlen(s)); + + /* Close the dump file. */ + __sys_close(fd); + } +} + +static void +dump_thread(int fd, pthread_t pthread, int long_version) +{ + struct pthread *curthread = _get_curthread(); + char s[512]; + int i; + + /* Find the state: */ + for (i = 0; i < NELEMENTS(thread_info) - 1; i++) + if (thread_info[i].state == pthread->state) + break; + + /* Output a record for the thread: */ + snprintf(s, sizeof(s), + "--------------------\n" + "Thread %p (%s), scope %s, prio %3d, blocked %s, state %s [%s:%d]\n", + pthread, (pthread->name == NULL) ? "" : pthread->name, + pthread->attr.flags & PTHREAD_SCOPE_SYSTEM ? "system" : "process", + pthread->active_priority, (pthread->blocked != 0) ? "yes" : "no", + thread_info[i].name, pthread->fname, pthread->lineno); + __sys_write(fd, s, strlen(s)); + + if (long_version != 0) { + /* Check if this is the running thread: */ + if (pthread == curthread) { + /* Output a record for the running thread: */ + strcpy(s, "This is the running thread\n"); + __sys_write(fd, s, strlen(s)); + } + /* Check if this is the initial thread: */ + if (pthread == _thr_initial) { + /* Output a record for the initial thread: */ + strcpy(s, "This is the initial thread\n"); + __sys_write(fd, s, strlen(s)); + } + + /* Process according to thread state: */ + switch (pthread->state) { + case PS_SIGWAIT: + snprintf(s, sizeof(s), "sigmask (hi) "); + __sys_write(fd, s, strlen(s)); + for (i = _SIG_WORDS - 1; i >= 0; i--) { + snprintf(s, sizeof(s), "%08x ", + pthread->sigmask.__bits[i]); + __sys_write(fd, s, strlen(s)); + } + snprintf(s, sizeof(s), "(lo)\n"); + __sys_write(fd, s, strlen(s)); + + snprintf(s, sizeof(s), "waitset (hi) "); + __sys_write(fd, s, strlen(s)); + for (i = _SIG_WORDS - 1; i >= 0; i--) { + snprintf(s, sizeof(s), "%08x ", + pthread->data.sigwait->waitset->__bits[i]); + __sys_write(fd, s, strlen(s)); + } + snprintf(s, sizeof(s), "(lo)\n"); + __sys_write(fd, s, strlen(s)); + break; + /* + * Trap other states that are not explicitly + * coded to dump information: + */ + default: + snprintf(s, sizeof(s), "sigmask (hi) "); + __sys_write(fd, s, strlen(s)); + for (i = _SIG_WORDS - 1; i >= 0; i--) { + snprintf(s, sizeof(s), "%08x ", + pthread->sigmask.__bits[i]); + __sys_write(fd, s, strlen(s)); + } + snprintf(s, sizeof(s), "(lo)\n"); + __sys_write(fd, s, strlen(s)); + break; + } + } +} + +/* Set the thread name for debug: */ +void +_pthread_set_name_np(pthread_t thread, char *name) +{ + /* Check if the caller has specified a valid thread: */ + if (thread != NULL && thread->magic == THR_MAGIC) { + if (thread->name != NULL) { + /* Free space for previous name. */ + free(thread->name); + } + thread->name = strdup(name); + } +} diff --git a/lib/libpthread/thread/thr_init.c b/lib/libpthread/thread/thr_init.c new file mode 100644 index 0000000..35c4ee3 --- /dev/null +++ b/lib/libpthread/thread/thr_init.c @@ -0,0 +1,533 @@ +/* + * Copyright (c) 2003 Daniel M. Eischen <deischen@freebsd.org> + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +/* Allocate space for global thread variables here: */ +#define GLOBAL_PTHREAD_PRIVATE + +#include "namespace.h" +#include <sys/param.h> +#include <sys/types.h> +#include <sys/signalvar.h> +#include <machine/reg.h> + +#include <sys/ioctl.h> +#include <sys/mount.h> +#include <sys/uio.h> +#include <sys/socket.h> +#include <sys/event.h> +#include <sys/stat.h> +#include <sys/sysctl.h> +#include <sys/time.h> +#include <sys/ttycom.h> +#include <sys/wait.h> +#include <sys/mman.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <paths.h> +#include <pthread.h> +#include <pthread_np.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "un-namespace.h" + +#include "libc_private.h" +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_libkse_debug); +LT10_COMPAT_PRIVATE(_thread_activated); +LT10_COMPAT_PRIVATE(_thread_active_threads); +LT10_COMPAT_PRIVATE(_thread_list); + +int __pthread_cond_wait(pthread_cond_t *, pthread_mutex_t *); +int __pthread_mutex_lock(pthread_mutex_t *); +int __pthread_mutex_trylock(pthread_mutex_t *); +void _thread_init_hack(void); +extern int _thread_state_running; + +static void init_private(void); +static void init_main_thread(struct pthread *thread); + +/* + * All weak references used within libc should be in this table. + * This is so that static libraries will work. + */ +static void *references[] = { + &_accept, + &_bind, + &_close, + &_connect, + &_dup, + &_dup2, + &_execve, + &_fcntl, + &_flock, + &_flockfile, + &_fstat, + &_fstatfs, + &_fsync, + &_funlockfile, + &_getdirentries, + &_getlogin, + &_getpeername, + &_getsockname, + &_getsockopt, + &_ioctl, + &_kevent, + &_listen, + &_nanosleep, + &_open, + &_pthread_getspecific, + &_pthread_key_create, + &_pthread_key_delete, + &_pthread_mutex_destroy, + &_pthread_mutex_init, + &_pthread_mutex_lock, + &_pthread_mutex_trylock, + &_pthread_mutex_unlock, + &_pthread_mutexattr_init, + &_pthread_mutexattr_destroy, + &_pthread_mutexattr_settype, + &_pthread_once, + &_pthread_setspecific, + &_read, + &_readv, + &_recvfrom, + &_recvmsg, + &_select, + &_sendmsg, + &_sendto, + &_setsockopt, + &_sigaction, + &_sigprocmask, + &_sigsuspend, + &_socket, + &_socketpair, + &_thread_init_hack, + &_wait4, + &_write, + &_writev +}; + +/* + * These are needed when linking statically. All references within + * libgcc (and in the future libc) to these routines are weak, but + * if they are not (strongly) referenced by the application or other + * libraries, then the actual functions will not be loaded. + */ +static void *libgcc_references[] = { + &_pthread_once, + &_pthread_key_create, + &_pthread_key_delete, + &_pthread_getspecific, + &_pthread_setspecific, + &_pthread_mutex_init, + &_pthread_mutex_destroy, + &_pthread_mutex_lock, + &_pthread_mutex_trylock, + &_pthread_mutex_unlock +}; + +#define DUAL_ENTRY(entry) \ + (pthread_func_t)entry, (pthread_func_t)entry + +static pthread_func_t jmp_table[][2] = { + {DUAL_ENTRY(_pthread_atfork)}, /* PJT_ATFORK */ + {DUAL_ENTRY(_pthread_attr_destroy)}, /* PJT_ATTR_DESTROY */ + {DUAL_ENTRY(_pthread_attr_getdetachstate)}, /* PJT_ATTR_GETDETACHSTATE */ + {DUAL_ENTRY(_pthread_attr_getguardsize)}, /* PJT_ATTR_GETGUARDSIZE */ + {DUAL_ENTRY(_pthread_attr_getinheritsched)}, /* PJT_ATTR_GETINHERITSCHED */ + {DUAL_ENTRY(_pthread_attr_getschedparam)}, /* PJT_ATTR_GETSCHEDPARAM */ + {DUAL_ENTRY(_pthread_attr_getschedpolicy)}, /* PJT_ATTR_GETSCHEDPOLICY */ + {DUAL_ENTRY(_pthread_attr_getscope)}, /* PJT_ATTR_GETSCOPE */ + {DUAL_ENTRY(_pthread_attr_getstackaddr)}, /* PJT_ATTR_GETSTACKADDR */ + {DUAL_ENTRY(_pthread_attr_getstacksize)}, /* PJT_ATTR_GETSTACKSIZE */ + {DUAL_ENTRY(_pthread_attr_init)}, /* PJT_ATTR_INIT */ + {DUAL_ENTRY(_pthread_attr_setdetachstate)}, /* PJT_ATTR_SETDETACHSTATE */ + {DUAL_ENTRY(_pthread_attr_setguardsize)}, /* PJT_ATTR_SETGUARDSIZE */ + {DUAL_ENTRY(_pthread_attr_setinheritsched)}, /* PJT_ATTR_SETINHERITSCHED */ + {DUAL_ENTRY(_pthread_attr_setschedparam)}, /* PJT_ATTR_SETSCHEDPARAM */ + {DUAL_ENTRY(_pthread_attr_setschedpolicy)}, /* PJT_ATTR_SETSCHEDPOLICY */ + {DUAL_ENTRY(_pthread_attr_setscope)}, /* PJT_ATTR_SETSCOPE */ + {DUAL_ENTRY(_pthread_attr_setstackaddr)}, /* PJT_ATTR_SETSTACKADDR */ + {DUAL_ENTRY(_pthread_attr_setstacksize)}, /* PJT_ATTR_SETSTACKSIZE */ + {DUAL_ENTRY(_pthread_cancel)}, /* PJT_CANCEL */ + {DUAL_ENTRY(_pthread_cleanup_pop)}, /* PJT_CLEANUP_POP */ + {DUAL_ENTRY(_pthread_cleanup_push)}, /* PJT_CLEANUP_PUSH */ + {DUAL_ENTRY(_pthread_cond_broadcast)}, /* PJT_COND_BROADCAST */ + {DUAL_ENTRY(_pthread_cond_destroy)}, /* PJT_COND_DESTROY */ + {DUAL_ENTRY(_pthread_cond_init)}, /* PJT_COND_INIT */ + {DUAL_ENTRY(_pthread_cond_signal)}, /* PJT_COND_SIGNAL */ + {DUAL_ENTRY(_pthread_cond_timedwait)}, /* PJT_COND_TIMEDWAIT */ + {(pthread_func_t)__pthread_cond_wait, + (pthread_func_t)_pthread_cond_wait}, /* PJT_COND_WAIT */ + {DUAL_ENTRY(_pthread_detach)}, /* PJT_DETACH */ + {DUAL_ENTRY(_pthread_equal)}, /* PJT_EQUAL */ + {DUAL_ENTRY(_pthread_exit)}, /* PJT_EXIT */ + {DUAL_ENTRY(_pthread_getspecific)}, /* PJT_GETSPECIFIC */ + {DUAL_ENTRY(_pthread_join)}, /* PJT_JOIN */ + {DUAL_ENTRY(_pthread_key_create)}, /* PJT_KEY_CREATE */ + {DUAL_ENTRY(_pthread_key_delete)}, /* PJT_KEY_DELETE*/ + {DUAL_ENTRY(_pthread_kill)}, /* PJT_KILL */ + {DUAL_ENTRY(_pthread_main_np)}, /* PJT_MAIN_NP */ + {DUAL_ENTRY(_pthread_mutexattr_destroy)}, /* PJT_MUTEXATTR_DESTROY */ + {DUAL_ENTRY(_pthread_mutexattr_init)}, /* PJT_MUTEXATTR_INIT */ + {DUAL_ENTRY(_pthread_mutexattr_settype)}, /* PJT_MUTEXATTR_SETTYPE */ + {DUAL_ENTRY(_pthread_mutex_destroy)}, /* PJT_MUTEX_DESTROY */ + {DUAL_ENTRY(_pthread_mutex_init)}, /* PJT_MUTEX_INIT */ + {(pthread_func_t)__pthread_mutex_lock, + (pthread_func_t)_pthread_mutex_lock}, /* PJT_MUTEX_LOCK */ + {(pthread_func_t)__pthread_mutex_trylock, + (pthread_func_t)_pthread_mutex_trylock},/* PJT_MUTEX_TRYLOCK */ + {DUAL_ENTRY(_pthread_mutex_unlock)}, /* PJT_MUTEX_UNLOCK */ + {DUAL_ENTRY(_pthread_once)}, /* PJT_ONCE */ + {DUAL_ENTRY(_pthread_rwlock_destroy)}, /* PJT_RWLOCK_DESTROY */ + {DUAL_ENTRY(_pthread_rwlock_init)}, /* PJT_RWLOCK_INIT */ + {DUAL_ENTRY(_pthread_rwlock_rdlock)}, /* PJT_RWLOCK_RDLOCK */ + {DUAL_ENTRY(_pthread_rwlock_tryrdlock)},/* PJT_RWLOCK_TRYRDLOCK */ + {DUAL_ENTRY(_pthread_rwlock_trywrlock)},/* PJT_RWLOCK_TRYWRLOCK */ + {DUAL_ENTRY(_pthread_rwlock_unlock)}, /* PJT_RWLOCK_UNLOCK */ + {DUAL_ENTRY(_pthread_rwlock_wrlock)}, /* PJT_RWLOCK_WRLOCK */ + {DUAL_ENTRY(_pthread_self)}, /* PJT_SELF */ + {DUAL_ENTRY(_pthread_setcancelstate)}, /* PJT_SETCANCELSTATE */ + {DUAL_ENTRY(_pthread_setcanceltype)}, /* PJT_SETCANCELTYPE */ + {DUAL_ENTRY(_pthread_setspecific)}, /* PJT_SETSPECIFIC */ + {DUAL_ENTRY(_pthread_sigmask)}, /* PJT_SIGMASK */ + {DUAL_ENTRY(_pthread_testcancel)} /* PJT_TESTCANCEL */ +}; + +static int init_once = 0; + +/* + * Threaded process initialization. + * + * This is only called under two conditions: + * + * 1) Some thread routines have detected that the library hasn't yet + * been initialized (_thr_initial == NULL && curthread == NULL), or + * + * 2) An explicit call to reinitialize after a fork (indicated + * by curthread != NULL) + */ +void +_libpthread_init(struct pthread *curthread) +{ + int fd; + + /* Check if this function has already been called: */ + if ((_thr_initial != NULL) && (curthread == NULL)) + /* Only initialize the threaded application once. */ + return; + + /* + * Make gcc quiescent about {,libgcc_}references not being + * referenced: + */ + if ((references[0] == NULL) || (libgcc_references[0] == NULL)) + PANIC("Failed loading mandatory references in _thread_init"); + + /* Pull debug symbols in for static binary */ + _thread_state_running = PS_RUNNING; + + /* + * Check the size of the jump table to make sure it is preset + * with the correct number of entries. + */ + if (sizeof(jmp_table) != (sizeof(pthread_func_t) * PJT_MAX * 2)) + PANIC("Thread jump table not properly initialized"); + memcpy(__thr_jtable, jmp_table, sizeof(jmp_table)); + + /* + * Check for the special case of this process running as + * or in place of init as pid = 1: + */ + if ((_thr_pid = getpid()) == 1) { + /* + * Setup a new session for this process which is + * assumed to be running as root. + */ + if (setsid() == -1) + PANIC("Can't set session ID"); + if (revoke(_PATH_CONSOLE) != 0) + PANIC("Can't revoke console"); + if ((fd = __sys_open(_PATH_CONSOLE, O_RDWR)) < 0) + PANIC("Can't open console"); + if (setlogin("root") == -1) + PANIC("Can't set login to root"); + if (__sys_ioctl(fd, TIOCSCTTY, (char *) NULL) == -1) + PANIC("Can't set controlling terminal"); + } + + /* Initialize pthread private data. */ + init_private(); + _kse_init(); + + /* Initialize the initial kse and kseg. */ + _kse_initial = _kse_alloc(NULL, _thread_scope_system > 0); + if (_kse_initial == NULL) + PANIC("Can't allocate initial kse."); + _kse_initial->k_kseg = _kseg_alloc(NULL); + if (_kse_initial->k_kseg == NULL) + PANIC("Can't allocate initial kseg."); + _kse_initial->k_kseg->kg_flags |= KGF_SINGLE_THREAD; + _kse_initial->k_schedq = &_kse_initial->k_kseg->kg_schedq; + + TAILQ_INSERT_TAIL(&_kse_initial->k_kseg->kg_kseq, _kse_initial, k_kgqe); + _kse_initial->k_kseg->kg_ksecount = 1; + + /* Set the initial thread. */ + if (curthread == NULL) { + /* Create and initialize the initial thread. */ + curthread = _thr_alloc(NULL); + if (curthread == NULL) + PANIC("Can't allocate initial thread"); + _thr_initial = curthread; + init_main_thread(curthread); + } else { + /* + * The initial thread is the current thread. It is + * assumed that the current thread is already initialized + * because it is left over from a fork(). + */ + _thr_initial = curthread; + } + _kse_initial->k_kseg->kg_threadcount = 0; + _thr_initial->kse = _kse_initial; + _thr_initial->kseg = _kse_initial->k_kseg; + _thr_initial->active = 1; + + /* + * Add the thread to the thread list and to the KSEG's thread + * queue. + */ + THR_LIST_ADD(_thr_initial); + KSEG_THRQ_ADD(_kse_initial->k_kseg, _thr_initial); + + /* Setup the KSE/thread specific data for the current KSE/thread. */ + _thr_initial->kse->k_curthread = _thr_initial; + _kcb_set(_thr_initial->kse->k_kcb); + _tcb_set(_thr_initial->kse->k_kcb, _thr_initial->tcb); + _thr_initial->kse->k_flags |= KF_INITIALIZED; + + _thr_signal_init(); + _kse_critical_leave(&_thr_initial->tcb->tcb_tmbx); + /* + * activate threaded mode as soon as possible if we are + * being debugged + */ + if (_libkse_debug) + _kse_setthreaded(1); +} + +/* + * This function and pthread_create() do a lot of the same things. + * It'd be nice to consolidate the common stuff in one place. + */ +static void +init_main_thread(struct pthread *thread) +{ + /* Setup the thread attributes. */ + thread->attr = _pthread_attr_default; + thread->attr.flags |= PTHREAD_SCOPE_SYSTEM; + /* + * Set up the thread stack. + * + * Create a red zone below the main stack. All other stacks + * are constrained to a maximum size by the parameters + * passed to mmap(), but this stack is only limited by + * resource limits, so this stack needs an explicitly mapped + * red zone to protect the thread stack that is just beyond. + */ + if (mmap((void *)_usrstack - _thr_stack_initial - + _thr_guard_default, _thr_guard_default, 0, MAP_ANON, + -1, 0) == MAP_FAILED) + PANIC("Cannot allocate red zone for initial thread"); + + /* + * Mark the stack as an application supplied stack so that it + * isn't deallocated. + * + * XXX - I'm not sure it would hurt anything to deallocate + * the main thread stack because deallocation doesn't + * actually free() it; it just puts it in the free + * stack queue for later reuse. + */ + thread->attr.stackaddr_attr = (void *)_usrstack - _thr_stack_initial; + thread->attr.stacksize_attr = _thr_stack_initial; + thread->attr.guardsize_attr = _thr_guard_default; + thread->attr.flags |= THR_STACK_USER; + + /* + * Write a magic value to the thread structure + * to help identify valid ones: + */ + thread->magic = THR_MAGIC; + + thread->slice_usec = -1; + thread->cancelflags = PTHREAD_CANCEL_ENABLE | PTHREAD_CANCEL_DEFERRED; + thread->name = strdup("initial thread"); + + /* Initialize the thread for signals: */ + SIGEMPTYSET(thread->sigmask); + + /* + * Set up the thread mailbox. The threads saved context + * is also in the mailbox. + */ + thread->tcb->tcb_tmbx.tm_udata = thread; + thread->tcb->tcb_tmbx.tm_context.uc_stack.ss_size = + thread->attr.stacksize_attr; + thread->tcb->tcb_tmbx.tm_context.uc_stack.ss_sp = + thread->attr.stackaddr_attr; + + /* Default the priority of the initial thread: */ + thread->base_priority = THR_DEFAULT_PRIORITY; + thread->active_priority = THR_DEFAULT_PRIORITY; + thread->inherited_priority = 0; + + /* Initialize the mutex queue: */ + TAILQ_INIT(&thread->mutexq); + + /* Initialize hooks in the thread structure: */ + thread->specific = NULL; + thread->cleanup = NULL; + thread->flags = 0; + thread->sigbackout = NULL; + thread->continuation = NULL; + + thread->state = PS_RUNNING; + thread->uniqueid = 0; +} + +static void +init_private(void) +{ + struct clockinfo clockinfo; + size_t len; + int mib[2]; + + /* + * Avoid reinitializing some things if they don't need to be, + * e.g. after a fork(). + */ + if (init_once == 0) { + /* Find the stack top */ + mib[0] = CTL_KERN; + mib[1] = KERN_USRSTACK; + len = sizeof (_usrstack); + if (sysctl(mib, 2, &_usrstack, &len, NULL, 0) == -1) + PANIC("Cannot get kern.usrstack from sysctl"); + /* Get the kernel clockrate: */ + mib[0] = CTL_KERN; + mib[1] = KERN_CLOCKRATE; + len = sizeof (struct clockinfo); + if (sysctl(mib, 2, &clockinfo, &len, NULL, 0) == 0) + _clock_res_usec = 1000000 / clockinfo.stathz; + else + _clock_res_usec = CLOCK_RES_USEC; + + _thr_page_size = getpagesize(); + _thr_guard_default = _thr_page_size; + if (sizeof(void *) == 8) { + _thr_stack_default = THR_STACK64_DEFAULT; + _thr_stack_initial = THR_STACK64_INITIAL; + } + else { + _thr_stack_default = THR_STACK32_DEFAULT; + _thr_stack_initial = THR_STACK32_INITIAL; + } + _pthread_attr_default.guardsize_attr = _thr_guard_default; + _pthread_attr_default.stacksize_attr = _thr_stack_default; + TAILQ_INIT(&_thr_atfork_list); + init_once = 1; /* Don't do this again. */ + } else { + /* + * Destroy the locks before creating them. We don't + * know what state they are in so it is better to just + * recreate them. + */ + _lock_destroy(&_thread_signal_lock); + _lock_destroy(&_mutex_static_lock); + _lock_destroy(&_rwlock_static_lock); + _lock_destroy(&_keytable_lock); + } + + /* Initialize everything else. */ + TAILQ_INIT(&_thread_list); + TAILQ_INIT(&_thread_gc_list); + _pthread_mutex_init(&_thr_atfork_mutex, NULL); + + /* + * Initialize the lock for temporary installation of signal + * handlers (to support sigwait() semantics) and for the + * process signal mask and pending signal sets. + */ + if (_lock_init(&_thread_signal_lock, LCK_ADAPTIVE, + _kse_lock_wait, _kse_lock_wakeup) != 0) + PANIC("Cannot initialize _thread_signal_lock"); + if (_lock_init(&_mutex_static_lock, LCK_ADAPTIVE, + _thr_lock_wait, _thr_lock_wakeup) != 0) + PANIC("Cannot initialize mutex static init lock"); + if (_lock_init(&_rwlock_static_lock, LCK_ADAPTIVE, + _thr_lock_wait, _thr_lock_wakeup) != 0) + PANIC("Cannot initialize rwlock static init lock"); + if (_lock_init(&_keytable_lock, LCK_ADAPTIVE, + _thr_lock_wait, _thr_lock_wakeup) != 0) + PANIC("Cannot initialize thread specific keytable lock"); + _thr_spinlock_init(); + + /* Clear pending signals and get the process signal mask. */ + SIGEMPTYSET(_thr_proc_sigpending); + + /* Are we in M:N mode (default) or 1:1 mode? */ +#ifdef SYSTEM_SCOPE_ONLY + _thread_scope_system = 1; +#else + if (getenv("LIBPTHREAD_SYSTEM_SCOPE") != NULL) + _thread_scope_system = 1; + else if (getenv("LIBPTHREAD_PROCESS_SCOPE") != NULL) + _thread_scope_system = -1; +#endif + if (getenv("LIBPTHREAD_DEBUG") != NULL) + _thr_debug_flags |= DBG_INFO_DUMP; + + /* + * _thread_list_lock and _kse_count are initialized + * by _kse_init() + */ +} diff --git a/lib/libpthread/thread/thr_join.c b/lib/libpthread/thread/thr_join.c new file mode 100644 index 0000000..1a3452e --- /dev/null +++ b/lib/libpthread/thread/thr_join.c @@ -0,0 +1,165 @@ +/* + * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_join); +LT10_COMPAT_DEFAULT(pthread_join); + +__weak_reference(_pthread_join, pthread_join); + +int +_pthread_join(pthread_t pthread, void **thread_return) +{ + struct pthread *curthread = _get_curthread(); + void *tmp; + kse_critical_t crit; + int ret = 0; + + _thr_cancel_enter(curthread); + + /* Check if the caller has specified an invalid thread: */ + if (pthread == NULL || pthread->magic != THR_MAGIC) { + /* Invalid thread: */ + _thr_cancel_leave(curthread, 1); + return (EINVAL); + } + + /* Check if the caller has specified itself: */ + if (pthread == curthread) { + /* Avoid a deadlock condition: */ + _thr_cancel_leave(curthread, 1); + return (EDEADLK); + } + + /* + * Find the thread in the list of active threads or in the + * list of dead threads: + */ + if ((ret = _thr_ref_add(curthread, pthread, /*include dead*/1)) != 0) { + /* Return an error: */ + _thr_cancel_leave(curthread, 1); + return (ESRCH); + } + + THR_SCHED_LOCK(curthread, pthread); + /* Check if this thread has been detached: */ + if ((pthread->attr.flags & PTHREAD_DETACHED) != 0) { + THR_SCHED_UNLOCK(curthread, pthread); + /* Remove the reference and return an error: */ + _thr_ref_delete(curthread, pthread); + ret = ESRCH; + } else { + /* Lock the target thread while checking its state. */ + if (pthread->state == PS_DEAD) { + /* Return the thread's return value: */ + tmp = pthread->ret; + + /* Detach the thread. */ + pthread->attr.flags |= PTHREAD_DETACHED; + + /* Unlock the thread. */ + THR_SCHED_UNLOCK(curthread, pthread); + + /* + * Remove the thread from the list of active + * threads and add it to the GC list. + */ + crit = _kse_critical_enter(); + KSE_LOCK_ACQUIRE(curthread->kse, &_thread_list_lock); + THR_LIST_REMOVE(pthread); + THR_GCLIST_ADD(pthread); + KSE_LOCK_RELEASE(curthread->kse, &_thread_list_lock); + _kse_critical_leave(crit); + + /* Remove the reference. */ + _thr_ref_delete(curthread, pthread); + if (thread_return != NULL) + *thread_return = tmp; + } + else if (pthread->joiner != NULL) { + /* Unlock the thread and remove the reference. */ + THR_SCHED_UNLOCK(curthread, pthread); + _thr_ref_delete(curthread, pthread); + + /* Multiple joiners are not supported. */ + ret = ENOTSUP; + } + else { + /* Set the running thread to be the joiner: */ + pthread->joiner = curthread; + + /* Keep track of which thread we're joining to: */ + curthread->join_status.thread = pthread; + + /* Unlock the thread and remove the reference. */ + THR_SCHED_UNLOCK(curthread, pthread); + _thr_ref_delete(curthread, pthread); + + THR_SCHED_LOCK(curthread, curthread); + while (curthread->join_status.thread == pthread) { + THR_SET_STATE(curthread, PS_JOIN); + THR_SCHED_UNLOCK(curthread, curthread); + /* Schedule the next thread: */ + _thr_sched_switch(curthread); + THR_SCHED_LOCK(curthread, curthread); + } + THR_SCHED_UNLOCK(curthread, curthread); + + if ((curthread->cancelflags & THR_CANCELLING) && + !(curthread->cancelflags & PTHREAD_CANCEL_DISABLE)) { + if (_thr_ref_add(curthread, pthread, 1) == 0) { + THR_SCHED_LOCK(curthread, pthread); + pthread->joiner = NULL; + THR_SCHED_UNLOCK(curthread, pthread); + _thr_ref_delete(curthread, pthread); + } + pthread_exit(PTHREAD_CANCELED); + } + + /* + * The thread return value and error are set by the + * thread we're joining to when it exits or detaches: + */ + ret = curthread->join_status.error; + if ((ret == 0) && (thread_return != NULL)) + *thread_return = curthread->join_status.ret; + } + } + _thr_cancel_leave(curthread, 1); + + /* Return the completion status: */ + return (ret); +} diff --git a/lib/libpthread/thread/thr_kern.c b/lib/libpthread/thread/thr_kern.c new file mode 100644 index 0000000..f1b28c0 --- /dev/null +++ b/lib/libpthread/thread/thr_kern.c @@ -0,0 +1,2551 @@ +/* + * Copyright (C) 2003 Daniel M. Eischen <deischen@freebsd.org> + * Copyright (C) 2002 Jonathon Mini <mini@freebsd.org> + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/kse.h> +#include <sys/ptrace.h> +#include <sys/signalvar.h> +#include <sys/queue.h> +#include <machine/atomic.h> +#include <machine/sigframe.h> + +#include <assert.h> +#include <errno.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <ucontext.h> +#include <unistd.h> + +#include "atomic_ops.h" +#include "thr_private.h" +#include "libc_private.h" +#ifdef NOTYET +#include "spinlock.h" +#endif + +/* #define DEBUG_THREAD_KERN */ +#ifdef DEBUG_THREAD_KERN +#define DBG_MSG stdout_debug +#else +#define DBG_MSG(x...) +#endif + +/* + * Define a high water mark for the maximum number of threads that + * will be cached. Once this level is reached, any extra threads + * will be free()'d. + */ +#define MAX_CACHED_THREADS 100 +/* + * Define high water marks for the maximum number of KSEs and KSE groups + * that will be cached. Because we support 1:1 threading, there could have + * same number of KSEs and KSE groups as threads. Once these levels are + * reached, any extra KSE and KSE groups will be free()'d. + */ +#define MAX_CACHED_KSES ((_thread_scope_system <= 0) ? 50 : 100) +#define MAX_CACHED_KSEGS ((_thread_scope_system <= 0) ? 50 : 100) + +#define KSE_SET_MBOX(kse, thrd) \ + (kse)->k_kcb->kcb_kmbx.km_curthread = &(thrd)->tcb->tcb_tmbx + +#define KSE_SET_EXITED(kse) (kse)->k_flags |= KF_EXITED + +/* + * Macros for manipulating the run queues. The priority queue + * routines use the thread's pqe link and also handle the setting + * and clearing of the thread's THR_FLAGS_IN_RUNQ flag. + */ +#define KSE_RUNQ_INSERT_HEAD(kse, thrd) \ + _pq_insert_head(&(kse)->k_schedq->sq_runq, thrd) +#define KSE_RUNQ_INSERT_TAIL(kse, thrd) \ + _pq_insert_tail(&(kse)->k_schedq->sq_runq, thrd) +#define KSE_RUNQ_REMOVE(kse, thrd) \ + _pq_remove(&(kse)->k_schedq->sq_runq, thrd) +#define KSE_RUNQ_FIRST(kse) \ + ((_libkse_debug == 0) ? \ + _pq_first(&(kse)->k_schedq->sq_runq) : \ + _pq_first_debug(&(kse)->k_schedq->sq_runq)) + +#define KSE_RUNQ_THREADS(kse) ((kse)->k_schedq->sq_runq.pq_threads) + +#define THR_NEED_CANCEL(thrd) \ + (((thrd)->cancelflags & THR_CANCELLING) != 0 && \ + ((thrd)->cancelflags & PTHREAD_CANCEL_DISABLE) == 0 && \ + (((thrd)->cancelflags & THR_AT_CANCEL_POINT) != 0 || \ + ((thrd)->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0)) + +#define THR_NEED_ASYNC_CANCEL(thrd) \ + (((thrd)->cancelflags & THR_CANCELLING) != 0 && \ + ((thrd)->cancelflags & PTHREAD_CANCEL_DISABLE) == 0 && \ + (((thrd)->cancelflags & THR_AT_CANCEL_POINT) == 0 && \ + ((thrd)->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0)) + +/* + * We've got to keep track of everything that is allocated, not only + * to have a speedy free list, but also so they can be deallocated + * after a fork(). + */ +static TAILQ_HEAD(, kse) active_kseq; +static TAILQ_HEAD(, kse) free_kseq; +static TAILQ_HEAD(, kse_group) free_kse_groupq; +static TAILQ_HEAD(, kse_group) active_kse_groupq; +static TAILQ_HEAD(, kse_group) gc_ksegq; +static struct lock kse_lock; /* also used for kseg queue */ +static int free_kse_count = 0; +static int free_kseg_count = 0; +static TAILQ_HEAD(, pthread) free_threadq; +static struct lock thread_lock; +static int free_thread_count = 0; +static int inited = 0; +static int active_kse_count = 0; +static int active_kseg_count = 0; +static u_int64_t next_uniqueid = 1; + +LIST_HEAD(thread_hash_head, pthread); +#define THREAD_HASH_QUEUES 127 +static struct thread_hash_head thr_hashtable[THREAD_HASH_QUEUES]; +#define THREAD_HASH(thrd) ((unsigned long)thrd % THREAD_HASH_QUEUES) + +/* Lock for thread tcb constructor/destructor */ +static pthread_mutex_t _tcb_mutex; + +#ifdef DEBUG_THREAD_KERN +static void dump_queues(struct kse *curkse); +#endif +static void kse_check_completed(struct kse *kse); +static void kse_check_waitq(struct kse *kse); +static void kse_fini(struct kse *curkse); +static void kse_reinit(struct kse *kse, int sys_scope); +static void kse_sched_multi(struct kse_mailbox *kmbx); +static void kse_sched_single(struct kse_mailbox *kmbx); +static void kse_switchout_thread(struct kse *kse, struct pthread *thread); +static void kse_wait(struct kse *kse, struct pthread *td_wait, int sigseq); +static void kse_free_unlocked(struct kse *kse); +static void kse_destroy(struct kse *kse); +static void kseg_free_unlocked(struct kse_group *kseg); +static void kseg_init(struct kse_group *kseg); +static void kseg_reinit(struct kse_group *kseg); +static void kseg_destroy(struct kse_group *kseg); +static void kse_waitq_insert(struct pthread *thread); +static void kse_wakeup_multi(struct kse *curkse); +static struct kse_mailbox *kse_wakeup_one(struct pthread *thread); +static void thr_cleanup(struct kse *kse, struct pthread *curthread); +static void thr_link(struct pthread *thread); +static void thr_resume_wrapper(int sig, siginfo_t *, ucontext_t *); +static void thr_resume_check(struct pthread *curthread, ucontext_t *ucp); +static int thr_timedout(struct pthread *thread, struct timespec *curtime); +static void thr_unlink(struct pthread *thread); +static void thr_destroy(struct pthread *curthread, struct pthread *thread); +static void thread_gc(struct pthread *thread); +static void kse_gc(struct pthread *thread); +static void kseg_gc(struct pthread *thread); + +static void __inline +thr_accounting(struct pthread *thread) +{ + if ((thread->slice_usec != -1) && + (thread->slice_usec <= TIMESLICE_USEC) && + (thread->attr.sched_policy != SCHED_FIFO)) { + thread->slice_usec += (thread->tcb->tcb_tmbx.tm_uticks + + thread->tcb->tcb_tmbx.tm_sticks) * _clock_res_usec; + /* Check for time quantum exceeded: */ + if (thread->slice_usec > TIMESLICE_USEC) + thread->slice_usec = -1; + } + thread->tcb->tcb_tmbx.tm_uticks = 0; + thread->tcb->tcb_tmbx.tm_sticks = 0; +} + +/* + * This is called after a fork(). + * No locks need to be taken here since we are guaranteed to be + * single threaded. + * + * XXX + * POSIX says for threaded process, fork() function is used + * only to run new programs, and the effects of calling functions + * that require certain resources between the call to fork() and + * the call to an exec function are undefined. + * + * It is not safe to free memory after fork(), because these data + * structures may be in inconsistent state. + */ +void +_kse_single_thread(struct pthread *curthread) +{ +#ifdef NOTYET + struct kse *kse; + struct kse_group *kseg; + struct pthread *thread; + + _thr_spinlock_init(); + *__malloc_lock = (spinlock_t)_SPINLOCK_INITIALIZER; + if (__isthreaded) { + _thr_rtld_fini(); + _thr_signal_deinit(); + } + __isthreaded = 0; + /* + * Restore signal mask early, so any memory problems could + * dump core. + */ + __sys_sigprocmask(SIG_SETMASK, &curthread->sigmask, NULL); + _thread_active_threads = 1; + + /* + * Enter a loop to remove and free all threads other than + * the running thread from the active thread list: + */ + while ((thread = TAILQ_FIRST(&_thread_list)) != NULL) { + THR_GCLIST_REMOVE(thread); + /* + * Remove this thread from the list (the current + * thread will be removed but re-added by libpthread + * initialization. + */ + TAILQ_REMOVE(&_thread_list, thread, tle); + /* Make sure this isn't the running thread: */ + if (thread != curthread) { + _thr_stack_free(&thread->attr); + if (thread->specific != NULL) + free(thread->specific); + thr_destroy(curthread, thread); + } + } + + TAILQ_INIT(&curthread->mutexq); /* initialize mutex queue */ + curthread->joiner = NULL; /* no joining threads yet */ + curthread->refcount = 0; + SIGEMPTYSET(curthread->sigpend); /* clear pending signals */ + + /* Don't free thread-specific data as the caller may require it */ + + /* Free the free KSEs: */ + while ((kse = TAILQ_FIRST(&free_kseq)) != NULL) { + TAILQ_REMOVE(&free_kseq, kse, k_qe); + kse_destroy(kse); + } + free_kse_count = 0; + + /* Free the active KSEs: */ + while ((kse = TAILQ_FIRST(&active_kseq)) != NULL) { + TAILQ_REMOVE(&active_kseq, kse, k_qe); + kse_destroy(kse); + } + active_kse_count = 0; + + /* Free the free KSEGs: */ + while ((kseg = TAILQ_FIRST(&free_kse_groupq)) != NULL) { + TAILQ_REMOVE(&free_kse_groupq, kseg, kg_qe); + kseg_destroy(kseg); + } + free_kseg_count = 0; + + /* Free the active KSEGs: */ + while ((kseg = TAILQ_FIRST(&active_kse_groupq)) != NULL) { + TAILQ_REMOVE(&active_kse_groupq, kseg, kg_qe); + kseg_destroy(kseg); + } + active_kseg_count = 0; + + /* Free the free threads. */ + while ((thread = TAILQ_FIRST(&free_threadq)) != NULL) { + TAILQ_REMOVE(&free_threadq, thread, tle); + thr_destroy(curthread, thread); + } + free_thread_count = 0; + + /* Free the to-be-gc'd threads. */ + while ((thread = TAILQ_FIRST(&_thread_gc_list)) != NULL) { + TAILQ_REMOVE(&_thread_gc_list, thread, gcle); + thr_destroy(curthread, thread); + } + TAILQ_INIT(&gc_ksegq); + _gc_count = 0; + + if (inited != 0) { + /* + * Destroy these locks; they'll be recreated to assure they + * are in the unlocked state. + */ + _lock_destroy(&kse_lock); + _lock_destroy(&thread_lock); + _lock_destroy(&_thread_list_lock); + inited = 0; + } + + /* + * After a fork(), the leftover thread goes back to being + * scope process. + */ + curthread->attr.flags &= ~PTHREAD_SCOPE_SYSTEM; + curthread->attr.flags |= PTHREAD_SCOPE_PROCESS; + + /* We're no longer part of any lists */ + curthread->tlflags = 0; + + /* + * After a fork, we are still operating on the thread's original + * stack. Don't clear the THR_FLAGS_USER from the thread's + * attribute flags. + */ + + /* Initialize the threads library. */ + curthread->kse = NULL; + curthread->kseg = NULL; + _kse_initial = NULL; + _libpthread_init(curthread); +#else + int i; + + /* Reset the current thread and KSE lock data. */ + for (i = 0; i < curthread->locklevel; i++) { + _lockuser_reinit(&curthread->lockusers[i], (void *)curthread); + } + curthread->locklevel = 0; + for (i = 0; i < curthread->kse->k_locklevel; i++) { + _lockuser_reinit(&curthread->kse->k_lockusers[i], + (void *)curthread->kse); + _LCK_SET_PRIVATE2(&curthread->kse->k_lockusers[i], NULL); + } + curthread->kse->k_locklevel = 0; + _thr_spinlock_init(); + if (__isthreaded) { + _thr_rtld_fini(); + _thr_signal_deinit(); + } + __isthreaded = 0; + curthread->kse->k_kcb->kcb_kmbx.km_curthread = NULL; + curthread->attr.flags |= PTHREAD_SCOPE_SYSTEM; + + /* After a fork(), there child should have no pending signals. */ + sigemptyset(&curthread->sigpend); + + /* + * Restore signal mask early, so any memory problems could + * dump core. + */ + sigprocmask(SIG_SETMASK, &curthread->sigmask, NULL); + _thread_active_threads = 1; +#endif +} + +/* + * This is used to initialize housekeeping and to initialize the + * KSD for the KSE. + */ +void +_kse_init(void) +{ + if (inited == 0) { + TAILQ_INIT(&active_kseq); + TAILQ_INIT(&active_kse_groupq); + TAILQ_INIT(&free_kseq); + TAILQ_INIT(&free_kse_groupq); + TAILQ_INIT(&free_threadq); + TAILQ_INIT(&gc_ksegq); + if (_lock_init(&kse_lock, LCK_ADAPTIVE, + _kse_lock_wait, _kse_lock_wakeup) != 0) + PANIC("Unable to initialize free KSE queue lock"); + if (_lock_init(&thread_lock, LCK_ADAPTIVE, + _kse_lock_wait, _kse_lock_wakeup) != 0) + PANIC("Unable to initialize free thread queue lock"); + if (_lock_init(&_thread_list_lock, LCK_ADAPTIVE, + _kse_lock_wait, _kse_lock_wakeup) != 0) + PANIC("Unable to initialize thread list lock"); + _pthread_mutex_init(&_tcb_mutex, NULL); + active_kse_count = 0; + active_kseg_count = 0; + _gc_count = 0; + inited = 1; + } +} + +/* + * This is called when the first thread (other than the initial + * thread) is created. + */ +int +_kse_setthreaded(int threaded) +{ + sigset_t sigset; + + if ((threaded != 0) && (__isthreaded == 0)) { + SIGFILLSET(sigset); + __sys_sigprocmask(SIG_SETMASK, &sigset, &_thr_initial->sigmask); + + /* + * Tell the kernel to create a KSE for the initial thread + * and enable upcalls in it. + */ + _kse_initial->k_flags |= KF_STARTED; + + if (_thread_scope_system <= 0) { + _thr_initial->attr.flags &= ~PTHREAD_SCOPE_SYSTEM; + _kse_initial->k_kseg->kg_flags &= ~KGF_SINGLE_THREAD; + _kse_initial->k_kcb->kcb_kmbx.km_curthread = NULL; + } + else { + /* + * For bound thread, kernel reads mailbox pointer + * once, we'd set it here before calling kse_create. + */ + _tcb_set(_kse_initial->k_kcb, _thr_initial->tcb); + KSE_SET_MBOX(_kse_initial, _thr_initial); + _kse_initial->k_kcb->kcb_kmbx.km_flags |= KMF_BOUND; + } + + /* + * Locking functions in libc are required when there are + * threads other than the initial thread. + */ + _thr_rtld_init(); + + __isthreaded = 1; + if (kse_create(&_kse_initial->k_kcb->kcb_kmbx, 0) != 0) { + _kse_initial->k_flags &= ~KF_STARTED; + __isthreaded = 0; + PANIC("kse_create() failed\n"); + return (-1); + } + _thr_initial->tcb->tcb_tmbx.tm_lwp = + _kse_initial->k_kcb->kcb_kmbx.km_lwp; + _thread_activated = 1; + +#ifndef SYSTEM_SCOPE_ONLY + if (_thread_scope_system <= 0) { + /* Set current thread to initial thread */ + _tcb_set(_kse_initial->k_kcb, _thr_initial->tcb); + KSE_SET_MBOX(_kse_initial, _thr_initial); + _thr_start_sig_daemon(); + _thr_setmaxconcurrency(); + } + else +#endif + __sys_sigprocmask(SIG_SETMASK, &_thr_initial->sigmask, + NULL); + } + return (0); +} + +/* + * Lock wait and wakeup handlers for KSE locks. These are only used by + * KSEs, and should never be used by threads. KSE locks include the + * KSE group lock (used for locking the scheduling queue) and the + * kse_lock defined above. + * + * When a KSE lock attempt blocks, the entire KSE blocks allowing another + * KSE to run. For the most part, it doesn't make much sense to try and + * schedule another thread because you need to lock the scheduling queue + * in order to do that. And since the KSE lock is used to lock the scheduling + * queue, you would just end up blocking again. + */ +void +_kse_lock_wait(struct lock *lock, struct lockuser *lu) +{ + struct kse *curkse = (struct kse *)_LCK_GET_PRIVATE(lu); + struct timespec ts; + int saved_flags; + + if (curkse->k_kcb->kcb_kmbx.km_curthread != NULL) + PANIC("kse_lock_wait does not disable upcall.\n"); + /* + * Enter a loop to wait until we get the lock. + */ + ts.tv_sec = 0; + ts.tv_nsec = 1000000; /* 1 sec */ + while (!_LCK_GRANTED(lu)) { + /* + * Yield the kse and wait to be notified when the lock + * is granted. + */ + saved_flags = curkse->k_kcb->kcb_kmbx.km_flags; + curkse->k_kcb->kcb_kmbx.km_flags |= KMF_NOUPCALL | + KMF_NOCOMPLETED; + kse_release(&ts); + curkse->k_kcb->kcb_kmbx.km_flags = saved_flags; + } +} + +void +_kse_lock_wakeup(struct lock *lock, struct lockuser *lu) +{ + struct kse *curkse; + struct kse *kse; + struct kse_mailbox *mbx; + + curkse = _get_curkse(); + kse = (struct kse *)_LCK_GET_PRIVATE(lu); + + if (kse == curkse) + PANIC("KSE trying to wake itself up in lock"); + else { + mbx = &kse->k_kcb->kcb_kmbx; + _lock_grant(lock, lu); + /* + * Notify the owning kse that it has the lock. + * It is safe to pass invalid address to kse_wakeup + * even if the mailbox is not in kernel at all, + * and waking up a wrong kse is also harmless. + */ + kse_wakeup(mbx); + } +} + +/* + * Thread wait and wakeup handlers for thread locks. These are only used + * by threads, never by KSEs. Thread locks include the per-thread lock + * (defined in its structure), and condition variable and mutex locks. + */ +void +_thr_lock_wait(struct lock *lock, struct lockuser *lu) +{ + struct pthread *curthread = (struct pthread *)lu->lu_private; + + do { + THR_LOCK_SWITCH(curthread); + THR_SET_STATE(curthread, PS_LOCKWAIT); + _thr_sched_switch_unlocked(curthread); + } while (!_LCK_GRANTED(lu)); +} + +void +_thr_lock_wakeup(struct lock *lock, struct lockuser *lu) +{ + struct pthread *thread; + struct pthread *curthread; + struct kse_mailbox *kmbx; + + curthread = _get_curthread(); + thread = (struct pthread *)_LCK_GET_PRIVATE(lu); + + THR_SCHED_LOCK(curthread, thread); + _lock_grant(lock, lu); + kmbx = _thr_setrunnable_unlocked(thread); + THR_SCHED_UNLOCK(curthread, thread); + if (kmbx != NULL) + kse_wakeup(kmbx); +} + +kse_critical_t +_kse_critical_enter(void) +{ + kse_critical_t crit; + + crit = (kse_critical_t)_kcb_critical_enter(); + return (crit); +} + +void +_kse_critical_leave(kse_critical_t crit) +{ + struct pthread *curthread; + + _kcb_critical_leave((struct kse_thr_mailbox *)crit); + if ((crit != NULL) && ((curthread = _get_curthread()) != NULL)) + THR_YIELD_CHECK(curthread); +} + +int +_kse_in_critical(void) +{ + return (_kcb_in_critical()); +} + +void +_thr_critical_enter(struct pthread *thread) +{ + thread->critical_count++; +} + +void +_thr_critical_leave(struct pthread *thread) +{ + thread->critical_count--; + THR_YIELD_CHECK(thread); +} + +void +_thr_sched_switch(struct pthread *curthread) +{ + struct kse *curkse; + + (void)_kse_critical_enter(); + curkse = _get_curkse(); + KSE_SCHED_LOCK(curkse, curkse->k_kseg); + _thr_sched_switch_unlocked(curthread); +} + +/* + * XXX - We may need to take the scheduling lock before calling + * this, or perhaps take the lock within here before + * doing anything else. + */ +void +_thr_sched_switch_unlocked(struct pthread *curthread) +{ + struct kse *curkse; + volatile int resume_once = 0; + ucontext_t *uc; + + /* We're in the scheduler, 5 by 5: */ + curkse = curthread->kse; + + curthread->need_switchout = 1; /* The thread yielded on its own. */ + curthread->critical_yield = 0; /* No need to yield anymore. */ + + /* Thread can unlock the scheduler lock. */ + curthread->lock_switch = 1; + + if (curthread->attr.flags & PTHREAD_SCOPE_SYSTEM) + kse_sched_single(&curkse->k_kcb->kcb_kmbx); + else { + if (__predict_false(_libkse_debug != 0)) { + /* + * Because debugger saves single step status in thread + * mailbox's tm_dflags, we can safely clear single + * step status here. the single step status will be + * restored by kse_switchin when the thread is + * switched in again. This also lets uts run in full + * speed. + */ + ptrace(PT_CLEARSTEP, curkse->k_kcb->kcb_kmbx.km_lwp, + (caddr_t) 1, 0); + } + + KSE_SET_SWITCH(curkse); + _thread_enter_uts(curthread->tcb, curkse->k_kcb); + } + + /* + * Unlock the scheduling queue and leave the + * critical region. + */ + /* Don't trust this after a switch! */ + curkse = curthread->kse; + + curthread->lock_switch = 0; + KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); + _kse_critical_leave(&curthread->tcb->tcb_tmbx); + + /* + * This thread is being resumed; check for cancellations. + */ + if (THR_NEED_ASYNC_CANCEL(curthread) && !THR_IN_CRITICAL(curthread)) { + uc = alloca(sizeof(ucontext_t)); + resume_once = 0; + THR_GETCONTEXT(uc); + if (resume_once == 0) { + resume_once = 1; + curthread->check_pending = 0; + thr_resume_check(curthread, uc); + } + } + THR_ACTIVATE_LAST_LOCK(curthread); +} + +/* + * This is the scheduler for a KSE which runs a scope system thread. + * The multi-thread KSE scheduler should also work for a single threaded + * KSE, but we use a separate scheduler so that it can be fine-tuned + * to be more efficient (and perhaps not need a separate stack for + * the KSE, allowing it to use the thread's stack). + */ + +static void +kse_sched_single(struct kse_mailbox *kmbx) +{ + struct kse *curkse; + struct pthread *curthread; + struct timespec ts; + sigset_t sigmask; + int i, sigseqno, level, first = 0; + + curkse = (struct kse *)kmbx->km_udata; + curthread = curkse->k_curthread; + + if (__predict_false((curkse->k_flags & KF_INITIALIZED) == 0)) { + /* Setup this KSEs specific data. */ + _kcb_set(curkse->k_kcb); + _tcb_set(curkse->k_kcb, curthread->tcb); + curkse->k_flags |= KF_INITIALIZED; + first = 1; + curthread->active = 1; + + /* Setup kernel signal masks for new thread. */ + __sys_sigprocmask(SIG_SETMASK, &curthread->sigmask, NULL); + /* + * Enter critical region, this is meanless for bound thread, + * It is used to let other code work, those code want mailbox + * to be cleared. + */ + (void)_kse_critical_enter(); + } else { + /* + * Bound thread always has tcb set, this prevent some + * code from blindly setting bound thread tcb to NULL, + * buggy code ? + */ + _tcb_set(curkse->k_kcb, curthread->tcb); + } + + curthread->critical_yield = 0; + curthread->need_switchout = 0; + + /* + * Lock the scheduling queue. + * + * There is no scheduling queue for single threaded KSEs, + * but we need a lock for protection regardless. + */ + if (curthread->lock_switch == 0) + KSE_SCHED_LOCK(curkse, curkse->k_kseg); + + /* + * This has to do the job of kse_switchout_thread(), only + * for a single threaded KSE/KSEG. + */ + + switch (curthread->state) { + case PS_MUTEX_WAIT: + case PS_COND_WAIT: + if (THR_NEED_CANCEL(curthread)) { + curthread->interrupted = 1; + curthread->continuation = _thr_finish_cancellation; + THR_SET_STATE(curthread, PS_RUNNING); + } + break; + + case PS_LOCKWAIT: + /* + * This state doesn't timeout. + */ + curthread->wakeup_time.tv_sec = -1; + curthread->wakeup_time.tv_nsec = -1; + level = curthread->locklevel - 1; + if (_LCK_GRANTED(&curthread->lockusers[level])) + THR_SET_STATE(curthread, PS_RUNNING); + break; + + case PS_DEAD: + curthread->check_pending = 0; + /* Unlock the scheduling queue and exit the KSE and thread. */ + thr_cleanup(curkse, curthread); + KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); + PANIC("bound thread shouldn't get here\n"); + break; + + case PS_JOIN: + if (THR_NEED_CANCEL(curthread)) { + curthread->join_status.thread = NULL; + THR_SET_STATE(curthread, PS_RUNNING); + } else { + /* + * This state doesn't timeout. + */ + curthread->wakeup_time.tv_sec = -1; + curthread->wakeup_time.tv_nsec = -1; + } + break; + + case PS_SUSPENDED: + if (THR_NEED_CANCEL(curthread)) { + curthread->interrupted = 1; + THR_SET_STATE(curthread, PS_RUNNING); + } else { + /* + * These states don't timeout. + */ + curthread->wakeup_time.tv_sec = -1; + curthread->wakeup_time.tv_nsec = -1; + } + break; + + case PS_RUNNING: + if ((curthread->flags & THR_FLAGS_SUSPENDED) != 0 && + !THR_NEED_CANCEL(curthread)) { + THR_SET_STATE(curthread, PS_SUSPENDED); + /* + * These states don't timeout. + */ + curthread->wakeup_time.tv_sec = -1; + curthread->wakeup_time.tv_nsec = -1; + } + break; + + case PS_SIGWAIT: + PANIC("bound thread does not have SIGWAIT state\n"); + + case PS_SLEEP_WAIT: + PANIC("bound thread does not have SLEEP_WAIT state\n"); + + case PS_SIGSUSPEND: + PANIC("bound thread does not have SIGSUSPEND state\n"); + + case PS_DEADLOCK: + /* + * These states don't timeout and don't need + * to be in the waiting queue. + */ + curthread->wakeup_time.tv_sec = -1; + curthread->wakeup_time.tv_nsec = -1; + break; + + default: + PANIC("Unknown state\n"); + break; + } + + while (curthread->state != PS_RUNNING) { + sigseqno = curkse->k_sigseqno; + if (curthread->check_pending != 0) { + /* + * Install pending signals into the frame, possible + * cause mutex or condvar backout. + */ + curthread->check_pending = 0; + SIGFILLSET(sigmask); + + /* + * Lock out kernel signal code when we are processing + * signals, and get a fresh copy of signal mask. + */ + __sys_sigprocmask(SIG_SETMASK, &sigmask, + &curthread->sigmask); + for (i = 1; i <= _SIG_MAXSIG; i++) { + if (SIGISMEMBER(curthread->sigmask, i)) + continue; + if (SIGISMEMBER(curthread->sigpend, i)) + (void)_thr_sig_add(curthread, i, + &curthread->siginfo[i-1]); + } + __sys_sigprocmask(SIG_SETMASK, &curthread->sigmask, + NULL); + /* The above code might make thread runnable */ + if (curthread->state == PS_RUNNING) + break; + } + THR_DEACTIVATE_LAST_LOCK(curthread); + kse_wait(curkse, curthread, sigseqno); + THR_ACTIVATE_LAST_LOCK(curthread); + if (curthread->wakeup_time.tv_sec >= 0) { + KSE_GET_TOD(curkse, &ts); + if (thr_timedout(curthread, &ts)) { + /* Indicate the thread timedout: */ + curthread->timeout = 1; + /* Make the thread runnable. */ + THR_SET_STATE(curthread, PS_RUNNING); + } + } + } + + if (curthread->lock_switch == 0) { + /* Unlock the scheduling queue. */ + KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); + } + + DBG_MSG("Continuing bound thread %p\n", curthread); + if (first) { + _kse_critical_leave(&curthread->tcb->tcb_tmbx); + pthread_exit(curthread->start_routine(curthread->arg)); + } +} + +#ifdef DEBUG_THREAD_KERN +static void +dump_queues(struct kse *curkse) +{ + struct pthread *thread; + + DBG_MSG("Threads in waiting queue:\n"); + TAILQ_FOREACH(thread, &curkse->k_kseg->kg_schedq.sq_waitq, pqe) { + DBG_MSG(" thread %p, state %d, blocked %d\n", + thread, thread->state, thread->blocked); + } +} +#endif + +/* + * This is the scheduler for a KSE which runs multiple threads. + */ +static void +kse_sched_multi(struct kse_mailbox *kmbx) +{ + struct kse *curkse; + struct pthread *curthread, *td_wait; + int ret; + + curkse = (struct kse *)kmbx->km_udata; + THR_ASSERT(curkse->k_kcb->kcb_kmbx.km_curthread == NULL, + "Mailbox not null in kse_sched_multi"); + + /* Check for first time initialization: */ + if (__predict_false((curkse->k_flags & KF_INITIALIZED) == 0)) { + /* Setup this KSEs specific data. */ + _kcb_set(curkse->k_kcb); + + /* Set this before grabbing the context. */ + curkse->k_flags |= KF_INITIALIZED; + } + + /* + * No current thread anymore, calling _get_curthread in UTS + * should dump core + */ + _tcb_set(curkse->k_kcb, NULL); + + /* If this is an upcall; take the scheduler lock. */ + if (!KSE_IS_SWITCH(curkse)) + KSE_SCHED_LOCK(curkse, curkse->k_kseg); + else + KSE_CLEAR_SWITCH(curkse); + + if (KSE_IS_IDLE(curkse)) { + KSE_CLEAR_IDLE(curkse); + curkse->k_kseg->kg_idle_kses--; + } + + /* + * Now that the scheduler lock is held, get the current + * thread. The KSE's current thread cannot be safely + * examined without the lock because it could have returned + * as completed on another KSE. See kse_check_completed(). + */ + curthread = curkse->k_curthread; + + /* + * If the current thread was completed in another KSE, then + * it will be in the run queue. Don't mark it as being blocked. + */ + if ((curthread != NULL) && + ((curthread->flags & THR_FLAGS_IN_RUNQ) == 0) && + (curthread->need_switchout == 0)) { + /* + * Assume the current thread is blocked; when the + * completed threads are checked and if the current + * thread is among the completed, the blocked flag + * will be cleared. + */ + curthread->blocked = 1; + DBG_MSG("Running thread %p is now blocked in kernel.\n", + curthread); + } + + /* Check for any unblocked threads in the kernel. */ + kse_check_completed(curkse); + + /* + * Check for threads that have timed-out. + */ + kse_check_waitq(curkse); + + /* + * Switchout the current thread, if necessary, as the last step + * so that it is inserted into the run queue (if it's runnable) + * _after_ any other threads that were added to it above. + */ + if (curthread == NULL) + ; /* Nothing to do here. */ + else if ((curthread->need_switchout == 0) && DBG_CAN_RUN(curthread) && + (curthread->blocked == 0) && (THR_IN_CRITICAL(curthread))) { + /* + * Resume the thread and tell it to yield when + * it leaves the critical region. + */ + curthread->critical_yield = 1; + curthread->active = 1; + if ((curthread->flags & THR_FLAGS_IN_RUNQ) != 0) + KSE_RUNQ_REMOVE(curkse, curthread); + curkse->k_curthread = curthread; + curthread->kse = curkse; + DBG_MSG("Continuing thread %p in critical region\n", + curthread); + kse_wakeup_multi(curkse); + KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); + ret = _thread_switch(curkse->k_kcb, curthread->tcb, 1); + if (ret != 0) + PANIC("Can't resume thread in critical region\n"); + } + else if ((curthread->flags & THR_FLAGS_IN_RUNQ) == 0) { + curthread->tcb->tcb_tmbx.tm_lwp = 0; + kse_switchout_thread(curkse, curthread); + } + curkse->k_curthread = NULL; + +#ifdef DEBUG_THREAD_KERN + dump_queues(curkse); +#endif + + /* Check if there are no threads ready to run: */ + while (((curthread = KSE_RUNQ_FIRST(curkse)) == NULL) && + (curkse->k_kseg->kg_threadcount != 0) && + ((curkse->k_flags & KF_TERMINATED) == 0)) { + /* + * Wait for a thread to become active or until there are + * no more threads. + */ + td_wait = KSE_WAITQ_FIRST(curkse); + kse_wait(curkse, td_wait, 0); + kse_check_completed(curkse); + kse_check_waitq(curkse); + } + + /* Check for no more threads: */ + if ((curkse->k_kseg->kg_threadcount == 0) || + ((curkse->k_flags & KF_TERMINATED) != 0)) { + /* + * Normally this shouldn't return, but it will if there + * are other KSEs running that create new threads that + * are assigned to this KSE[G]. For instance, if a scope + * system thread were to create a scope process thread + * and this kse[g] is the initial kse[g], then that newly + * created thread would be assigned to us (the initial + * kse[g]). + */ + kse_wakeup_multi(curkse); + KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); + kse_fini(curkse); + /* never returns */ + } + + THR_ASSERT(curthread != NULL, + "Return from kse_wait/fini without thread."); + THR_ASSERT(curthread->state != PS_DEAD, + "Trying to resume dead thread!"); + KSE_RUNQ_REMOVE(curkse, curthread); + + /* + * Make the selected thread the current thread. + */ + curkse->k_curthread = curthread; + + /* + * Make sure the current thread's kse points to this kse. + */ + curthread->kse = curkse; + + /* + * Reset the time slice if this thread is running for the first + * time or running again after using its full time slice allocation. + */ + if (curthread->slice_usec == -1) + curthread->slice_usec = 0; + + /* Mark the thread active. */ + curthread->active = 1; + + /* + * The thread's current signal frame will only be NULL if it + * is being resumed after being blocked in the kernel. In + * this case, and if the thread needs to run down pending + * signals or needs a cancellation check, we need to add a + * signal frame to the thread's context. + */ + if (curthread->lock_switch == 0 && curthread->state == PS_RUNNING && + (curthread->check_pending != 0 || + THR_NEED_ASYNC_CANCEL(curthread)) && + !THR_IN_CRITICAL(curthread)) { + curthread->check_pending = 0; + signalcontext(&curthread->tcb->tcb_tmbx.tm_context, 0, + (__sighandler_t *)thr_resume_wrapper); + } + kse_wakeup_multi(curkse); + /* + * Continue the thread at its current frame: + */ + if (curthread->lock_switch != 0) { + /* + * This thread came from a scheduler switch; it will + * unlock the scheduler lock and set the mailbox. + */ + ret = _thread_switch(curkse->k_kcb, curthread->tcb, 0); + } else { + /* This thread won't unlock the scheduler lock. */ + KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); + ret = _thread_switch(curkse->k_kcb, curthread->tcb, 1); + } + if (ret != 0) + PANIC("Thread has returned from _thread_switch"); + + /* This point should not be reached. */ + PANIC("Thread has returned from _thread_switch"); +} + +static void +thr_resume_wrapper(int sig, siginfo_t *siginfo, ucontext_t *ucp) +{ + struct pthread *curthread = _get_curthread(); + struct kse *curkse; + int ret, err_save = errno; + + DBG_MSG(">>> sig wrapper\n"); + if (curthread->lock_switch) + PANIC("thr_resume_wrapper, lock_switch != 0\n"); + thr_resume_check(curthread, ucp); + errno = err_save; + _kse_critical_enter(); + curkse = curthread->kse; + curthread->tcb->tcb_tmbx.tm_context = *ucp; + ret = _thread_switch(curkse->k_kcb, curthread->tcb, 1); + if (ret != 0) + PANIC("thr_resume_wrapper: thread has returned " + "from _thread_switch"); + /* THR_SETCONTEXT(ucp); */ /* not work, why ? */ +} + +static void +thr_resume_check(struct pthread *curthread, ucontext_t *ucp) +{ + _thr_sig_rundown(curthread, ucp); + + if (THR_NEED_ASYNC_CANCEL(curthread)) + pthread_testcancel(); +} + +/* + * Clean up a thread. This must be called with the thread's KSE + * scheduling lock held. The thread must be a thread from the + * KSE's group. + */ +static void +thr_cleanup(struct kse *curkse, struct pthread *thread) +{ + struct pthread *joiner; + struct kse_mailbox *kmbx = NULL; + int sys_scope; + + if ((joiner = thread->joiner) != NULL) { + /* Joinee scheduler lock held; joiner won't leave. */ + if (joiner->kseg == curkse->k_kseg) { + if (joiner->join_status.thread == thread) { + joiner->join_status.thread = NULL; + joiner->join_status.ret = thread->ret; + (void)_thr_setrunnable_unlocked(joiner); + } + } else { + KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); + /* The joiner may have removed itself and exited. */ + if (_thr_ref_add(thread, joiner, 0) == 0) { + KSE_SCHED_LOCK(curkse, joiner->kseg); + if (joiner->join_status.thread == thread) { + joiner->join_status.thread = NULL; + joiner->join_status.ret = thread->ret; + kmbx = _thr_setrunnable_unlocked(joiner); + } + KSE_SCHED_UNLOCK(curkse, joiner->kseg); + _thr_ref_delete(thread, joiner); + if (kmbx != NULL) + kse_wakeup(kmbx); + } + KSE_SCHED_LOCK(curkse, curkse->k_kseg); + } + thread->attr.flags |= PTHREAD_DETACHED; + } + + if (!(sys_scope = (thread->attr.flags & PTHREAD_SCOPE_SYSTEM))) { + /* + * Remove the thread from the KSEG's list of threads. + */ + KSEG_THRQ_REMOVE(thread->kseg, thread); + /* + * Migrate the thread to the main KSE so that this + * KSE and KSEG can be cleaned when their last thread + * exits. + */ + thread->kseg = _kse_initial->k_kseg; + thread->kse = _kse_initial; + } + + /* + * We can't hold the thread list lock while holding the + * scheduler lock. + */ + KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); + DBG_MSG("Adding thread %p to GC list\n", thread); + KSE_LOCK_ACQUIRE(curkse, &_thread_list_lock); + thread->tlflags |= TLFLAGS_GC_SAFE; + THR_GCLIST_ADD(thread); + KSE_LOCK_RELEASE(curkse, &_thread_list_lock); + if (sys_scope) { + /* + * System scope thread is single thread group, + * when thread is exited, its kse and ksegrp should + * be recycled as well. + * kse upcall stack belongs to thread, clear it here. + */ + curkse->k_stack.ss_sp = 0; + curkse->k_stack.ss_size = 0; + kse_exit(); + PANIC("kse_exit() failed for system scope thread"); + } + KSE_SCHED_LOCK(curkse, curkse->k_kseg); +} + +void +_thr_gc(struct pthread *curthread) +{ + thread_gc(curthread); + kse_gc(curthread); + kseg_gc(curthread); +} + +static void +thread_gc(struct pthread *curthread) +{ + struct pthread *td, *td_next; + kse_critical_t crit; + TAILQ_HEAD(, pthread) worklist; + + TAILQ_INIT(&worklist); + crit = _kse_critical_enter(); + KSE_LOCK_ACQUIRE(curthread->kse, &_thread_list_lock); + + /* Check the threads waiting for GC. */ + for (td = TAILQ_FIRST(&_thread_gc_list); td != NULL; td = td_next) { + td_next = TAILQ_NEXT(td, gcle); + if ((td->tlflags & TLFLAGS_GC_SAFE) == 0) + continue; + else if (((td->attr.flags & PTHREAD_SCOPE_SYSTEM) != 0) && + ((td->kse->k_kcb->kcb_kmbx.km_flags & KMF_DONE) == 0)) { + /* + * The thread and KSE are operating on the same + * stack. Wait for the KSE to exit before freeing + * the thread's stack as well as everything else. + */ + continue; + } + /* + * Remove the thread from the GC list. If the thread + * isn't yet detached, it will get added back to the + * GC list at a later time. + */ + THR_GCLIST_REMOVE(td); + DBG_MSG("Freeing thread %p stack\n", td); + /* + * We can free the thread stack since it's no longer + * in use. + */ + _thr_stack_free(&td->attr); + if (((td->attr.flags & PTHREAD_DETACHED) != 0) && + (td->refcount == 0)) { + /* + * The thread has detached and is no longer + * referenced. It is safe to remove all + * remnants of the thread. + */ + THR_LIST_REMOVE(td); + TAILQ_INSERT_HEAD(&worklist, td, gcle); + } + } + KSE_LOCK_RELEASE(curthread->kse, &_thread_list_lock); + _kse_critical_leave(crit); + + while ((td = TAILQ_FIRST(&worklist)) != NULL) { + TAILQ_REMOVE(&worklist, td, gcle); + /* + * XXX we don't free initial thread and its kse + * (if thread is a bound thread), because there might + * have some code referencing initial thread and kse. + */ + if (td == _thr_initial) { + DBG_MSG("Initial thread won't be freed\n"); + continue; + } + + if ((td->attr.flags & PTHREAD_SCOPE_SYSTEM) != 0) { + crit = _kse_critical_enter(); + KSE_LOCK_ACQUIRE(curthread->kse, &kse_lock); + kse_free_unlocked(td->kse); + kseg_free_unlocked(td->kseg); + KSE_LOCK_RELEASE(curthread->kse, &kse_lock); + _kse_critical_leave(crit); + } + DBG_MSG("Freeing thread %p\n", td); + _thr_free(curthread, td); + } +} + +static void +kse_gc(struct pthread *curthread) +{ + kse_critical_t crit; + TAILQ_HEAD(, kse) worklist; + struct kse *kse; + + if (free_kse_count <= MAX_CACHED_KSES) + return; + TAILQ_INIT(&worklist); + crit = _kse_critical_enter(); + KSE_LOCK_ACQUIRE(curthread->kse, &kse_lock); + while (free_kse_count > MAX_CACHED_KSES) { + kse = TAILQ_FIRST(&free_kseq); + TAILQ_REMOVE(&free_kseq, kse, k_qe); + TAILQ_INSERT_HEAD(&worklist, kse, k_qe); + free_kse_count--; + } + KSE_LOCK_RELEASE(curthread->kse, &kse_lock); + _kse_critical_leave(crit); + + while ((kse = TAILQ_FIRST(&worklist))) { + TAILQ_REMOVE(&worklist, kse, k_qe); + kse_destroy(kse); + } +} + +static void +kseg_gc(struct pthread *curthread) +{ + kse_critical_t crit; + TAILQ_HEAD(, kse_group) worklist; + struct kse_group *kseg; + + if (free_kseg_count <= MAX_CACHED_KSEGS) + return; + TAILQ_INIT(&worklist); + crit = _kse_critical_enter(); + KSE_LOCK_ACQUIRE(curthread->kse, &kse_lock); + while (free_kseg_count > MAX_CACHED_KSEGS) { + kseg = TAILQ_FIRST(&free_kse_groupq); + TAILQ_REMOVE(&free_kse_groupq, kseg, kg_qe); + free_kseg_count--; + TAILQ_INSERT_HEAD(&worklist, kseg, kg_qe); + } + KSE_LOCK_RELEASE(curthread->kse, &kse_lock); + _kse_critical_leave(crit); + + while ((kseg = TAILQ_FIRST(&worklist))) { + TAILQ_REMOVE(&worklist, kseg, kg_qe); + kseg_destroy(kseg); + } +} + +/* + * Only new threads that are running or suspended may be scheduled. + */ +int +_thr_schedule_add(struct pthread *curthread, struct pthread *newthread) +{ + kse_critical_t crit; + int ret; + + /* Add the new thread. */ + thr_link(newthread); + + /* + * If this is the first time creating a thread, make sure + * the mailbox is set for the current thread. + */ + if ((newthread->attr.flags & PTHREAD_SCOPE_SYSTEM) != 0) { + /* We use the thread's stack as the KSE's stack. */ + newthread->kse->k_kcb->kcb_kmbx.km_stack.ss_sp = + newthread->attr.stackaddr_attr; + newthread->kse->k_kcb->kcb_kmbx.km_stack.ss_size = + newthread->attr.stacksize_attr; + + /* + * No need to lock the scheduling queue since the + * KSE/KSEG pair have not yet been started. + */ + KSEG_THRQ_ADD(newthread->kseg, newthread); + /* this thread never gives up kse */ + newthread->active = 1; + newthread->kse->k_curthread = newthread; + newthread->kse->k_kcb->kcb_kmbx.km_flags = KMF_BOUND; + newthread->kse->k_kcb->kcb_kmbx.km_func = + (kse_func_t *)kse_sched_single; + newthread->kse->k_kcb->kcb_kmbx.km_quantum = 0; + KSE_SET_MBOX(newthread->kse, newthread); + /* + * This thread needs a new KSE and KSEG. + */ + newthread->kse->k_flags &= ~KF_INITIALIZED; + newthread->kse->k_flags |= KF_STARTED; + /* Fire up! */ + ret = kse_create(&newthread->kse->k_kcb->kcb_kmbx, 1); + if (ret != 0) + ret = errno; + } + else { + /* + * Lock the KSE and add the new thread to its list of + * assigned threads. If the new thread is runnable, also + * add it to the KSE's run queue. + */ + crit = _kse_critical_enter(); + KSE_SCHED_LOCK(curthread->kse, newthread->kseg); + KSEG_THRQ_ADD(newthread->kseg, newthread); + if (newthread->state == PS_RUNNING) + THR_RUNQ_INSERT_TAIL(newthread); + if ((newthread->kse->k_flags & KF_STARTED) == 0) { + /* + * This KSE hasn't been started yet. Start it + * outside of holding the lock. + */ + newthread->kse->k_flags |= KF_STARTED; + newthread->kse->k_kcb->kcb_kmbx.km_func = + (kse_func_t *)kse_sched_multi; + newthread->kse->k_kcb->kcb_kmbx.km_flags = 0; + kse_create(&newthread->kse->k_kcb->kcb_kmbx, 0); + } else if ((newthread->state == PS_RUNNING) && + KSE_IS_IDLE(newthread->kse)) { + /* + * The thread is being scheduled on another KSEG. + */ + kse_wakeup_one(newthread); + } + KSE_SCHED_UNLOCK(curthread->kse, newthread->kseg); + _kse_critical_leave(crit); + ret = 0; + } + if (ret != 0) + thr_unlink(newthread); + + return (ret); +} + +void +kse_waitq_insert(struct pthread *thread) +{ + struct pthread *td; + + if (thread->wakeup_time.tv_sec == -1) + TAILQ_INSERT_TAIL(&thread->kse->k_schedq->sq_waitq, thread, + pqe); + else { + td = TAILQ_FIRST(&thread->kse->k_schedq->sq_waitq); + while ((td != NULL) && (td->wakeup_time.tv_sec != -1) && + ((td->wakeup_time.tv_sec < thread->wakeup_time.tv_sec) || + ((td->wakeup_time.tv_sec == thread->wakeup_time.tv_sec) && + (td->wakeup_time.tv_nsec <= thread->wakeup_time.tv_nsec)))) + td = TAILQ_NEXT(td, pqe); + if (td == NULL) + TAILQ_INSERT_TAIL(&thread->kse->k_schedq->sq_waitq, + thread, pqe); + else + TAILQ_INSERT_BEFORE(td, thread, pqe); + } + thread->flags |= THR_FLAGS_IN_WAITQ; +} + +/* + * This must be called with the scheduling lock held. + */ +static void +kse_check_completed(struct kse *kse) +{ + struct pthread *thread; + struct kse_thr_mailbox *completed; + int sig; + + if ((completed = kse->k_kcb->kcb_kmbx.km_completed) != NULL) { + kse->k_kcb->kcb_kmbx.km_completed = NULL; + while (completed != NULL) { + thread = completed->tm_udata; + DBG_MSG("Found completed thread %p, name %s\n", + thread, + (thread->name == NULL) ? "none" : thread->name); + thread->blocked = 0; + if (thread != kse->k_curthread) { + thr_accounting(thread); + if ((thread->flags & THR_FLAGS_SUSPENDED) != 0) + THR_SET_STATE(thread, PS_SUSPENDED); + else + KSE_RUNQ_INSERT_TAIL(kse, thread); + if ((thread->kse != kse) && + (thread->kse->k_curthread == thread)) { + /* + * Remove this thread from its + * previous KSE so that it (the KSE) + * doesn't think it is still active. + */ + thread->kse->k_curthread = NULL; + thread->active = 0; + } + } + if ((sig = thread->tcb->tcb_tmbx.tm_syncsig.si_signo) + != 0) { + if (SIGISMEMBER(thread->sigmask, sig)) + SIGADDSET(thread->sigpend, sig); + else if (THR_IN_CRITICAL(thread)) + kse_thr_interrupt(NULL, KSE_INTR_SIGEXIT, sig); + else + (void)_thr_sig_add(thread, sig, + &thread->tcb->tcb_tmbx.tm_syncsig); + thread->tcb->tcb_tmbx.tm_syncsig.si_signo = 0; + } + completed = completed->tm_next; + } + } +} + +/* + * This must be called with the scheduling lock held. + */ +static void +kse_check_waitq(struct kse *kse) +{ + struct pthread *pthread; + struct timespec ts; + + KSE_GET_TOD(kse, &ts); + + /* + * Wake up threads that have timedout. This has to be + * done before adding the current thread to the run queue + * so that a CPU intensive thread doesn't get preference + * over waiting threads. + */ + while (((pthread = KSE_WAITQ_FIRST(kse)) != NULL) && + thr_timedout(pthread, &ts)) { + /* Remove the thread from the wait queue: */ + KSE_WAITQ_REMOVE(kse, pthread); + DBG_MSG("Found timedout thread %p in waitq\n", pthread); + + /* Indicate the thread timedout: */ + pthread->timeout = 1; + + /* Add the thread to the priority queue: */ + if ((pthread->flags & THR_FLAGS_SUSPENDED) != 0) + THR_SET_STATE(pthread, PS_SUSPENDED); + else { + THR_SET_STATE(pthread, PS_RUNNING); + KSE_RUNQ_INSERT_TAIL(kse, pthread); + } + } +} + +static int +thr_timedout(struct pthread *thread, struct timespec *curtime) +{ + if (thread->wakeup_time.tv_sec < 0) + return (0); + else if (thread->wakeup_time.tv_sec > curtime->tv_sec) + return (0); + else if ((thread->wakeup_time.tv_sec == curtime->tv_sec) && + (thread->wakeup_time.tv_nsec > curtime->tv_nsec)) + return (0); + else + return (1); +} + +/* + * This must be called with the scheduling lock held. + * + * Each thread has a time slice, a wakeup time (used when it wants + * to wait for a specified amount of time), a run state, and an + * active flag. + * + * When a thread gets run by the scheduler, the active flag is + * set to non-zero (1). When a thread performs an explicit yield + * or schedules a state change, it enters the scheduler and the + * active flag is cleared. When the active flag is still seen + * set in the scheduler, that means that the thread is blocked in + * the kernel (because it is cleared before entering the scheduler + * in all other instances). + * + * The wakeup time is only set for those states that can timeout. + * It is set to (-1, -1) for all other instances. + * + * The thread's run state, aside from being useful when debugging, + * is used to place the thread in an appropriate queue. There + * are 2 basic queues: + * + * o run queue - queue ordered by priority for all threads + * that are runnable + * o waiting queue - queue sorted by wakeup time for all threads + * that are not otherwise runnable (not blocked + * in kernel, not waiting for locks) + * + * The thread's time slice is used for round-robin scheduling + * (the default scheduling policy). While a SCHED_RR thread + * is runnable it's time slice accumulates. When it reaches + * the time slice interval, it gets reset and added to the end + * of the queue of threads at its priority. When a thread no + * longer becomes runnable (blocks in kernel, waits, etc), its + * time slice is reset. + * + * The job of kse_switchout_thread() is to handle all of the above. + */ +static void +kse_switchout_thread(struct kse *kse, struct pthread *thread) +{ + int level; + int i; + int restart; + siginfo_t siginfo; + + /* + * Place the currently running thread into the + * appropriate queue(s). + */ + DBG_MSG("Switching out thread %p, state %d\n", thread, thread->state); + + THR_DEACTIVATE_LAST_LOCK(thread); + if (thread->blocked != 0) { + thread->active = 0; + thread->need_switchout = 0; + /* This thread must have blocked in the kernel. */ + /* + * Check for pending signals and cancellation for + * this thread to see if we need to interrupt it + * in the kernel. + */ + if (THR_NEED_CANCEL(thread)) { + kse_thr_interrupt(&thread->tcb->tcb_tmbx, + KSE_INTR_INTERRUPT, 0); + } else if (thread->check_pending != 0) { + for (i = 1; i <= _SIG_MAXSIG; ++i) { + if (SIGISMEMBER(thread->sigpend, i) && + !SIGISMEMBER(thread->sigmask, i)) { + restart = _thread_sigact[i - 1].sa_flags & SA_RESTART; + kse_thr_interrupt(&thread->tcb->tcb_tmbx, + restart ? KSE_INTR_RESTART : KSE_INTR_INTERRUPT, 0); + break; + } + } + } + } + else { + switch (thread->state) { + case PS_MUTEX_WAIT: + case PS_COND_WAIT: + if (THR_NEED_CANCEL(thread)) { + thread->interrupted = 1; + thread->continuation = _thr_finish_cancellation; + THR_SET_STATE(thread, PS_RUNNING); + } else { + /* Insert into the waiting queue: */ + KSE_WAITQ_INSERT(kse, thread); + } + break; + + case PS_LOCKWAIT: + /* + * This state doesn't timeout. + */ + thread->wakeup_time.tv_sec = -1; + thread->wakeup_time.tv_nsec = -1; + level = thread->locklevel - 1; + if (!_LCK_GRANTED(&thread->lockusers[level])) + KSE_WAITQ_INSERT(kse, thread); + else + THR_SET_STATE(thread, PS_RUNNING); + break; + + case PS_SLEEP_WAIT: + case PS_SIGWAIT: + if (THR_NEED_CANCEL(thread)) { + thread->interrupted = 1; + THR_SET_STATE(thread, PS_RUNNING); + } else { + KSE_WAITQ_INSERT(kse, thread); + } + break; + + case PS_JOIN: + if (THR_NEED_CANCEL(thread)) { + thread->join_status.thread = NULL; + THR_SET_STATE(thread, PS_RUNNING); + } else { + /* + * This state doesn't timeout. + */ + thread->wakeup_time.tv_sec = -1; + thread->wakeup_time.tv_nsec = -1; + + /* Insert into the waiting queue: */ + KSE_WAITQ_INSERT(kse, thread); + } + break; + + case PS_SIGSUSPEND: + case PS_SUSPENDED: + if (THR_NEED_CANCEL(thread)) { + thread->interrupted = 1; + THR_SET_STATE(thread, PS_RUNNING); + } else { + /* + * These states don't timeout. + */ + thread->wakeup_time.tv_sec = -1; + thread->wakeup_time.tv_nsec = -1; + + /* Insert into the waiting queue: */ + KSE_WAITQ_INSERT(kse, thread); + } + break; + + case PS_DEAD: + /* + * The scheduler is operating on a different + * stack. It is safe to do garbage collecting + * here. + */ + thread->active = 0; + thread->need_switchout = 0; + thread->lock_switch = 0; + thr_cleanup(kse, thread); + return; + break; + + case PS_RUNNING: + if ((thread->flags & THR_FLAGS_SUSPENDED) != 0 && + !THR_NEED_CANCEL(thread)) + THR_SET_STATE(thread, PS_SUSPENDED); + break; + + case PS_DEADLOCK: + /* + * These states don't timeout. + */ + thread->wakeup_time.tv_sec = -1; + thread->wakeup_time.tv_nsec = -1; + + /* Insert into the waiting queue: */ + KSE_WAITQ_INSERT(kse, thread); + break; + + default: + PANIC("Unknown state\n"); + break; + } + + thr_accounting(thread); + if (thread->state == PS_RUNNING) { + if (thread->slice_usec == -1) { + /* + * The thread exceeded its time quantum or + * it yielded the CPU; place it at the tail + * of the queue for its priority. + */ + KSE_RUNQ_INSERT_TAIL(kse, thread); + } else { + /* + * The thread hasn't exceeded its interval + * Place it at the head of the queue for its + * priority. + */ + KSE_RUNQ_INSERT_HEAD(kse, thread); + } + } + } + thread->active = 0; + thread->need_switchout = 0; + if (thread->check_pending != 0) { + /* Install pending signals into the frame. */ + thread->check_pending = 0; + KSE_LOCK_ACQUIRE(kse, &_thread_signal_lock); + for (i = 1; i <= _SIG_MAXSIG; i++) { + if (SIGISMEMBER(thread->sigmask, i)) + continue; + if (SIGISMEMBER(thread->sigpend, i)) + (void)_thr_sig_add(thread, i, + &thread->siginfo[i-1]); + else if (SIGISMEMBER(_thr_proc_sigpending, i) && + _thr_getprocsig_unlocked(i, &siginfo)) { + (void)_thr_sig_add(thread, i, &siginfo); + } + } + KSE_LOCK_RELEASE(kse, &_thread_signal_lock); + } +} + +/* + * This function waits for the smallest timeout value of any waiting + * thread, or until it receives a message from another KSE. + * + * This must be called with the scheduling lock held. + */ +static void +kse_wait(struct kse *kse, struct pthread *td_wait, int sigseqno) +{ + struct timespec ts, ts_sleep; + int saved_flags; + + if ((td_wait == NULL) || (td_wait->wakeup_time.tv_sec < 0)) { + /* Limit sleep to no more than 1 minute. */ + ts_sleep.tv_sec = 60; + ts_sleep.tv_nsec = 0; + } else { + KSE_GET_TOD(kse, &ts); + TIMESPEC_SUB(&ts_sleep, &td_wait->wakeup_time, &ts); + if (ts_sleep.tv_sec > 60) { + ts_sleep.tv_sec = 60; + ts_sleep.tv_nsec = 0; + } + } + /* Don't sleep for negative times. */ + if ((ts_sleep.tv_sec >= 0) && (ts_sleep.tv_nsec >= 0)) { + KSE_SET_IDLE(kse); + kse->k_kseg->kg_idle_kses++; + KSE_SCHED_UNLOCK(kse, kse->k_kseg); + if ((kse->k_kseg->kg_flags & KGF_SINGLE_THREAD) && + (kse->k_sigseqno != sigseqno)) + ; /* don't sleep */ + else { + saved_flags = kse->k_kcb->kcb_kmbx.km_flags; + kse->k_kcb->kcb_kmbx.km_flags |= KMF_NOUPCALL; + kse_release(&ts_sleep); + kse->k_kcb->kcb_kmbx.km_flags = saved_flags; + } + KSE_SCHED_LOCK(kse, kse->k_kseg); + if (KSE_IS_IDLE(kse)) { + KSE_CLEAR_IDLE(kse); + kse->k_kseg->kg_idle_kses--; + } + } +} + +/* + * Avoid calling this kse_exit() so as not to confuse it with the + * system call of the same name. + */ +static void +kse_fini(struct kse *kse) +{ + /* struct kse_group *free_kseg = NULL; */ + struct timespec ts; + struct pthread *td; + + /* + * Check to see if this is one of the main kses. + */ + if (kse->k_kseg != _kse_initial->k_kseg) { + PANIC("shouldn't get here"); + /* This is for supporting thread groups. */ +#ifdef NOT_YET + /* Remove this KSE from the KSEG's list of KSEs. */ + KSE_SCHED_LOCK(kse, kse->k_kseg); + TAILQ_REMOVE(&kse->k_kseg->kg_kseq, kse, k_kgqe); + kse->k_kseg->kg_ksecount--; + if (TAILQ_EMPTY(&kse->k_kseg->kg_kseq)) + free_kseg = kse->k_kseg; + KSE_SCHED_UNLOCK(kse, kse->k_kseg); + + /* + * Add this KSE to the list of free KSEs along with + * the KSEG if is now orphaned. + */ + KSE_LOCK_ACQUIRE(kse, &kse_lock); + if (free_kseg != NULL) + kseg_free_unlocked(free_kseg); + kse_free_unlocked(kse); + KSE_LOCK_RELEASE(kse, &kse_lock); + kse_exit(); + /* Never returns. */ + PANIC("kse_exit()"); +#endif + } else { + /* + * We allow program to kill kse in initial group (by + * lowering the concurrency). + */ + if ((kse != _kse_initial) && + ((kse->k_flags & KF_TERMINATED) != 0)) { + KSE_SCHED_LOCK(kse, kse->k_kseg); + TAILQ_REMOVE(&kse->k_kseg->kg_kseq, kse, k_kgqe); + kse->k_kseg->kg_ksecount--; + /* + * Migrate thread to _kse_initial if its lastest + * kse it ran on is the kse. + */ + td = TAILQ_FIRST(&kse->k_kseg->kg_threadq); + while (td != NULL) { + if (td->kse == kse) + td->kse = _kse_initial; + td = TAILQ_NEXT(td, kle); + } + KSE_SCHED_UNLOCK(kse, kse->k_kseg); + KSE_LOCK_ACQUIRE(kse, &kse_lock); + kse_free_unlocked(kse); + KSE_LOCK_RELEASE(kse, &kse_lock); + /* Make sure there is always at least one is awake */ + KSE_WAKEUP(_kse_initial); + kse_exit(); + /* Never returns. */ + PANIC("kse_exit() failed for initial kseg"); + } + KSE_SCHED_LOCK(kse, kse->k_kseg); + KSE_SET_IDLE(kse); + kse->k_kseg->kg_idle_kses++; + KSE_SCHED_UNLOCK(kse, kse->k_kseg); + ts.tv_sec = 120; + ts.tv_nsec = 0; + kse->k_kcb->kcb_kmbx.km_flags = 0; + kse_release(&ts); + /* Never reach */ + } +} + +void +_thr_set_timeout(const struct timespec *timeout) +{ + struct pthread *curthread = _get_curthread(); + struct timespec ts; + + /* Reset the timeout flag for the running thread: */ + curthread->timeout = 0; + + /* Check if the thread is to wait forever: */ + if (timeout == NULL) { + /* + * Set the wakeup time to something that can be recognised as + * different to an actual time of day: + */ + curthread->wakeup_time.tv_sec = -1; + curthread->wakeup_time.tv_nsec = -1; + } + /* Check if no waiting is required: */ + else if ((timeout->tv_sec == 0) && (timeout->tv_nsec == 0)) { + /* Set the wake up time to 'immediately': */ + curthread->wakeup_time.tv_sec = 0; + curthread->wakeup_time.tv_nsec = 0; + } else { + /* Calculate the time for the current thread to wakeup: */ + KSE_GET_TOD(curthread->kse, &ts); + TIMESPEC_ADD(&curthread->wakeup_time, &ts, timeout); + } +} + +void +_thr_panic_exit(char *file, int line, char *msg) +{ + char buf[256]; + + snprintf(buf, sizeof(buf), "(%s:%d) %s\n", file, line, msg); + __sys_write(2, buf, strlen(buf)); + abort(); +} + +void +_thr_setrunnable(struct pthread *curthread, struct pthread *thread) +{ + kse_critical_t crit; + struct kse_mailbox *kmbx; + + crit = _kse_critical_enter(); + KSE_SCHED_LOCK(curthread->kse, thread->kseg); + kmbx = _thr_setrunnable_unlocked(thread); + KSE_SCHED_UNLOCK(curthread->kse, thread->kseg); + _kse_critical_leave(crit); + if ((kmbx != NULL) && (__isthreaded != 0)) + kse_wakeup(kmbx); +} + +struct kse_mailbox * +_thr_setrunnable_unlocked(struct pthread *thread) +{ + struct kse_mailbox *kmbx = NULL; + + if ((thread->kseg->kg_flags & KGF_SINGLE_THREAD) != 0) { + /* No silly queues for these threads. */ + if ((thread->flags & THR_FLAGS_SUSPENDED) != 0) + THR_SET_STATE(thread, PS_SUSPENDED); + else { + THR_SET_STATE(thread, PS_RUNNING); + kmbx = kse_wakeup_one(thread); + } + + } else if (thread->state != PS_RUNNING) { + if ((thread->flags & THR_FLAGS_IN_WAITQ) != 0) + KSE_WAITQ_REMOVE(thread->kse, thread); + if ((thread->flags & THR_FLAGS_SUSPENDED) != 0) + THR_SET_STATE(thread, PS_SUSPENDED); + else { + THR_SET_STATE(thread, PS_RUNNING); + if ((thread->blocked == 0) && (thread->active == 0) && + (thread->flags & THR_FLAGS_IN_RUNQ) == 0) + THR_RUNQ_INSERT_TAIL(thread); + /* + * XXX - Threads are not yet assigned to specific + * KSEs; they are assigned to the KSEG. So + * the fact that a thread's KSE is waiting + * doesn't necessarily mean that it will be + * the KSE that runs the thread after the + * lock is granted. But we don't know if the + * other KSEs within the same KSEG are also + * in a waiting state or not so we err on the + * side of caution and wakeup the thread's + * last known KSE. We ensure that the + * threads KSE doesn't change while it's + * scheduling lock is held so it is safe to + * reference it (the KSE). If the KSE wakes + * up and doesn't find any more work it will + * again go back to waiting so no harm is + * done. + */ + kmbx = kse_wakeup_one(thread); + } + } + return (kmbx); +} + +static struct kse_mailbox * +kse_wakeup_one(struct pthread *thread) +{ + struct kse *ke; + + if (KSE_IS_IDLE(thread->kse)) { + KSE_CLEAR_IDLE(thread->kse); + thread->kseg->kg_idle_kses--; + return (&thread->kse->k_kcb->kcb_kmbx); + } else { + TAILQ_FOREACH(ke, &thread->kseg->kg_kseq, k_kgqe) { + if (KSE_IS_IDLE(ke)) { + KSE_CLEAR_IDLE(ke); + ke->k_kseg->kg_idle_kses--; + return (&ke->k_kcb->kcb_kmbx); + } + } + } + return (NULL); +} + +static void +kse_wakeup_multi(struct kse *curkse) +{ + struct kse *ke; + int tmp; + + if ((tmp = KSE_RUNQ_THREADS(curkse)) && curkse->k_kseg->kg_idle_kses) { + TAILQ_FOREACH(ke, &curkse->k_kseg->kg_kseq, k_kgqe) { + if (KSE_IS_IDLE(ke)) { + KSE_CLEAR_IDLE(ke); + ke->k_kseg->kg_idle_kses--; + KSE_WAKEUP(ke); + if (--tmp == 0) + break; + } + } + } +} + +/* + * Allocate a new KSEG. + * + * We allow the current thread to be NULL in the case that this + * is the first time a KSEG is being created (library initialization). + * In this case, we don't need to (and can't) take any locks. + */ +struct kse_group * +_kseg_alloc(struct pthread *curthread) +{ + struct kse_group *kseg = NULL; + kse_critical_t crit; + + if ((curthread != NULL) && (free_kseg_count > 0)) { + /* Use the kse lock for the kseg queue. */ + crit = _kse_critical_enter(); + KSE_LOCK_ACQUIRE(curthread->kse, &kse_lock); + if ((kseg = TAILQ_FIRST(&free_kse_groupq)) != NULL) { + TAILQ_REMOVE(&free_kse_groupq, kseg, kg_qe); + free_kseg_count--; + active_kseg_count++; + TAILQ_INSERT_TAIL(&active_kse_groupq, kseg, kg_qe); + } + KSE_LOCK_RELEASE(curthread->kse, &kse_lock); + _kse_critical_leave(crit); + if (kseg) + kseg_reinit(kseg); + } + + /* + * If requested, attempt to allocate a new KSE group only if the + * KSE allocation was successful and a KSE group wasn't found in + * the free list. + */ + if ((kseg == NULL) && + ((kseg = (struct kse_group *)malloc(sizeof(*kseg))) != NULL)) { + if (_pq_alloc(&kseg->kg_schedq.sq_runq, + THR_MIN_PRIORITY, THR_LAST_PRIORITY) != 0) { + free(kseg); + kseg = NULL; + } else { + kseg_init(kseg); + /* Add the KSEG to the list of active KSEGs. */ + if (curthread != NULL) { + crit = _kse_critical_enter(); + KSE_LOCK_ACQUIRE(curthread->kse, &kse_lock); + active_kseg_count++; + TAILQ_INSERT_TAIL(&active_kse_groupq, + kseg, kg_qe); + KSE_LOCK_RELEASE(curthread->kse, &kse_lock); + _kse_critical_leave(crit); + } else { + active_kseg_count++; + TAILQ_INSERT_TAIL(&active_kse_groupq, + kseg, kg_qe); + } + } + } + return (kseg); +} + +static void +kseg_init(struct kse_group *kseg) +{ + kseg_reinit(kseg); + _lock_init(&kseg->kg_lock, LCK_ADAPTIVE, _kse_lock_wait, + _kse_lock_wakeup); +} + +static void +kseg_reinit(struct kse_group *kseg) +{ + TAILQ_INIT(&kseg->kg_kseq); + TAILQ_INIT(&kseg->kg_threadq); + TAILQ_INIT(&kseg->kg_schedq.sq_waitq); + kseg->kg_threadcount = 0; + kseg->kg_ksecount = 0; + kseg->kg_idle_kses = 0; + kseg->kg_flags = 0; +} + +/* + * This must be called with the kse lock held and when there are + * no more threads that reference it. + */ +static void +kseg_free_unlocked(struct kse_group *kseg) +{ + TAILQ_REMOVE(&active_kse_groupq, kseg, kg_qe); + TAILQ_INSERT_HEAD(&free_kse_groupq, kseg, kg_qe); + free_kseg_count++; + active_kseg_count--; +} + +void +_kseg_free(struct kse_group *kseg) +{ + struct kse *curkse; + kse_critical_t crit; + + crit = _kse_critical_enter(); + curkse = _get_curkse(); + KSE_LOCK_ACQUIRE(curkse, &kse_lock); + kseg_free_unlocked(kseg); + KSE_LOCK_RELEASE(curkse, &kse_lock); + _kse_critical_leave(crit); +} + +static void +kseg_destroy(struct kse_group *kseg) +{ + _lock_destroy(&kseg->kg_lock); + _pq_free(&kseg->kg_schedq.sq_runq); + free(kseg); +} + +/* + * Allocate a new KSE. + * + * We allow the current thread to be NULL in the case that this + * is the first time a KSE is being created (library initialization). + * In this case, we don't need to (and can't) take any locks. + */ +struct kse * +_kse_alloc(struct pthread *curthread, int sys_scope) +{ + struct kse *kse = NULL; + char *stack; + kse_critical_t crit; + int i; + + if ((curthread != NULL) && (free_kse_count > 0)) { + crit = _kse_critical_enter(); + KSE_LOCK_ACQUIRE(curthread->kse, &kse_lock); + /* Search for a finished KSE. */ + kse = TAILQ_FIRST(&free_kseq); + while ((kse != NULL) && + ((kse->k_kcb->kcb_kmbx.km_flags & KMF_DONE) == 0)) { + kse = TAILQ_NEXT(kse, k_qe); + } + if (kse != NULL) { + DBG_MSG("found an unused kse.\n"); + TAILQ_REMOVE(&free_kseq, kse, k_qe); + free_kse_count--; + TAILQ_INSERT_TAIL(&active_kseq, kse, k_qe); + active_kse_count++; + } + KSE_LOCK_RELEASE(curthread->kse, &kse_lock); + _kse_critical_leave(crit); + if (kse != NULL) + kse_reinit(kse, sys_scope); + } + if ((kse == NULL) && + ((kse = (struct kse *)malloc(sizeof(*kse))) != NULL)) { + if (sys_scope != 0) + stack = NULL; + else if ((stack = malloc(KSE_STACKSIZE)) == NULL) { + free(kse); + return (NULL); + } + bzero(kse, sizeof(*kse)); + + /* Initialize KCB without the lock. */ + if ((kse->k_kcb = _kcb_ctor(kse)) == NULL) { + if (stack != NULL) + free(stack); + free(kse); + return (NULL); + } + + /* Initialize the lockusers. */ + for (i = 0; i < MAX_KSE_LOCKLEVEL; i++) { + _lockuser_init(&kse->k_lockusers[i], (void *)kse); + _LCK_SET_PRIVATE2(&kse->k_lockusers[i], NULL); + } + /* _lock_init(kse->k_lock, ...) */ + + if (curthread != NULL) { + crit = _kse_critical_enter(); + KSE_LOCK_ACQUIRE(curthread->kse, &kse_lock); + } + kse->k_flags = 0; + TAILQ_INSERT_TAIL(&active_kseq, kse, k_qe); + active_kse_count++; + if (curthread != NULL) { + KSE_LOCK_RELEASE(curthread->kse, &kse_lock); + _kse_critical_leave(crit); + } + /* + * Create the KSE context. + * Scope system threads (one thread per KSE) are not required + * to have a stack for an unneeded kse upcall. + */ + if (!sys_scope) { + kse->k_kcb->kcb_kmbx.km_func = (kse_func_t *)kse_sched_multi; + kse->k_stack.ss_sp = stack; + kse->k_stack.ss_size = KSE_STACKSIZE; + } else { + kse->k_kcb->kcb_kmbx.km_func = (kse_func_t *)kse_sched_single; + kse->k_stack.ss_sp = NULL; + kse->k_stack.ss_size = 0; + } + kse->k_kcb->kcb_kmbx.km_udata = (void *)kse; + kse->k_kcb->kcb_kmbx.km_quantum = 20000; + /* + * We need to keep a copy of the stack in case it + * doesn't get used; a KSE running a scope system + * thread will use that thread's stack. + */ + kse->k_kcb->kcb_kmbx.km_stack = kse->k_stack; + } + return (kse); +} + +static void +kse_reinit(struct kse *kse, int sys_scope) +{ + if (!sys_scope) { + kse->k_kcb->kcb_kmbx.km_func = (kse_func_t *)kse_sched_multi; + if (kse->k_stack.ss_sp == NULL) { + /* XXX check allocation failure */ + kse->k_stack.ss_sp = (char *) malloc(KSE_STACKSIZE); + kse->k_stack.ss_size = KSE_STACKSIZE; + } + kse->k_kcb->kcb_kmbx.km_quantum = 20000; + } else { + kse->k_kcb->kcb_kmbx.km_func = (kse_func_t *)kse_sched_single; + if (kse->k_stack.ss_sp) + free(kse->k_stack.ss_sp); + kse->k_stack.ss_sp = NULL; + kse->k_stack.ss_size = 0; + kse->k_kcb->kcb_kmbx.km_quantum = 0; + } + kse->k_kcb->kcb_kmbx.km_stack = kse->k_stack; + kse->k_kcb->kcb_kmbx.km_udata = (void *)kse; + kse->k_kcb->kcb_kmbx.km_curthread = NULL; + kse->k_kcb->kcb_kmbx.km_flags = 0; + kse->k_curthread = NULL; + kse->k_kseg = 0; + kse->k_schedq = 0; + kse->k_locklevel = 0; + kse->k_flags = 0; + kse->k_error = 0; + kse->k_cpu = 0; + kse->k_sigseqno = 0; +} + +void +kse_free_unlocked(struct kse *kse) +{ + TAILQ_REMOVE(&active_kseq, kse, k_qe); + active_kse_count--; + kse->k_kseg = NULL; + kse->k_kcb->kcb_kmbx.km_quantum = 20000; + kse->k_flags = 0; + TAILQ_INSERT_HEAD(&free_kseq, kse, k_qe); + free_kse_count++; +} + +void +_kse_free(struct pthread *curthread, struct kse *kse) +{ + kse_critical_t crit; + + if (curthread == NULL) + kse_free_unlocked(kse); + else { + crit = _kse_critical_enter(); + KSE_LOCK_ACQUIRE(curthread->kse, &kse_lock); + kse_free_unlocked(kse); + KSE_LOCK_RELEASE(curthread->kse, &kse_lock); + _kse_critical_leave(crit); + } +} + +static void +kse_destroy(struct kse *kse) +{ + int i; + + if (kse->k_stack.ss_sp != NULL) + free(kse->k_stack.ss_sp); + _kcb_dtor(kse->k_kcb); + for (i = 0; i < MAX_KSE_LOCKLEVEL; ++i) + _lockuser_destroy(&kse->k_lockusers[i]); + _lock_destroy(&kse->k_lock); + free(kse); +} + +struct pthread * +_thr_alloc(struct pthread *curthread) +{ + kse_critical_t crit; + struct pthread *thread = NULL; + int i; + + if (curthread != NULL) { + if (GC_NEEDED()) + _thr_gc(curthread); + if (free_thread_count > 0) { + crit = _kse_critical_enter(); + KSE_LOCK_ACQUIRE(curthread->kse, &thread_lock); + if ((thread = TAILQ_FIRST(&free_threadq)) != NULL) { + TAILQ_REMOVE(&free_threadq, thread, tle); + free_thread_count--; + } + KSE_LOCK_RELEASE(curthread->kse, &thread_lock); + _kse_critical_leave(crit); + } + } + if ((thread == NULL) && + ((thread = malloc(sizeof(struct pthread))) != NULL)) { + bzero(thread, sizeof(struct pthread)); + thread->siginfo = calloc(_SIG_MAXSIG, sizeof(siginfo_t)); + if (thread->siginfo == NULL) { + free(thread); + return (NULL); + } + if (curthread) { + _pthread_mutex_lock(&_tcb_mutex); + thread->tcb = _tcb_ctor(thread, 0 /* not initial tls */); + _pthread_mutex_unlock(&_tcb_mutex); + } else { + thread->tcb = _tcb_ctor(thread, 1 /* initial tls */); + } + if (thread->tcb == NULL) { + free(thread->siginfo); + free(thread); + return (NULL); + } + /* + * Initialize thread locking. + * Lock initializing needs malloc, so don't + * enter critical region before doing this! + */ + if (_lock_init(&thread->lock, LCK_ADAPTIVE, + _thr_lock_wait, _thr_lock_wakeup) != 0) + PANIC("Cannot initialize thread lock"); + for (i = 0; i < MAX_THR_LOCKLEVEL; i++) { + _lockuser_init(&thread->lockusers[i], (void *)thread); + _LCK_SET_PRIVATE2(&thread->lockusers[i], + (void *)thread); + } + } + return (thread); +} + +void +_thr_free(struct pthread *curthread, struct pthread *thread) +{ + kse_critical_t crit; + + DBG_MSG("Freeing thread %p\n", thread); + if (thread->name) { + free(thread->name); + thread->name = NULL; + } + if ((curthread == NULL) || (free_thread_count >= MAX_CACHED_THREADS)) { + thr_destroy(curthread, thread); + } else { + /* Add the thread to the free thread list. */ + crit = _kse_critical_enter(); + KSE_LOCK_ACQUIRE(curthread->kse, &thread_lock); + TAILQ_INSERT_TAIL(&free_threadq, thread, tle); + free_thread_count++; + KSE_LOCK_RELEASE(curthread->kse, &thread_lock); + _kse_critical_leave(crit); + } +} + +static void +thr_destroy(struct pthread *curthread, struct pthread *thread) +{ + int i; + + for (i = 0; i < MAX_THR_LOCKLEVEL; i++) + _lockuser_destroy(&thread->lockusers[i]); + _lock_destroy(&thread->lock); + if (curthread) { + _pthread_mutex_lock(&_tcb_mutex); + _tcb_dtor(thread->tcb); + _pthread_mutex_unlock(&_tcb_mutex); + } else { + _tcb_dtor(thread->tcb); + } + free(thread->siginfo); + free(thread); +} + +/* + * Add an active thread: + * + * o Assign the thread a unique id (which GDB uses to track + * threads. + * o Add the thread to the list of all threads and increment + * number of active threads. + */ +static void +thr_link(struct pthread *thread) +{ + kse_critical_t crit; + struct kse *curkse; + + crit = _kse_critical_enter(); + curkse = _get_curkse(); + KSE_LOCK_ACQUIRE(curkse, &_thread_list_lock); + /* + * Initialize the unique id (which GDB uses to track + * threads), add the thread to the list of all threads, + * and + */ + thread->uniqueid = next_uniqueid++; + THR_LIST_ADD(thread); + _thread_active_threads++; + KSE_LOCK_RELEASE(curkse, &_thread_list_lock); + _kse_critical_leave(crit); +} + +/* + * Remove an active thread. + */ +static void +thr_unlink(struct pthread *thread) +{ + kse_critical_t crit; + struct kse *curkse; + + crit = _kse_critical_enter(); + curkse = _get_curkse(); + KSE_LOCK_ACQUIRE(curkse, &_thread_list_lock); + THR_LIST_REMOVE(thread); + _thread_active_threads--; + KSE_LOCK_RELEASE(curkse, &_thread_list_lock); + _kse_critical_leave(crit); +} + +void +_thr_hash_add(struct pthread *thread) +{ + struct thread_hash_head *head; + + head = &thr_hashtable[THREAD_HASH(thread)]; + LIST_INSERT_HEAD(head, thread, hle); +} + +void +_thr_hash_remove(struct pthread *thread) +{ + LIST_REMOVE(thread, hle); +} + +struct pthread * +_thr_hash_find(struct pthread *thread) +{ + struct pthread *td; + struct thread_hash_head *head; + + head = &thr_hashtable[THREAD_HASH(thread)]; + LIST_FOREACH(td, head, hle) { + if (td == thread) + return (thread); + } + return (NULL); +} + +void +_thr_debug_check_yield(struct pthread *curthread) +{ + /* + * Note that TMDF_SUSPEND is set after process is suspended. + * When we are being debugged, every suspension in process + * will cause all KSEs to schedule an upcall in kernel, unless the + * KSE is in critical region. + * If the function is being called, it means the KSE is no longer + * in critical region, if the TMDF_SUSPEND is set by debugger + * before KSE leaves critical region, we will catch it here, else + * if the flag is changed during testing, it also not a problem, + * because the change only occurs after a process suspension event + * occurs. A suspension event will always cause KSE to schedule an + * upcall, in the case, because we are not in critical region, + * upcall will be scheduled sucessfully, the flag will be checked + * again in kse_sched_multi, we won't back until the flag + * is cleared by debugger, the flag will be cleared in next + * suspension event. + */ + if (!DBG_CAN_RUN(curthread)) { + if ((curthread->attr.flags & PTHREAD_SCOPE_SYSTEM) == 0) + _thr_sched_switch(curthread); + else + kse_thr_interrupt(&curthread->tcb->tcb_tmbx, + KSE_INTR_DBSUSPEND, 0); + } +} diff --git a/lib/libpthread/thread/thr_kill.c b/lib/libpthread/thread/thr_kill.c new file mode 100644 index 0000000..a03ec38 --- /dev/null +++ b/lib/libpthread/thread/thr_kill.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 1997 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <errno.h> +#include <signal.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_kill); +LT10_COMPAT_DEFAULT(pthread_kill); + +__weak_reference(_pthread_kill, pthread_kill); + +int +_pthread_kill(pthread_t pthread, int sig) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + /* Check for invalid signal numbers: */ + if (sig < 0 || sig > _SIG_MAXSIG) + /* Invalid signal: */ + ret = EINVAL; + /* + * Ensure the thread is in the list of active threads, and the + * signal is valid (signal 0 specifies error checking only) and + * not being ignored: + */ + else if ((ret = _thr_ref_add(curthread, pthread, /*include dead*/0)) + == 0) { + if ((sig > 0) && + (_thread_sigact[sig - 1].sa_handler != SIG_IGN)) + _thr_sig_send(pthread, sig); + _thr_ref_delete(curthread, pthread); + } + + /* Return the completion status: */ + return (ret); +} diff --git a/lib/libpthread/thread/thr_main_np.c b/lib/libpthread/thread/thr_main_np.c new file mode 100644 index 0000000..50fb9c8 --- /dev/null +++ b/lib/libpthread/thread/thr_main_np.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2001 Alfred Perlstein + * Author: Alfred Perlstein <alfred@FreeBSD.org> + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include <pthread.h> +#include <pthread_np.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_main_np); +LT10_COMPAT_DEFAULT(pthread_main_np); + +__weak_reference(_pthread_main_np, pthread_main_np); + +/* + * Provide the equivelant to Solaris thr_main() function + */ +int +_pthread_main_np() +{ + + if (!_thr_initial) + return (-1); + else + return (pthread_equal(pthread_self(), _thr_initial) ? 1 : 0); +} diff --git a/lib/libpthread/thread/thr_mattr_init.c b/lib/libpthread/thread/thr_mattr_init.c new file mode 100644 index 0000000..b273bab --- /dev/null +++ b/lib/libpthread/thread/thr_mattr_init.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 1996 Jeffrey Hsu <hsu@freebsd.org>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_mutexattr_init); +LT10_COMPAT_DEFAULT(pthread_mutexattr_init); + +__weak_reference(_pthread_mutexattr_init, pthread_mutexattr_init); + +int +_pthread_mutexattr_init(pthread_mutexattr_t *attr) +{ + int ret; + pthread_mutexattr_t pattr; + + if ((pattr = (pthread_mutexattr_t) + malloc(sizeof(struct pthread_mutex_attr))) == NULL) { + ret = ENOMEM; + } else { + memcpy(pattr, &_pthread_mutexattr_default, + sizeof(struct pthread_mutex_attr)); + *attr = pattr; + ret = 0; + } + return (ret); +} diff --git a/lib/libpthread/thread/thr_mattr_kind_np.c b/lib/libpthread/thread/thr_mattr_kind_np.c new file mode 100644 index 0000000..12cd775 --- /dev/null +++ b/lib/libpthread/thread/thr_mattr_kind_np.c @@ -0,0 +1,106 @@ +/* + * Copyright (c) 1996 Jeffrey Hsu <hsu@freebsd.org>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_mutexattr_setkind_np); +LT10_COMPAT_DEFAULT(pthread_mutexattr_setkind_np); +LT10_COMPAT_PRIVATE(_pthread_mutexattr_getkind_np); +LT10_COMPAT_DEFAULT(pthread_mutexattr_getkind_np); +LT10_COMPAT_PRIVATE(_pthread_mutexattr_gettype); +LT10_COMPAT_DEFAULT(pthread_mutexattr_gettype); +LT10_COMPAT_PRIVATE(_pthread_mutexattr_settype); +LT10_COMPAT_DEFAULT(pthread_mutexattr_settype); + +__weak_reference(_pthread_mutexattr_setkind_np, pthread_mutexattr_setkind_np); +__weak_reference(_pthread_mutexattr_getkind_np, pthread_mutexattr_getkind_np); +__weak_reference(_pthread_mutexattr_gettype, pthread_mutexattr_gettype); +__weak_reference(_pthread_mutexattr_settype, pthread_mutexattr_settype); + +int +_pthread_mutexattr_setkind_np(pthread_mutexattr_t *attr, int kind) +{ + int ret; + if (attr == NULL || *attr == NULL) { + errno = EINVAL; + ret = -1; + } else { + (*attr)->m_type = kind; + ret = 0; + } + return(ret); +} + +int +_pthread_mutexattr_getkind_np(pthread_mutexattr_t attr) +{ + int ret; + if (attr == NULL) { + errno = EINVAL; + ret = -1; + } else { + ret = attr->m_type; + } + return(ret); +} + +int +_pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) +{ + int ret; + if (attr == NULL || *attr == NULL || type >= PTHREAD_MUTEX_TYPE_MAX) { + errno = EINVAL; + ret = -1; + } else { + (*attr)->m_type = type; + ret = 0; + } + return(ret); +} + +int +_pthread_mutexattr_gettype(pthread_mutexattr_t *attr, int *type) +{ + int ret; + + if (attr == NULL || *attr == NULL || (*attr)->m_type >= + PTHREAD_MUTEX_TYPE_MAX) { + ret = EINVAL; + } else { + *type = (*attr)->m_type; + ret = 0; + } + return ret; +} diff --git a/lib/libpthread/thread/thr_mattr_pshared.c b/lib/libpthread/thread/thr_mattr_pshared.c new file mode 100644 index 0000000..03aff41 --- /dev/null +++ b/lib/libpthread/thread/thr_mattr_pshared.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2005 David Xu <davidxu@freebsd.org> + * 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 unmodified, 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. + * + * 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. + * + * $FreeBSD$ + * + */ + +#include <errno.h> +#include "thr_private.h" + +__weak_reference(_pthread_mutexattr_getpshared, pthread_mutexattr_getpshared); +__weak_reference(_pthread_mutexattr_setpshared, pthread_mutexattr_setpshared); + +int +_pthread_mutexattr_getpshared(const pthread_mutexattr_t *attr, + int *pshared) +{ + if (attr == NULL || *attr == NULL) + return (EINVAL); + + pshared = PTHREAD_PROCESS_PRIVATE; + return (0); +} + +int +_pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared) +{ + if (attr == NULL || *attr == NULL) + return (EINVAL); + + if (pshared != PTHREAD_PROCESS_PRIVATE) + return (EINVAL); + return (0); +} diff --git a/lib/libpthread/thread/thr_msync.c b/lib/libpthread/thread/thr_msync.c new file mode 100644 index 0000000..66e88c5 --- /dev/null +++ b/lib/libpthread/thread/thr_msync.c @@ -0,0 +1,36 @@ +/* + * David Leonard <d@openbsd.org>, 1999. Public Domain. + * + * $OpenBSD: uthread_msync.c,v 1.2 1999/06/09 07:16:17 d Exp $ + * + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <sys/mman.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(__msync); +LT10_COMPAT_DEFAULT(msync); + +__weak_reference(__msync, msync); + +int +__msync(void *addr, size_t len, int flags) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + /* + * XXX This is quite pointless unless we know how to get the + * file descriptor associated with the memory, and lock it for + * write. The only real use of this wrapper is to guarantee + * a cancellation point, as per the standard. sigh. + */ + _thr_cancel_enter(curthread); + ret = __sys_msync(addr, len, flags); + _thr_cancel_leave(curthread, 1); + + return ret; +} diff --git a/lib/libpthread/thread/thr_multi_np.c b/lib/libpthread/thread/thr_multi_np.c new file mode 100644 index 0000000..54ce22c --- /dev/null +++ b/lib/libpthread/thread/thr_multi_np.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 1996 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <pthread.h> +#include <pthread_np.h> + +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_multi_np); +LT10_COMPAT_DEFAULT(pthread_multi_np); + +__weak_reference(_pthread_multi_np, pthread_multi_np); + +int +_pthread_multi_np() +{ + + /* Return to multi-threaded scheduling mode: */ + /* + * XXX - Do we want to do this? + * __is_threaded = 1; + */ + pthread_resume_all_np(); + return (0); +} diff --git a/lib/libpthread/thread/thr_mutex.c b/lib/libpthread/thread/thr_mutex.c new file mode 100644 index 0000000..6383cd8 --- /dev/null +++ b/lib/libpthread/thread/thr_mutex.c @@ -0,0 +1,1832 @@ +/* + * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <sys/param.h> +#include <sys/queue.h> +#include <pthread.h> +#include "thr_private.h" + +#if defined(_PTHREADS_INVARIANTS) +#define MUTEX_INIT_LINK(m) do { \ + (m)->m_qe.tqe_prev = NULL; \ + (m)->m_qe.tqe_next = NULL; \ +} while (0) +#define MUTEX_ASSERT_IS_OWNED(m) do { \ + if ((m)->m_qe.tqe_prev == NULL) \ + PANIC("mutex is not on list"); \ +} while (0) +#define MUTEX_ASSERT_NOT_OWNED(m) do { \ + if (((m)->m_qe.tqe_prev != NULL) || \ + ((m)->m_qe.tqe_next != NULL)) \ + PANIC("mutex is on list"); \ +} while (0) +#define THR_ASSERT_NOT_IN_SYNCQ(thr) do { \ + THR_ASSERT(((thr)->sflags & THR_FLAGS_IN_SYNCQ) == 0, \ + "thread in syncq when it shouldn't be."); \ +} while (0); +#else +#define MUTEX_INIT_LINK(m) +#define MUTEX_ASSERT_IS_OWNED(m) +#define MUTEX_ASSERT_NOT_OWNED(m) +#define THR_ASSERT_NOT_IN_SYNCQ(thr) +#endif + +#define THR_IN_MUTEXQ(thr) (((thr)->sflags & THR_FLAGS_IN_SYNCQ) != 0) +#define MUTEX_DESTROY(m) do { \ + _lock_destroy(&(m)->m_lock); \ + free(m); \ +} while (0) + + +/* + * Prototypes + */ +static struct kse_mailbox *mutex_handoff(struct pthread *, + struct pthread_mutex *); +static inline int mutex_self_trylock(struct pthread *, pthread_mutex_t); +static inline int mutex_self_lock(struct pthread *, pthread_mutex_t); +static int mutex_unlock_common(pthread_mutex_t *, int); +static void mutex_priority_adjust(struct pthread *, pthread_mutex_t); +static void mutex_rescan_owned (struct pthread *, struct pthread *, + struct pthread_mutex *); +static inline pthread_t mutex_queue_deq(pthread_mutex_t); +static inline void mutex_queue_remove(pthread_mutex_t, pthread_t); +static inline void mutex_queue_enq(pthread_mutex_t, pthread_t); +static void mutex_lock_backout(void *arg); + +static struct pthread_mutex_attr static_mutex_attr = + PTHREAD_MUTEXATTR_STATIC_INITIALIZER; +static pthread_mutexattr_t static_mattr = &static_mutex_attr; + +LT10_COMPAT_PRIVATE(__pthread_mutex_init); +LT10_COMPAT_PRIVATE(_pthread_mutex_init); +LT10_COMPAT_DEFAULT(pthread_mutex_init); +LT10_COMPAT_PRIVATE(__pthread_mutex_lock); +LT10_COMPAT_PRIVATE(_pthread_mutex_lock); +LT10_COMPAT_DEFAULT(pthread_mutex_lock); +LT10_COMPAT_PRIVATE(__pthread_mutex_timedlock); +LT10_COMPAT_PRIVATE(_pthread_mutex_timedlock); +LT10_COMPAT_DEFAULT(pthread_mutex_timedlock); +LT10_COMPAT_PRIVATE(__pthread_mutex_trylock); +LT10_COMPAT_PRIVATE(_pthread_mutex_trylock); +LT10_COMPAT_DEFAULT(pthread_mutex_trylock); +LT10_COMPAT_PRIVATE(_pthread_mutex_destroy); +LT10_COMPAT_DEFAULT(pthread_mutex_destroy); +LT10_COMPAT_PRIVATE(_pthread_mutex_unlock); +LT10_COMPAT_DEFAULT(pthread_mutex_unlock); + +/* Single underscore versions provided for libc internal usage: */ +__weak_reference(__pthread_mutex_init, pthread_mutex_init); +__weak_reference(__pthread_mutex_lock, pthread_mutex_lock); +__weak_reference(__pthread_mutex_timedlock, pthread_mutex_timedlock); +__weak_reference(__pthread_mutex_trylock, pthread_mutex_trylock); + +/* No difference between libc and application usage of these: */ +__weak_reference(_pthread_mutex_destroy, pthread_mutex_destroy); +__weak_reference(_pthread_mutex_unlock, pthread_mutex_unlock); + + + +int +__pthread_mutex_init(pthread_mutex_t *mutex, + const pthread_mutexattr_t *mutex_attr) +{ + struct pthread_mutex *pmutex; + enum pthread_mutextype type; + int protocol; + int ceiling; + int flags; + int ret = 0; + + if (mutex == NULL) + ret = EINVAL; + + /* Check if default mutex attributes: */ + else if (mutex_attr == NULL || *mutex_attr == NULL) { + /* Default to a (error checking) POSIX mutex: */ + type = PTHREAD_MUTEX_ERRORCHECK; + protocol = PTHREAD_PRIO_NONE; + ceiling = THR_MAX_PRIORITY; + flags = 0; + } + + /* Check mutex type: */ + else if (((*mutex_attr)->m_type < PTHREAD_MUTEX_ERRORCHECK) || + ((*mutex_attr)->m_type >= PTHREAD_MUTEX_TYPE_MAX)) + /* Return an invalid argument error: */ + ret = EINVAL; + + /* Check mutex protocol: */ + else if (((*mutex_attr)->m_protocol < PTHREAD_PRIO_NONE) || + ((*mutex_attr)->m_protocol > PTHREAD_MUTEX_RECURSIVE)) + /* Return an invalid argument error: */ + ret = EINVAL; + + else { + /* Use the requested mutex type and protocol: */ + type = (*mutex_attr)->m_type; + protocol = (*mutex_attr)->m_protocol; + ceiling = (*mutex_attr)->m_ceiling; + flags = (*mutex_attr)->m_flags; + } + + /* Check no errors so far: */ + if (ret == 0) { + if ((pmutex = (pthread_mutex_t) + malloc(sizeof(struct pthread_mutex))) == NULL) + ret = ENOMEM; + else if (_lock_init(&pmutex->m_lock, LCK_ADAPTIVE, + _thr_lock_wait, _thr_lock_wakeup) != 0) { + free(pmutex); + *mutex = NULL; + ret = ENOMEM; + } else { + /* Set the mutex flags: */ + pmutex->m_flags = flags; + + /* Process according to mutex type: */ + switch (type) { + /* case PTHREAD_MUTEX_DEFAULT: */ + case PTHREAD_MUTEX_ERRORCHECK: + case PTHREAD_MUTEX_NORMAL: + /* Nothing to do here. */ + break; + + /* Single UNIX Spec 2 recursive mutex: */ + case PTHREAD_MUTEX_RECURSIVE: + /* Reset the mutex count: */ + pmutex->m_count = 0; + break; + + /* Trap invalid mutex types: */ + default: + /* Return an invalid argument error: */ + ret = EINVAL; + break; + } + if (ret == 0) { + /* Initialise the rest of the mutex: */ + TAILQ_INIT(&pmutex->m_queue); + pmutex->m_flags |= MUTEX_FLAGS_INITED; + pmutex->m_owner = NULL; + pmutex->m_type = type; + pmutex->m_protocol = protocol; + pmutex->m_refcount = 0; + if (protocol == PTHREAD_PRIO_PROTECT) + pmutex->m_prio = ceiling; + else + pmutex->m_prio = -1; + pmutex->m_saved_prio = 0; + MUTEX_INIT_LINK(pmutex); + *mutex = pmutex; + } else { + /* Free the mutex lock structure: */ + MUTEX_DESTROY(pmutex); + *mutex = NULL; + } + } + } + /* Return the completion status: */ + return (ret); +} + +int +_pthread_mutex_init(pthread_mutex_t *mutex, + const pthread_mutexattr_t *mutex_attr) +{ + struct pthread_mutex_attr mattr, *mattrp; + + if ((mutex_attr == NULL) || (*mutex_attr == NULL)) + return (__pthread_mutex_init(mutex, &static_mattr)); + else { + mattr = **mutex_attr; + mattr.m_flags |= MUTEX_FLAGS_PRIVATE; + mattrp = &mattr; + return (__pthread_mutex_init(mutex, &mattrp)); + } +} + +void +_thr_mutex_reinit(pthread_mutex_t *mutex) +{ + _lock_reinit(&(*mutex)->m_lock, LCK_ADAPTIVE, + _thr_lock_wait, _thr_lock_wakeup); + TAILQ_INIT(&(*mutex)->m_queue); + (*mutex)->m_owner = NULL; + (*mutex)->m_count = 0; + (*mutex)->m_refcount = 0; + (*mutex)->m_prio = 0; + (*mutex)->m_saved_prio = 0; +} + +int +_pthread_mutex_destroy(pthread_mutex_t *mutex) +{ + struct pthread *curthread = _get_curthread(); + pthread_mutex_t m; + int ret = 0; + + if (mutex == NULL || *mutex == NULL) + ret = EINVAL; + else { + /* Lock the mutex structure: */ + THR_LOCK_ACQUIRE(curthread, &(*mutex)->m_lock); + + /* + * Check to see if this mutex is in use: + */ + if (((*mutex)->m_owner != NULL) || + (!TAILQ_EMPTY(&(*mutex)->m_queue)) || + ((*mutex)->m_refcount != 0)) { + ret = EBUSY; + + /* Unlock the mutex structure: */ + THR_LOCK_RELEASE(curthread, &(*mutex)->m_lock); + } else { + /* + * Save a pointer to the mutex so it can be free'd + * and set the caller's pointer to NULL: + */ + m = *mutex; + *mutex = NULL; + + /* Unlock the mutex structure: */ + THR_LOCK_RELEASE(curthread, &m->m_lock); + + /* + * Free the memory allocated for the mutex + * structure: + */ + MUTEX_ASSERT_NOT_OWNED(m); + MUTEX_DESTROY(m); + } + } + + /* Return the completion status: */ + return (ret); +} + +static int +init_static(struct pthread *thread, pthread_mutex_t *mutex) +{ + int ret; + + THR_LOCK_ACQUIRE(thread, &_mutex_static_lock); + + if (*mutex == NULL) + ret = pthread_mutex_init(mutex, NULL); + else + ret = 0; + + THR_LOCK_RELEASE(thread, &_mutex_static_lock); + + return (ret); +} + +static int +init_static_private(struct pthread *thread, pthread_mutex_t *mutex) +{ + int ret; + + THR_LOCK_ACQUIRE(thread, &_mutex_static_lock); + + if (*mutex == NULL) + ret = pthread_mutex_init(mutex, &static_mattr); + else + ret = 0; + + THR_LOCK_RELEASE(thread, &_mutex_static_lock); + + return (ret); +} + +static int +mutex_trylock_common(struct pthread *curthread, pthread_mutex_t *mutex) +{ + int private; + int ret = 0; + + THR_ASSERT((mutex != NULL) && (*mutex != NULL), + "Uninitialized mutex in pthread_mutex_trylock_basic"); + + /* Lock the mutex structure: */ + THR_LOCK_ACQUIRE(curthread, &(*mutex)->m_lock); + private = (*mutex)->m_flags & MUTEX_FLAGS_PRIVATE; + + /* + * If the mutex was statically allocated, properly + * initialize the tail queue. + */ + if (((*mutex)->m_flags & MUTEX_FLAGS_INITED) == 0) { + TAILQ_INIT(&(*mutex)->m_queue); + MUTEX_INIT_LINK(*mutex); + (*mutex)->m_flags |= MUTEX_FLAGS_INITED; + } + + /* Process according to mutex type: */ + switch ((*mutex)->m_protocol) { + /* Default POSIX mutex: */ + case PTHREAD_PRIO_NONE: + /* Check if this mutex is not locked: */ + if ((*mutex)->m_owner == NULL) { + /* Lock the mutex for the running thread: */ + (*mutex)->m_owner = curthread; + + /* Add to the list of owned mutexes: */ + MUTEX_ASSERT_NOT_OWNED(*mutex); + TAILQ_INSERT_TAIL(&curthread->mutexq, + (*mutex), m_qe); + } else if ((*mutex)->m_owner == curthread) + ret = mutex_self_trylock(curthread, *mutex); + else + /* Return a busy error: */ + ret = EBUSY; + break; + + /* POSIX priority inheritence mutex: */ + case PTHREAD_PRIO_INHERIT: + /* Check if this mutex is not locked: */ + if ((*mutex)->m_owner == NULL) { + /* Lock the mutex for the running thread: */ + (*mutex)->m_owner = curthread; + + THR_SCHED_LOCK(curthread, curthread); + /* Track number of priority mutexes owned: */ + curthread->priority_mutex_count++; + + /* + * The mutex takes on the attributes of the + * running thread when there are no waiters. + */ + (*mutex)->m_prio = curthread->active_priority; + (*mutex)->m_saved_prio = + curthread->inherited_priority; + curthread->inherited_priority = (*mutex)->m_prio; + THR_SCHED_UNLOCK(curthread, curthread); + + /* Add to the list of owned mutexes: */ + MUTEX_ASSERT_NOT_OWNED(*mutex); + TAILQ_INSERT_TAIL(&curthread->mutexq, + (*mutex), m_qe); + } else if ((*mutex)->m_owner == curthread) + ret = mutex_self_trylock(curthread, *mutex); + else + /* Return a busy error: */ + ret = EBUSY; + break; + + /* POSIX priority protection mutex: */ + case PTHREAD_PRIO_PROTECT: + /* Check for a priority ceiling violation: */ + if (curthread->active_priority > (*mutex)->m_prio) + ret = EINVAL; + + /* Check if this mutex is not locked: */ + else if ((*mutex)->m_owner == NULL) { + /* Lock the mutex for the running thread: */ + (*mutex)->m_owner = curthread; + + THR_SCHED_LOCK(curthread, curthread); + /* Track number of priority mutexes owned: */ + curthread->priority_mutex_count++; + + /* + * The running thread inherits the ceiling + * priority of the mutex and executes at that + * priority. + */ + curthread->active_priority = (*mutex)->m_prio; + (*mutex)->m_saved_prio = + curthread->inherited_priority; + curthread->inherited_priority = + (*mutex)->m_prio; + THR_SCHED_UNLOCK(curthread, curthread); + /* Add to the list of owned mutexes: */ + MUTEX_ASSERT_NOT_OWNED(*mutex); + TAILQ_INSERT_TAIL(&curthread->mutexq, + (*mutex), m_qe); + } else if ((*mutex)->m_owner == curthread) + ret = mutex_self_trylock(curthread, *mutex); + else + /* Return a busy error: */ + ret = EBUSY; + break; + + /* Trap invalid mutex types: */ + default: + /* Return an invalid argument error: */ + ret = EINVAL; + break; + } + + if (ret == 0 && private) + THR_CRITICAL_ENTER(curthread); + + /* Unlock the mutex structure: */ + THR_LOCK_RELEASE(curthread, &(*mutex)->m_lock); + + /* Return the completion status: */ + return (ret); +} + +int +__pthread_mutex_trylock(pthread_mutex_t *mutex) +{ + struct pthread *curthread = _get_curthread(); + int ret = 0; + + if (mutex == NULL) + ret = EINVAL; + + /* + * If the mutex is statically initialized, perform the dynamic + * initialization: + */ + else if ((*mutex != NULL) || + ((ret = init_static(curthread, mutex)) == 0)) + ret = mutex_trylock_common(curthread, mutex); + + return (ret); +} + +int +_pthread_mutex_trylock(pthread_mutex_t *mutex) +{ + struct pthread *curthread = _get_curthread(); + int ret = 0; + + if (mutex == NULL) + ret = EINVAL; + + /* + * If the mutex is statically initialized, perform the dynamic + * initialization marking the mutex private (delete safe): + */ + else if ((*mutex != NULL) || + ((ret = init_static_private(curthread, mutex)) == 0)) + ret = mutex_trylock_common(curthread, mutex); + + return (ret); +} + +static int +mutex_lock_common(struct pthread *curthread, pthread_mutex_t *m, + const struct timespec * abstime) +{ + int private; + int ret = 0; + + THR_ASSERT((m != NULL) && (*m != NULL), + "Uninitialized mutex in pthread_mutex_trylock_basic"); + + if (abstime != NULL && (abstime->tv_sec < 0 || abstime->tv_nsec < 0 || + abstime->tv_nsec >= 1000000000)) + return (EINVAL); + + /* Reset the interrupted flag: */ + curthread->interrupted = 0; + curthread->timeout = 0; + curthread->wakeup_time.tv_sec = -1; + + private = (*m)->m_flags & MUTEX_FLAGS_PRIVATE; + + /* + * Enter a loop waiting to become the mutex owner. We need a + * loop in case the waiting thread is interrupted by a signal + * to execute a signal handler. It is not (currently) possible + * to remain in the waiting queue while running a handler. + * Instead, the thread is interrupted and backed out of the + * waiting queue prior to executing the signal handler. + */ + do { + /* Lock the mutex structure: */ + THR_LOCK_ACQUIRE(curthread, &(*m)->m_lock); + + /* + * If the mutex was statically allocated, properly + * initialize the tail queue. + */ + if (((*m)->m_flags & MUTEX_FLAGS_INITED) == 0) { + TAILQ_INIT(&(*m)->m_queue); + (*m)->m_flags |= MUTEX_FLAGS_INITED; + MUTEX_INIT_LINK(*m); + } + + /* Process according to mutex type: */ + switch ((*m)->m_protocol) { + /* Default POSIX mutex: */ + case PTHREAD_PRIO_NONE: + if ((*m)->m_owner == NULL) { + /* Lock the mutex for this thread: */ + (*m)->m_owner = curthread; + + /* Add to the list of owned mutexes: */ + MUTEX_ASSERT_NOT_OWNED(*m); + TAILQ_INSERT_TAIL(&curthread->mutexq, + (*m), m_qe); + if (private) + THR_CRITICAL_ENTER(curthread); + + /* Unlock the mutex structure: */ + THR_LOCK_RELEASE(curthread, &(*m)->m_lock); + } else if ((*m)->m_owner == curthread) { + ret = mutex_self_lock(curthread, *m); + + /* Unlock the mutex structure: */ + THR_LOCK_RELEASE(curthread, &(*m)->m_lock); + } else { + /* + * Join the queue of threads waiting to lock + * the mutex and save a pointer to the mutex. + */ + mutex_queue_enq(*m, curthread); + curthread->data.mutex = *m; + curthread->sigbackout = mutex_lock_backout; + /* + * This thread is active and is in a critical + * region (holding the mutex lock); we should + * be able to safely set the state. + */ + THR_SCHED_LOCK(curthread, curthread); + /* Set the wakeup time: */ + if (abstime) { + curthread->wakeup_time.tv_sec = + abstime->tv_sec; + curthread->wakeup_time.tv_nsec = + abstime->tv_nsec; + } + + THR_SET_STATE(curthread, PS_MUTEX_WAIT); + THR_SCHED_UNLOCK(curthread, curthread); + + /* Unlock the mutex structure: */ + THR_LOCK_RELEASE(curthread, &(*m)->m_lock); + + /* Schedule the next thread: */ + _thr_sched_switch(curthread); + + if (THR_IN_MUTEXQ(curthread)) { + THR_LOCK_ACQUIRE(curthread, &(*m)->m_lock); + mutex_queue_remove(*m, curthread); + THR_LOCK_RELEASE(curthread, &(*m)->m_lock); + } + /* + * Only clear these after assuring the + * thread is dequeued. + */ + curthread->data.mutex = NULL; + curthread->sigbackout = NULL; + } + break; + + /* POSIX priority inheritence mutex: */ + case PTHREAD_PRIO_INHERIT: + /* Check if this mutex is not locked: */ + if ((*m)->m_owner == NULL) { + /* Lock the mutex for this thread: */ + (*m)->m_owner = curthread; + + THR_SCHED_LOCK(curthread, curthread); + /* Track number of priority mutexes owned: */ + curthread->priority_mutex_count++; + + /* + * The mutex takes on attributes of the + * running thread when there are no waiters. + * Make sure the thread's scheduling lock is + * held while priorities are adjusted. + */ + (*m)->m_prio = curthread->active_priority; + (*m)->m_saved_prio = + curthread->inherited_priority; + curthread->inherited_priority = (*m)->m_prio; + THR_SCHED_UNLOCK(curthread, curthread); + + /* Add to the list of owned mutexes: */ + MUTEX_ASSERT_NOT_OWNED(*m); + TAILQ_INSERT_TAIL(&curthread->mutexq, + (*m), m_qe); + if (private) + THR_CRITICAL_ENTER(curthread); + + /* Unlock the mutex structure: */ + THR_LOCK_RELEASE(curthread, &(*m)->m_lock); + } else if ((*m)->m_owner == curthread) { + ret = mutex_self_lock(curthread, *m); + + /* Unlock the mutex structure: */ + THR_LOCK_RELEASE(curthread, &(*m)->m_lock); + } else { + /* + * Join the queue of threads waiting to lock + * the mutex and save a pointer to the mutex. + */ + mutex_queue_enq(*m, curthread); + curthread->data.mutex = *m; + curthread->sigbackout = mutex_lock_backout; + + /* + * This thread is active and is in a critical + * region (holding the mutex lock); we should + * be able to safely set the state. + */ + if (curthread->active_priority > (*m)->m_prio) + /* Adjust priorities: */ + mutex_priority_adjust(curthread, *m); + + THR_SCHED_LOCK(curthread, curthread); + /* Set the wakeup time: */ + if (abstime) { + curthread->wakeup_time.tv_sec = + abstime->tv_sec; + curthread->wakeup_time.tv_nsec = + abstime->tv_nsec; + } + THR_SET_STATE(curthread, PS_MUTEX_WAIT); + THR_SCHED_UNLOCK(curthread, curthread); + + /* Unlock the mutex structure: */ + THR_LOCK_RELEASE(curthread, &(*m)->m_lock); + + /* Schedule the next thread: */ + _thr_sched_switch(curthread); + + if (THR_IN_MUTEXQ(curthread)) { + THR_LOCK_ACQUIRE(curthread, &(*m)->m_lock); + mutex_queue_remove(*m, curthread); + THR_LOCK_RELEASE(curthread, &(*m)->m_lock); + } + /* + * Only clear these after assuring the + * thread is dequeued. + */ + curthread->data.mutex = NULL; + curthread->sigbackout = NULL; + } + break; + + /* POSIX priority protection mutex: */ + case PTHREAD_PRIO_PROTECT: + /* Check for a priority ceiling violation: */ + if (curthread->active_priority > (*m)->m_prio) { + /* Unlock the mutex structure: */ + THR_LOCK_RELEASE(curthread, &(*m)->m_lock); + ret = EINVAL; + } + /* Check if this mutex is not locked: */ + else if ((*m)->m_owner == NULL) { + /* + * Lock the mutex for the running + * thread: + */ + (*m)->m_owner = curthread; + + THR_SCHED_LOCK(curthread, curthread); + /* Track number of priority mutexes owned: */ + curthread->priority_mutex_count++; + + /* + * The running thread inherits the ceiling + * priority of the mutex and executes at that + * priority. Make sure the thread's + * scheduling lock is held while priorities + * are adjusted. + */ + curthread->active_priority = (*m)->m_prio; + (*m)->m_saved_prio = + curthread->inherited_priority; + curthread->inherited_priority = (*m)->m_prio; + THR_SCHED_UNLOCK(curthread, curthread); + + /* Add to the list of owned mutexes: */ + MUTEX_ASSERT_NOT_OWNED(*m); + TAILQ_INSERT_TAIL(&curthread->mutexq, + (*m), m_qe); + if (private) + THR_CRITICAL_ENTER(curthread); + + /* Unlock the mutex structure: */ + THR_LOCK_RELEASE(curthread, &(*m)->m_lock); + } else if ((*m)->m_owner == curthread) { + ret = mutex_self_lock(curthread, *m); + + /* Unlock the mutex structure: */ + THR_LOCK_RELEASE(curthread, &(*m)->m_lock); + } else { + /* + * Join the queue of threads waiting to lock + * the mutex and save a pointer to the mutex. + */ + mutex_queue_enq(*m, curthread); + curthread->data.mutex = *m; + curthread->sigbackout = mutex_lock_backout; + + /* Clear any previous error: */ + curthread->error = 0; + + /* + * This thread is active and is in a critical + * region (holding the mutex lock); we should + * be able to safely set the state. + */ + + THR_SCHED_LOCK(curthread, curthread); + /* Set the wakeup time: */ + if (abstime) { + curthread->wakeup_time.tv_sec = + abstime->tv_sec; + curthread->wakeup_time.tv_nsec = + abstime->tv_nsec; + } + THR_SET_STATE(curthread, PS_MUTEX_WAIT); + THR_SCHED_UNLOCK(curthread, curthread); + + /* Unlock the mutex structure: */ + THR_LOCK_RELEASE(curthread, &(*m)->m_lock); + + /* Schedule the next thread: */ + _thr_sched_switch(curthread); + + if (THR_IN_MUTEXQ(curthread)) { + THR_LOCK_ACQUIRE(curthread, &(*m)->m_lock); + mutex_queue_remove(*m, curthread); + THR_LOCK_RELEASE(curthread, &(*m)->m_lock); + } + /* + * Only clear these after assuring the + * thread is dequeued. + */ + curthread->data.mutex = NULL; + curthread->sigbackout = NULL; + + /* + * The threads priority may have changed while + * waiting for the mutex causing a ceiling + * violation. + */ + ret = curthread->error; + curthread->error = 0; + } + break; + + /* Trap invalid mutex types: */ + default: + /* Unlock the mutex structure: */ + THR_LOCK_RELEASE(curthread, &(*m)->m_lock); + + /* Return an invalid argument error: */ + ret = EINVAL; + break; + } + + } while (((*m)->m_owner != curthread) && (ret == 0) && + (curthread->interrupted == 0) && (curthread->timeout == 0)); + + if (ret == 0 && (*m)->m_owner != curthread && curthread->timeout) + ret = ETIMEDOUT; + + /* + * Check to see if this thread was interrupted and + * is still in the mutex queue of waiting threads: + */ + if (curthread->interrupted != 0) { + /* Remove this thread from the mutex queue. */ + THR_LOCK_ACQUIRE(curthread, &(*m)->m_lock); + if (THR_IN_SYNCQ(curthread)) + mutex_queue_remove(*m, curthread); + THR_LOCK_RELEASE(curthread, &(*m)->m_lock); + + /* Check for asynchronous cancellation. */ + if (curthread->continuation != NULL) + curthread->continuation((void *) curthread); + } + + /* Return the completion status: */ + return (ret); +} + +int +__pthread_mutex_lock(pthread_mutex_t *m) +{ + struct pthread *curthread; + int ret = 0; + + if (_thr_initial == NULL) + _libpthread_init(NULL); + + curthread = _get_curthread(); + if (m == NULL) + ret = EINVAL; + + /* + * If the mutex is statically initialized, perform the dynamic + * initialization: + */ + else if ((*m != NULL) || ((ret = init_static(curthread, m)) == 0)) + ret = mutex_lock_common(curthread, m, NULL); + + return (ret); +} + +__strong_reference(__pthread_mutex_lock, _thr_mutex_lock); + +int +_pthread_mutex_lock(pthread_mutex_t *m) +{ + struct pthread *curthread; + int ret = 0; + + if (_thr_initial == NULL) + _libpthread_init(NULL); + curthread = _get_curthread(); + + if (m == NULL) + ret = EINVAL; + + /* + * If the mutex is statically initialized, perform the dynamic + * initialization marking it private (delete safe): + */ + else if ((*m != NULL) || + ((ret = init_static_private(curthread, m)) == 0)) + ret = mutex_lock_common(curthread, m, NULL); + + return (ret); +} + +int +__pthread_mutex_timedlock(pthread_mutex_t *m, + const struct timespec *abs_timeout) +{ + struct pthread *curthread; + int ret = 0; + + if (_thr_initial == NULL) + _libpthread_init(NULL); + + curthread = _get_curthread(); + if (m == NULL) + ret = EINVAL; + + /* + * If the mutex is statically initialized, perform the dynamic + * initialization: + */ + else if ((*m != NULL) || ((ret = init_static(curthread, m)) == 0)) + ret = mutex_lock_common(curthread, m, abs_timeout); + + return (ret); +} + +int +_pthread_mutex_timedlock(pthread_mutex_t *m, + const struct timespec *abs_timeout) +{ + struct pthread *curthread; + int ret = 0; + + if (_thr_initial == NULL) + _libpthread_init(NULL); + curthread = _get_curthread(); + + if (m == NULL) + ret = EINVAL; + + /* + * If the mutex is statically initialized, perform the dynamic + * initialization marking it private (delete safe): + */ + else if ((*m != NULL) || + ((ret = init_static_private(curthread, m)) == 0)) + ret = mutex_lock_common(curthread, m, abs_timeout); + + return (ret); +} + +int +_pthread_mutex_unlock(pthread_mutex_t *m) +{ + return (mutex_unlock_common(m, /* add reference */ 0)); +} + +__strong_reference(_pthread_mutex_unlock, _thr_mutex_unlock); + +int +_mutex_cv_unlock(pthread_mutex_t *m) +{ + return (mutex_unlock_common(m, /* add reference */ 1)); +} + +int +_mutex_cv_lock(pthread_mutex_t *m) +{ + struct pthread *curthread; + int ret; + + curthread = _get_curthread(); + if ((ret = _pthread_mutex_lock(m)) == 0) { + THR_LOCK_ACQUIRE(curthread, &(*m)->m_lock); + (*m)->m_refcount--; + THR_LOCK_RELEASE(curthread, &(*m)->m_lock); + } + return (ret); +} + +static inline int +mutex_self_trylock(struct pthread *curthread, pthread_mutex_t m) +{ + int ret = 0; + + switch (m->m_type) { + /* case PTHREAD_MUTEX_DEFAULT: */ + case PTHREAD_MUTEX_ERRORCHECK: + case PTHREAD_MUTEX_NORMAL: + ret = EBUSY; + break; + + case PTHREAD_MUTEX_RECURSIVE: + /* Increment the lock count: */ + m->m_count++; + break; + + default: + /* Trap invalid mutex types; */ + ret = EINVAL; + } + + return (ret); +} + +static inline int +mutex_self_lock(struct pthread *curthread, pthread_mutex_t m) +{ + int ret = 0; + + /* + * Don't allow evil recursive mutexes for private use + * in libc and libpthread. + */ + if (m->m_flags & MUTEX_FLAGS_PRIVATE) + PANIC("Recurse on a private mutex."); + + switch (m->m_type) { + /* case PTHREAD_MUTEX_DEFAULT: */ + case PTHREAD_MUTEX_ERRORCHECK: + /* + * POSIX specifies that mutexes should return EDEADLK if a + * recursive lock is detected. + */ + ret = EDEADLK; + break; + + case PTHREAD_MUTEX_NORMAL: + /* + * What SS2 define as a 'normal' mutex. Intentionally + * deadlock on attempts to get a lock you already own. + */ + + THR_SCHED_LOCK(curthread, curthread); + THR_SET_STATE(curthread, PS_DEADLOCK); + THR_SCHED_UNLOCK(curthread, curthread); + + /* Unlock the mutex structure: */ + THR_LOCK_RELEASE(curthread, &m->m_lock); + + /* Schedule the next thread: */ + _thr_sched_switch(curthread); + break; + + case PTHREAD_MUTEX_RECURSIVE: + /* Increment the lock count: */ + m->m_count++; + break; + + default: + /* Trap invalid mutex types; */ + ret = EINVAL; + } + + return (ret); +} + +static int +mutex_unlock_common(pthread_mutex_t *m, int add_reference) +{ + struct pthread *curthread = _get_curthread(); + struct kse_mailbox *kmbx = NULL; + int ret = 0; + + if (m == NULL || *m == NULL) + ret = EINVAL; + else { + /* Lock the mutex structure: */ + THR_LOCK_ACQUIRE(curthread, &(*m)->m_lock); + + /* Process according to mutex type: */ + switch ((*m)->m_protocol) { + /* Default POSIX mutex: */ + case PTHREAD_PRIO_NONE: + /* + * Check if the running thread is not the owner of the + * mutex: + */ + if ((*m)->m_owner != curthread) + ret = EPERM; + else if (((*m)->m_type == PTHREAD_MUTEX_RECURSIVE) && + ((*m)->m_count > 0)) + /* Decrement the count: */ + (*m)->m_count--; + else { + /* + * Clear the count in case this is a recursive + * mutex. + */ + (*m)->m_count = 0; + + /* Remove the mutex from the threads queue. */ + MUTEX_ASSERT_IS_OWNED(*m); + TAILQ_REMOVE(&(*m)->m_owner->mutexq, + (*m), m_qe); + MUTEX_INIT_LINK(*m); + + /* + * Hand off the mutex to the next waiting + * thread: + */ + kmbx = mutex_handoff(curthread, *m); + } + break; + + /* POSIX priority inheritence mutex: */ + case PTHREAD_PRIO_INHERIT: + /* + * Check if the running thread is not the owner of the + * mutex: + */ + if ((*m)->m_owner != curthread) + ret = EPERM; + else if (((*m)->m_type == PTHREAD_MUTEX_RECURSIVE) && + ((*m)->m_count > 0)) + /* Decrement the count: */ + (*m)->m_count--; + else { + /* + * Clear the count in case this is recursive + * mutex. + */ + (*m)->m_count = 0; + + /* + * Restore the threads inherited priority and + * recompute the active priority (being careful + * not to override changes in the threads base + * priority subsequent to locking the mutex). + */ + THR_SCHED_LOCK(curthread, curthread); + curthread->inherited_priority = + (*m)->m_saved_prio; + curthread->active_priority = + MAX(curthread->inherited_priority, + curthread->base_priority); + + /* + * This thread now owns one less priority mutex. + */ + curthread->priority_mutex_count--; + THR_SCHED_UNLOCK(curthread, curthread); + + /* Remove the mutex from the threads queue. */ + MUTEX_ASSERT_IS_OWNED(*m); + TAILQ_REMOVE(&(*m)->m_owner->mutexq, + (*m), m_qe); + MUTEX_INIT_LINK(*m); + + /* + * Hand off the mutex to the next waiting + * thread: + */ + kmbx = mutex_handoff(curthread, *m); + } + break; + + /* POSIX priority ceiling mutex: */ + case PTHREAD_PRIO_PROTECT: + /* + * Check if the running thread is not the owner of the + * mutex: + */ + if ((*m)->m_owner != curthread) + ret = EPERM; + else if (((*m)->m_type == PTHREAD_MUTEX_RECURSIVE) && + ((*m)->m_count > 0)) + /* Decrement the count: */ + (*m)->m_count--; + else { + /* + * Clear the count in case this is a recursive + * mutex. + */ + (*m)->m_count = 0; + + /* + * Restore the threads inherited priority and + * recompute the active priority (being careful + * not to override changes in the threads base + * priority subsequent to locking the mutex). + */ + THR_SCHED_LOCK(curthread, curthread); + curthread->inherited_priority = + (*m)->m_saved_prio; + curthread->active_priority = + MAX(curthread->inherited_priority, + curthread->base_priority); + + /* + * This thread now owns one less priority mutex. + */ + curthread->priority_mutex_count--; + THR_SCHED_UNLOCK(curthread, curthread); + + /* Remove the mutex from the threads queue. */ + MUTEX_ASSERT_IS_OWNED(*m); + TAILQ_REMOVE(&(*m)->m_owner->mutexq, + (*m), m_qe); + MUTEX_INIT_LINK(*m); + + /* + * Hand off the mutex to the next waiting + * thread: + */ + kmbx = mutex_handoff(curthread, *m); + } + break; + + /* Trap invalid mutex types: */ + default: + /* Return an invalid argument error: */ + ret = EINVAL; + break; + } + + if ((ret == 0) && (add_reference != 0)) + /* Increment the reference count: */ + (*m)->m_refcount++; + + /* Leave the critical region if this is a private mutex. */ + if ((ret == 0) && ((*m)->m_flags & MUTEX_FLAGS_PRIVATE)) + THR_CRITICAL_LEAVE(curthread); + + /* Unlock the mutex structure: */ + THR_LOCK_RELEASE(curthread, &(*m)->m_lock); + + if (kmbx != NULL) + kse_wakeup(kmbx); + } + + /* Return the completion status: */ + return (ret); +} + + +/* + * This function is called when a change in base priority occurs for + * a thread that is holding or waiting for a priority protection or + * inheritence mutex. A change in a threads base priority can effect + * changes to active priorities of other threads and to the ordering + * of mutex locking by waiting threads. + * + * This must be called without the target thread's scheduling lock held. + */ +void +_mutex_notify_priochange(struct pthread *curthread, struct pthread *pthread, + int propagate_prio) +{ + struct pthread_mutex *m; + + /* Adjust the priorites of any owned priority mutexes: */ + if (pthread->priority_mutex_count > 0) { + /* + * Rescan the mutexes owned by this thread and correct + * their priorities to account for this threads change + * in priority. This has the side effect of changing + * the threads active priority. + * + * Be sure to lock the first mutex in the list of owned + * mutexes. This acts as a barrier against another + * simultaneous call to change the threads priority + * and from the owning thread releasing the mutex. + */ + m = TAILQ_FIRST(&pthread->mutexq); + if (m != NULL) { + THR_LOCK_ACQUIRE(curthread, &m->m_lock); + /* + * Make sure the thread still owns the lock. + */ + if (m == TAILQ_FIRST(&pthread->mutexq)) + mutex_rescan_owned(curthread, pthread, + /* rescan all owned */ NULL); + THR_LOCK_RELEASE(curthread, &m->m_lock); + } + } + + /* + * If this thread is waiting on a priority inheritence mutex, + * check for priority adjustments. A change in priority can + * also cause a ceiling violation(*) for a thread waiting on + * a priority protection mutex; we don't perform the check here + * as it is done in pthread_mutex_unlock. + * + * (*) It should be noted that a priority change to a thread + * _after_ taking and owning a priority ceiling mutex + * does not affect ownership of that mutex; the ceiling + * priority is only checked before mutex ownership occurs. + */ + if (propagate_prio != 0) { + /* + * Lock the thread's scheduling queue. This is a bit + * convoluted; the "in synchronization queue flag" can + * only be cleared with both the thread's scheduling and + * mutex locks held. The thread's pointer to the wanted + * mutex is guaranteed to be valid during this time. + */ + THR_SCHED_LOCK(curthread, pthread); + + if (((pthread->sflags & THR_FLAGS_IN_SYNCQ) == 0) || + ((m = pthread->data.mutex) == NULL)) + THR_SCHED_UNLOCK(curthread, pthread); + else { + /* + * This thread is currently waiting on a mutex; unlock + * the scheduling queue lock and lock the mutex. We + * can't hold both at the same time because the locking + * order could cause a deadlock. + */ + THR_SCHED_UNLOCK(curthread, pthread); + THR_LOCK_ACQUIRE(curthread, &m->m_lock); + + /* + * Check to make sure this thread is still in the + * same state (the lock above can yield the CPU to + * another thread or the thread may be running on + * another CPU). + */ + if (((pthread->sflags & THR_FLAGS_IN_SYNCQ) != 0) && + (pthread->data.mutex == m)) { + /* + * Remove and reinsert this thread into + * the list of waiting threads to preserve + * decreasing priority order. + */ + mutex_queue_remove(m, pthread); + mutex_queue_enq(m, pthread); + + if (m->m_protocol == PTHREAD_PRIO_INHERIT) + /* Adjust priorities: */ + mutex_priority_adjust(curthread, m); + } + + /* Unlock the mutex structure: */ + THR_LOCK_RELEASE(curthread, &m->m_lock); + } + } +} + +/* + * Called when a new thread is added to the mutex waiting queue or + * when a threads priority changes that is already in the mutex + * waiting queue. + * + * This must be called with the mutex locked by the current thread. + */ +static void +mutex_priority_adjust(struct pthread *curthread, pthread_mutex_t mutex) +{ + pthread_mutex_t m = mutex; + struct pthread *pthread_next, *pthread = mutex->m_owner; + int done, temp_prio; + + /* + * Calculate the mutex priority as the maximum of the highest + * active priority of any waiting threads and the owning threads + * active priority(*). + * + * (*) Because the owning threads current active priority may + * reflect priority inherited from this mutex (and the mutex + * priority may have changed) we must recalculate the active + * priority based on the threads saved inherited priority + * and its base priority. + */ + pthread_next = TAILQ_FIRST(&m->m_queue); /* should never be NULL */ + temp_prio = MAX(pthread_next->active_priority, + MAX(m->m_saved_prio, pthread->base_priority)); + + /* See if this mutex really needs adjusting: */ + if (temp_prio == m->m_prio) + /* No need to propagate the priority: */ + return; + + /* Set new priority of the mutex: */ + m->m_prio = temp_prio; + + /* + * Don't unlock the mutex passed in as an argument. It is + * expected to be locked and unlocked by the caller. + */ + done = 1; + do { + /* + * Save the threads priority before rescanning the + * owned mutexes: + */ + temp_prio = pthread->active_priority; + + /* + * Fix the priorities for all mutexes held by the owning + * thread since taking this mutex. This also has a + * potential side-effect of changing the threads priority. + * + * At this point the mutex is locked by the current thread. + * The owning thread can't release the mutex until it is + * unlocked, so we should be able to safely walk its list + * of owned mutexes. + */ + mutex_rescan_owned(curthread, pthread, m); + + /* + * If this isn't the first time through the loop, + * the current mutex needs to be unlocked. + */ + if (done == 0) + THR_LOCK_RELEASE(curthread, &m->m_lock); + + /* Assume we're done unless told otherwise: */ + done = 1; + + /* + * If the thread is currently waiting on a mutex, check + * to see if the threads new priority has affected the + * priority of the mutex. + */ + if ((temp_prio != pthread->active_priority) && + ((pthread->sflags & THR_FLAGS_IN_SYNCQ) != 0) && + ((m = pthread->data.mutex) != NULL) && + (m->m_protocol == PTHREAD_PRIO_INHERIT)) { + /* Lock the mutex structure: */ + THR_LOCK_ACQUIRE(curthread, &m->m_lock); + + /* + * Make sure the thread is still waiting on the + * mutex: + */ + if (((pthread->sflags & THR_FLAGS_IN_SYNCQ) != 0) && + (m == pthread->data.mutex)) { + /* + * The priority for this thread has changed. + * Remove and reinsert this thread into the + * list of waiting threads to preserve + * decreasing priority order. + */ + mutex_queue_remove(m, pthread); + mutex_queue_enq(m, pthread); + + /* + * Grab the waiting thread with highest + * priority: + */ + pthread_next = TAILQ_FIRST(&m->m_queue); + + /* + * Calculate the mutex priority as the maximum + * of the highest active priority of any + * waiting threads and the owning threads + * active priority. + */ + temp_prio = MAX(pthread_next->active_priority, + MAX(m->m_saved_prio, + m->m_owner->base_priority)); + + if (temp_prio != m->m_prio) { + /* + * The priority needs to be propagated + * to the mutex this thread is waiting + * on and up to the owner of that mutex. + */ + m->m_prio = temp_prio; + pthread = m->m_owner; + + /* We're not done yet: */ + done = 0; + } + } + /* Only release the mutex if we're done: */ + if (done != 0) + THR_LOCK_RELEASE(curthread, &m->m_lock); + } + } while (done == 0); +} + +static void +mutex_rescan_owned(struct pthread *curthread, struct pthread *pthread, + struct pthread_mutex *mutex) +{ + struct pthread_mutex *m; + struct pthread *pthread_next; + int active_prio, inherited_prio; + + /* + * Start walking the mutexes the thread has taken since + * taking this mutex. + */ + if (mutex == NULL) { + /* + * A null mutex means start at the beginning of the owned + * mutex list. + */ + m = TAILQ_FIRST(&pthread->mutexq); + + /* There is no inherited priority yet. */ + inherited_prio = 0; + } else { + /* + * The caller wants to start after a specific mutex. It + * is assumed that this mutex is a priority inheritence + * mutex and that its priority has been correctly + * calculated. + */ + m = TAILQ_NEXT(mutex, m_qe); + + /* Start inheriting priority from the specified mutex. */ + inherited_prio = mutex->m_prio; + } + active_prio = MAX(inherited_prio, pthread->base_priority); + + for (; m != NULL; m = TAILQ_NEXT(m, m_qe)) { + /* + * We only want to deal with priority inheritence + * mutexes. This might be optimized by only placing + * priority inheritence mutexes into the owned mutex + * list, but it may prove to be useful having all + * owned mutexes in this list. Consider a thread + * exiting while holding mutexes... + */ + if (m->m_protocol == PTHREAD_PRIO_INHERIT) { + /* + * Fix the owners saved (inherited) priority to + * reflect the priority of the previous mutex. + */ + m->m_saved_prio = inherited_prio; + + if ((pthread_next = TAILQ_FIRST(&m->m_queue)) != NULL) + /* Recalculate the priority of the mutex: */ + m->m_prio = MAX(active_prio, + pthread_next->active_priority); + else + m->m_prio = active_prio; + + /* Recalculate new inherited and active priorities: */ + inherited_prio = m->m_prio; + active_prio = MAX(m->m_prio, pthread->base_priority); + } + } + + /* + * Fix the threads inherited priority and recalculate its + * active priority. + */ + pthread->inherited_priority = inherited_prio; + active_prio = MAX(inherited_prio, pthread->base_priority); + + if (active_prio != pthread->active_priority) { + /* Lock the thread's scheduling queue: */ + THR_SCHED_LOCK(curthread, pthread); + + if ((pthread->flags & THR_FLAGS_IN_RUNQ) == 0) { + /* + * This thread is not in a run queue. Just set + * its active priority. + */ + pthread->active_priority = active_prio; + } + else { + /* + * This thread is in a run queue. Remove it from + * the queue before changing its priority: + */ + THR_RUNQ_REMOVE(pthread); + + /* + * POSIX states that if the priority is being + * lowered, the thread must be inserted at the + * head of the queue for its priority if it owns + * any priority protection or inheritence mutexes. + */ + if ((active_prio < pthread->active_priority) && + (pthread->priority_mutex_count > 0)) { + /* Set the new active priority. */ + pthread->active_priority = active_prio; + + THR_RUNQ_INSERT_HEAD(pthread); + } else { + /* Set the new active priority. */ + pthread->active_priority = active_prio; + + THR_RUNQ_INSERT_TAIL(pthread); + } + } + THR_SCHED_UNLOCK(curthread, pthread); + } +} + +void +_mutex_unlock_private(pthread_t pthread) +{ + struct pthread_mutex *m, *m_next; + + for (m = TAILQ_FIRST(&pthread->mutexq); m != NULL; m = m_next) { + m_next = TAILQ_NEXT(m, m_qe); + if ((m->m_flags & MUTEX_FLAGS_PRIVATE) != 0) + pthread_mutex_unlock(&m); + } +} + +/* + * This is called by the current thread when it wants to back out of a + * mutex_lock in order to run a signal handler. + */ +static void +mutex_lock_backout(void *arg) +{ + struct pthread *curthread = (struct pthread *)arg; + struct pthread_mutex *m; + + if ((curthread->sflags & THR_FLAGS_IN_SYNCQ) != 0) { + /* + * Any other thread may clear the "in sync queue flag", + * but only the current thread can clear the pointer + * to the mutex. So if the flag is set, we can + * guarantee that the pointer to the mutex is valid. + * The only problem may be if the mutex is destroyed + * out from under us, but that should be considered + * an application bug. + */ + m = curthread->data.mutex; + + /* Lock the mutex structure: */ + THR_LOCK_ACQUIRE(curthread, &m->m_lock); + + + /* + * Check to make sure this thread doesn't already own + * the mutex. Since mutexes are unlocked with direct + * handoffs, it is possible the previous owner gave it + * to us after we checked the sync queue flag and before + * we locked the mutex structure. + */ + if (m->m_owner == curthread) { + THR_LOCK_RELEASE(curthread, &m->m_lock); + mutex_unlock_common(&m, /* add_reference */ 0); + } else { + /* + * Remove ourselves from the mutex queue and + * clear the pointer to the mutex. We may no + * longer be in the mutex queue, but the removal + * function will DTRT. + */ + mutex_queue_remove(m, curthread); + curthread->data.mutex = NULL; + THR_LOCK_RELEASE(curthread, &m->m_lock); + } + } + /* No need to call this again. */ + curthread->sigbackout = NULL; +} + +/* + * Dequeue a waiting thread from the head of a mutex queue in descending + * priority order. + * + * In order to properly dequeue a thread from the mutex queue and + * make it runnable without the possibility of errant wakeups, it + * is necessary to lock the thread's scheduling queue while also + * holding the mutex lock. + */ +static struct kse_mailbox * +mutex_handoff(struct pthread *curthread, struct pthread_mutex *mutex) +{ + struct kse_mailbox *kmbx = NULL; + struct pthread *pthread; + + /* Keep dequeueing until we find a valid thread: */ + mutex->m_owner = NULL; + pthread = TAILQ_FIRST(&mutex->m_queue); + while (pthread != NULL) { + /* Take the thread's scheduling lock: */ + THR_SCHED_LOCK(curthread, pthread); + + /* Remove the thread from the mutex queue: */ + TAILQ_REMOVE(&mutex->m_queue, pthread, sqe); + pthread->sflags &= ~THR_FLAGS_IN_SYNCQ; + + /* + * Only exit the loop if the thread hasn't been + * cancelled. + */ + switch (mutex->m_protocol) { + case PTHREAD_PRIO_NONE: + /* + * Assign the new owner and add the mutex to the + * thread's list of owned mutexes. + */ + mutex->m_owner = pthread; + TAILQ_INSERT_TAIL(&pthread->mutexq, mutex, m_qe); + break; + + case PTHREAD_PRIO_INHERIT: + /* + * Assign the new owner and add the mutex to the + * thread's list of owned mutexes. + */ + mutex->m_owner = pthread; + TAILQ_INSERT_TAIL(&pthread->mutexq, mutex, m_qe); + + /* Track number of priority mutexes owned: */ + pthread->priority_mutex_count++; + + /* + * Set the priority of the mutex. Since our waiting + * threads are in descending priority order, the + * priority of the mutex becomes the active priority + * of the thread we just dequeued. + */ + mutex->m_prio = pthread->active_priority; + + /* Save the owning threads inherited priority: */ + mutex->m_saved_prio = pthread->inherited_priority; + + /* + * The owning threads inherited priority now becomes + * his active priority (the priority of the mutex). + */ + pthread->inherited_priority = mutex->m_prio; + break; + + case PTHREAD_PRIO_PROTECT: + if (pthread->active_priority > mutex->m_prio) { + /* + * Either the mutex ceiling priority has + * been lowered and/or this threads priority + * has been raised subsequent to the thread + * being queued on the waiting list. + */ + pthread->error = EINVAL; + } + else { + /* + * Assign the new owner and add the mutex + * to the thread's list of owned mutexes. + */ + mutex->m_owner = pthread; + TAILQ_INSERT_TAIL(&pthread->mutexq, + mutex, m_qe); + + /* Track number of priority mutexes owned: */ + pthread->priority_mutex_count++; + + /* + * Save the owning threads inherited + * priority: + */ + mutex->m_saved_prio = + pthread->inherited_priority; + + /* + * The owning thread inherits the ceiling + * priority of the mutex and executes at + * that priority: + */ + pthread->inherited_priority = mutex->m_prio; + pthread->active_priority = mutex->m_prio; + + } + break; + } + + /* Make the thread runnable and unlock the scheduling queue: */ + kmbx = _thr_setrunnable_unlocked(pthread); + + /* Add a preemption point. */ + if ((curthread->kseg == pthread->kseg) && + (pthread->active_priority > curthread->active_priority)) + curthread->critical_yield = 1; + + if (mutex->m_owner == pthread) { + /* We're done; a valid owner was found. */ + if (mutex->m_flags & MUTEX_FLAGS_PRIVATE) + THR_CRITICAL_ENTER(pthread); + THR_SCHED_UNLOCK(curthread, pthread); + break; + } + THR_SCHED_UNLOCK(curthread, pthread); + /* Get the next thread from the waiting queue: */ + pthread = TAILQ_NEXT(pthread, sqe); + } + + if ((pthread == NULL) && (mutex->m_protocol == PTHREAD_PRIO_INHERIT)) + /* This mutex has no priority: */ + mutex->m_prio = 0; + return (kmbx); +} + +/* + * Dequeue a waiting thread from the head of a mutex queue in descending + * priority order. + */ +static inline pthread_t +mutex_queue_deq(struct pthread_mutex *mutex) +{ + pthread_t pthread; + + while ((pthread = TAILQ_FIRST(&mutex->m_queue)) != NULL) { + TAILQ_REMOVE(&mutex->m_queue, pthread, sqe); + pthread->sflags &= ~THR_FLAGS_IN_SYNCQ; + + /* + * Only exit the loop if the thread hasn't been + * cancelled. + */ + if (pthread->interrupted == 0) + break; + } + + return (pthread); +} + +/* + * Remove a waiting thread from a mutex queue in descending priority order. + */ +static inline void +mutex_queue_remove(pthread_mutex_t mutex, pthread_t pthread) +{ + if ((pthread->sflags & THR_FLAGS_IN_SYNCQ) != 0) { + TAILQ_REMOVE(&mutex->m_queue, pthread, sqe); + pthread->sflags &= ~THR_FLAGS_IN_SYNCQ; + } +} + +/* + * Enqueue a waiting thread to a queue in descending priority order. + */ +static inline void +mutex_queue_enq(pthread_mutex_t mutex, pthread_t pthread) +{ + pthread_t tid = TAILQ_LAST(&mutex->m_queue, mutex_head); + + THR_ASSERT_NOT_IN_SYNCQ(pthread); + /* + * For the common case of all threads having equal priority, + * we perform a quick check against the priority of the thread + * at the tail of the queue. + */ + if ((tid == NULL) || (pthread->active_priority <= tid->active_priority)) + TAILQ_INSERT_TAIL(&mutex->m_queue, pthread, sqe); + else { + tid = TAILQ_FIRST(&mutex->m_queue); + while (pthread->active_priority <= tid->active_priority) + tid = TAILQ_NEXT(tid, sqe); + TAILQ_INSERT_BEFORE(tid, pthread, sqe); + } + pthread->sflags |= THR_FLAGS_IN_SYNCQ; +} diff --git a/lib/libpthread/thread/thr_mutex_prioceiling.c b/lib/libpthread/thread/thr_mutex_prioceiling.c new file mode 100644 index 0000000..f254346 --- /dev/null +++ b/lib/libpthread/thread/thr_mutex_prioceiling.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>. + * 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 Daniel Eischen. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_mutexattr_getprioceiling); +LT10_COMPAT_DEFAULT(pthread_mutexattr_getprioceiling); +LT10_COMPAT_PRIVATE(_pthread_mutexattr_setprioceiling); +LT10_COMPAT_DEFAULT(pthread_mutexattr_setprioceiling); +LT10_COMPAT_PRIVATE(_pthread_mutex_getprioceiling); +LT10_COMPAT_DEFAULT(pthread_mutex_getprioceiling); +LT10_COMPAT_PRIVATE(_pthread_mutex_setprioceiling); +LT10_COMPAT_DEFAULT(pthread_mutex_setprioceiling); + +__weak_reference(_pthread_mutexattr_getprioceiling, pthread_mutexattr_getprioceiling); +__weak_reference(_pthread_mutexattr_setprioceiling, pthread_mutexattr_setprioceiling); +__weak_reference(_pthread_mutex_getprioceiling, pthread_mutex_getprioceiling); +__weak_reference(_pthread_mutex_setprioceiling, pthread_mutex_setprioceiling); + +int +_pthread_mutexattr_getprioceiling(pthread_mutexattr_t *mattr, int *prioceiling) +{ + int ret = 0; + + if ((mattr == NULL) || (*mattr == NULL)) + ret = EINVAL; + else if ((*mattr)->m_protocol != PTHREAD_PRIO_PROTECT) + ret = EINVAL; + else + *prioceiling = (*mattr)->m_ceiling; + + return(ret); +} + +int +_pthread_mutexattr_setprioceiling(pthread_mutexattr_t *mattr, int prioceiling) +{ + int ret = 0; + + if ((mattr == NULL) || (*mattr == NULL)) + ret = EINVAL; + else if ((*mattr)->m_protocol != PTHREAD_PRIO_PROTECT) + ret = EINVAL; + else + (*mattr)->m_ceiling = prioceiling; + + return(ret); +} + +int +_pthread_mutex_getprioceiling(pthread_mutex_t *mutex, + int *prioceiling) +{ + int ret; + + if ((mutex == NULL) || (*mutex == NULL)) + ret = EINVAL; + else if ((*mutex)->m_protocol != PTHREAD_PRIO_PROTECT) + ret = EINVAL; + else + ret = (*mutex)->m_prio; + + return(ret); +} + +int +_pthread_mutex_setprioceiling(pthread_mutex_t *mutex, + int prioceiling, int *old_ceiling) +{ + int ret = 0; + int tmp; + + if ((mutex == NULL) || (*mutex == NULL)) + ret = EINVAL; + else if ((*mutex)->m_protocol != PTHREAD_PRIO_PROTECT) + ret = EINVAL; + /* Lock the mutex: */ + else if ((ret = pthread_mutex_lock(mutex)) == 0) { + tmp = (*mutex)->m_prio; + /* Set the new ceiling: */ + (*mutex)->m_prio = prioceiling; + + /* Unlock the mutex: */ + ret = pthread_mutex_unlock(mutex); + + /* Return the old ceiling: */ + *old_ceiling = tmp; + } + return(ret); +} diff --git a/lib/libpthread/thread/thr_mutex_protocol.c b/lib/libpthread/thread/thr_mutex_protocol.c new file mode 100644 index 0000000..9e3e46b --- /dev/null +++ b/lib/libpthread/thread/thr_mutex_protocol.c @@ -0,0 +1,75 @@ +/* + * Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>. + * 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 Daniel Eischen. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_mutexattr_getprotocol); +LT10_COMPAT_DEFAULT(pthread_mutexattr_getprotocol); +LT10_COMPAT_PRIVATE(_pthread_mutexattr_setprotocol); +LT10_COMPAT_DEFAULT(pthread_mutexattr_setprotocol); + +__weak_reference(_pthread_mutexattr_getprotocol, pthread_mutexattr_getprotocol); +__weak_reference(_pthread_mutexattr_setprotocol, pthread_mutexattr_setprotocol); + +int +_pthread_mutexattr_getprotocol(pthread_mutexattr_t *mattr, int *protocol) +{ + int ret = 0; + + if ((mattr == NULL) || (*mattr == NULL)) + ret = EINVAL; + else + *protocol = (*mattr)->m_protocol; + + return(ret); +} + +int +_pthread_mutexattr_setprotocol(pthread_mutexattr_t *mattr, int protocol) +{ + int ret = 0; + + if ((mattr == NULL) || (*mattr == NULL) || + (protocol < PTHREAD_PRIO_NONE) || (protocol > PTHREAD_PRIO_PROTECT)) + ret = EINVAL; + else { + (*mattr)->m_protocol = protocol; + (*mattr)->m_ceiling = THR_MAX_PRIORITY; + } + return(ret); +} + diff --git a/lib/libpthread/thread/thr_mutexattr_destroy.c b/lib/libpthread/thread/thr_mutexattr_destroy.c new file mode 100644 index 0000000..2ae34a8 --- /dev/null +++ b/lib/libpthread/thread/thr_mutexattr_destroy.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 1997 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <stdlib.h> +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_mutexattr_destroy); +LT10_COMPAT_DEFAULT(pthread_mutexattr_destroy); + +__weak_reference(_pthread_mutexattr_destroy, pthread_mutexattr_destroy); + +int +_pthread_mutexattr_destroy(pthread_mutexattr_t *attr) +{ + int ret; + if (attr == NULL || *attr == NULL) { + ret = EINVAL; + } else { + free(*attr); + *attr = NULL; + ret = 0; + } + return(ret); +} diff --git a/lib/libpthread/thread/thr_nanosleep.c b/lib/libpthread/thread/thr_nanosleep.c new file mode 100644 index 0000000..72f85b2 --- /dev/null +++ b/lib/libpthread/thread/thr_nanosleep.c @@ -0,0 +1,135 @@ +/* + * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <stdio.h> +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(__nanosleep); +LT10_COMPAT_PRIVATE(_nanosleep); +LT10_COMPAT_DEFAULT(nanosleep); + +__weak_reference(__nanosleep, nanosleep); + +int +_nanosleep(const struct timespec *time_to_sleep, + struct timespec *time_remaining) +{ + struct pthread *curthread = _get_curthread(); + int ret = 0; + struct timespec ts, ts1; + struct timespec remaining_time; + struct timespec wakeup_time; + + /* Check if the time to sleep is legal: */ + if ((time_to_sleep == NULL) || (time_to_sleep->tv_sec < 0) || + (time_to_sleep->tv_nsec < 0) || + (time_to_sleep->tv_nsec >= 1000000000)) { + /* Return an EINVAL error : */ + errno = EINVAL; + ret = -1; + } else { + if (curthread->attr.flags & PTHREAD_SCOPE_SYSTEM) + return (__sys_nanosleep(time_to_sleep, time_remaining)); + + KSE_GET_TOD(curthread->kse, &ts); + + /* Calculate the time for the current thread to wake up: */ + TIMESPEC_ADD(&wakeup_time, &ts, time_to_sleep); + + THR_LOCK_SWITCH(curthread); + curthread->interrupted = 0; + curthread->wakeup_time = wakeup_time; + THR_SET_STATE(curthread, PS_SLEEP_WAIT); + + /* Reschedule the current thread to sleep: */ + _thr_sched_switch_unlocked(curthread); + + /* Calculate the remaining time to sleep: */ + KSE_GET_TOD(curthread->kse, &ts1); + remaining_time.tv_sec = time_to_sleep->tv_sec + + ts.tv_sec - ts1.tv_sec; + remaining_time.tv_nsec = time_to_sleep->tv_nsec + + ts.tv_nsec - ts1.tv_nsec; + + /* Check if the nanosecond field has underflowed: */ + if (remaining_time.tv_nsec < 0) { + /* Handle the underflow: */ + remaining_time.tv_sec -= 1; + remaining_time.tv_nsec += 1000000000; + } + /* Check if the nanosecond field has overflowed: */ + else if (remaining_time.tv_nsec >= 1000000000) { + /* Handle the overflow: */ + remaining_time.tv_sec += 1; + remaining_time.tv_nsec -= 1000000000; + } + + /* Check if the sleep was longer than the required time: */ + if (remaining_time.tv_sec < 0) { + /* Reset the time left: */ + remaining_time.tv_sec = 0; + remaining_time.tv_nsec = 0; + } + + /* Check if the time remaining is to be returned: */ + if (time_remaining != NULL) { + /* Return the actual time slept: */ + time_remaining->tv_sec = remaining_time.tv_sec; + time_remaining->tv_nsec = remaining_time.tv_nsec; + } + + /* Check if the sleep was interrupted: */ + if (curthread->interrupted) { + /* Return an EINTR error : */ + errno = EINTR; + ret = -1; + } + } + return (ret); +} + +int +__nanosleep(const struct timespec *time_to_sleep, + struct timespec *time_remaining) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_cancel_enter(curthread); + ret = _nanosleep(time_to_sleep, time_remaining); + _thr_cancel_leave(curthread, 1); + + return (ret); +} diff --git a/lib/libpthread/thread/thr_once.c b/lib/libpthread/thread/thr_once.c new file mode 100644 index 0000000..f93800f --- /dev/null +++ b/lib/libpthread/thread/thr_once.c @@ -0,0 +1,101 @@ +/* + * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include "namespace.h" +#include <pthread.h> +#include "un-namespace.h" +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_once); +LT10_COMPAT_DEFAULT(pthread_once); + +__weak_reference(_pthread_once, pthread_once); + +#define ONCE_NEVER_DONE PTHREAD_NEEDS_INIT +#define ONCE_DONE PTHREAD_DONE_INIT +#define ONCE_IN_PROGRESS 0x02 +#define ONCE_MASK 0x03 + +static pthread_mutex_t once_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t once_cv = PTHREAD_COND_INITIALIZER; + +/* + * POSIX: + * The pthread_once() function is not a cancellation point. However, + * if init_routine is a cancellation point and is canceled, the effect + * on once_control shall be as if pthread_once() was never called. + */ + +static void +once_cancel_handler(void *arg) +{ + pthread_once_t *once_control = arg; + + _pthread_mutex_lock(&once_lock); + once_control->state = ONCE_NEVER_DONE; + _pthread_mutex_unlock(&once_lock); + _pthread_cond_broadcast(&once_cv); +} + +int +_pthread_once(pthread_once_t *once_control, void (*init_routine) (void)) +{ + struct pthread *curthread; + int wakeup = 0; + + if (once_control->state == ONCE_DONE) + return (0); + _pthread_mutex_lock(&once_lock); + while (*(volatile int *)&(once_control->state) == ONCE_IN_PROGRESS) + _pthread_cond_wait(&once_cv, &once_lock); + /* + * If previous thread was canceled, then the state still + * could be ONCE_NEVER_DONE, we need to check it again. + */ + if (*(volatile int *)&(once_control->state) == ONCE_NEVER_DONE) { + once_control->state = ONCE_IN_PROGRESS; + _pthread_mutex_unlock(&once_lock); + curthread = _get_curthread(); + THR_CLEANUP_PUSH(curthread, once_cancel_handler, once_control); + init_routine(); + THR_CLEANUP_POP(curthread, 0); + _pthread_mutex_lock(&once_lock); + once_control->state = ONCE_DONE; + wakeup = 1; + } + _pthread_mutex_unlock(&once_lock); + if (wakeup) + _pthread_cond_broadcast(&once_cv); + return (0); +} + diff --git a/lib/libpthread/thread/thr_open.c b/lib/libpthread/thread/thr_open.c new file mode 100644 index 0000000..63b5f4a --- /dev/null +++ b/lib/libpthread/thread/thr_open.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + * + */ +#include <stdarg.h> +#include <unistd.h> +#include <fcntl.h> +#include <dirent.h> +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(__open); +LT10_COMPAT_DEFAULT(open); + +__weak_reference(__open, open); + +int +__open(const char *path, int flags,...) +{ + struct pthread *curthread = _get_curthread(); + int ret; + int mode = 0; + va_list ap; + + _thr_cancel_enter(curthread); + + /* Check if the file is being created: */ + if (flags & O_CREAT) { + /* Get the creation mode: */ + va_start(ap, flags); + mode = va_arg(ap, int); + va_end(ap); + } + + ret = __sys_open(path, flags, mode); + /* + * To avoid possible file handle leak, + * only check cancellation point if it is failure + */ + _thr_cancel_leave(curthread, (ret == -1)); + + return ret; +} diff --git a/lib/libpthread/thread/thr_pause.c b/lib/libpthread/thread/thr_pause.c new file mode 100644 index 0000000..b3f0fe5 --- /dev/null +++ b/lib/libpthread/thread/thr_pause.c @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>. + * 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(s), this list of conditions and the following disclaimer as + * the first lines of this file unmodified other than the possible + * addition of one or more copyright notices. + * 2. Redistributions in binary form must reproduce the above copyright + * notice(s), this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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. + * + * $FreeBSD$ + */ + +#include <unistd.h> +#include <pthread.h> +#include "thr_private.h" + +extern int __pause(void); + +LT10_COMPAT_PRIVATE(_pause); +LT10_COMPAT_DEFAULT(pause); + +__weak_reference(_pause, pause); + +int +_pause(void) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_cancel_enter(curthread); + ret = __pause(); + _thr_cancel_leave(curthread, 1); + + return ret; +} diff --git a/lib/libpthread/thread/thr_poll.c b/lib/libpthread/thread/thr_poll.c new file mode 100644 index 0000000..5e3890b --- /dev/null +++ b/lib/libpthread/thread/thr_poll.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 1999 Daniel Eischen <eischen@vigrid.com> + * 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 Daniel Eischen. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <poll.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/fcntl.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(__poll); +LT10_COMPAT_DEFAULT(poll); + +__weak_reference(__poll, poll); + +int +__poll(struct pollfd *fds, unsigned int nfds, int timeout) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_cancel_enter(curthread); + ret = __sys_poll(fds, nfds, timeout); + _thr_cancel_leave(curthread, 1); + + return ret; +} diff --git a/lib/libpthread/thread/thr_printf.c b/lib/libpthread/thread/thr_printf.c new file mode 100644 index 0000000..2a4b12b --- /dev/null +++ b/lib/libpthread/thread/thr_printf.c @@ -0,0 +1,135 @@ +/*- + * Copyright (c) 2002 Jonathan Mini <mini@freebsd.org> + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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> +__FBSDID("$FreeBSD$"); + +#include <stdarg.h> +#include <string.h> +#include <unistd.h> + +#include "thr_private.h" + +static void pchar(int fd, char c); +static void pstr(int fd, const char *s); + +/* + * Write formatted output to stdout, in a thread-safe manner. + * + * Recognises the following conversions: + * %c -> char + * %d -> signed int (base 10) + * %s -> string + * %u -> unsigned int (base 10) + * %x -> unsigned int (base 16) + * %p -> unsigned int (base 16) + */ +void +_thread_printf(int fd, const char *fmt, ...) +{ + static const char digits[16] = "0123456789abcdef"; + va_list ap; + char buf[20]; + char *s; + unsigned long r, u; + int c; + long d; + int islong; + + va_start(ap, fmt); + while ((c = *fmt++)) { + islong = 0; + if (c == '%') { +next: c = *fmt++; + if (c == '\0') + goto out; + switch (c) { + case 'c': + pchar(fd, va_arg(ap, int)); + continue; + case 's': + pstr(fd, va_arg(ap, char *)); + continue; + case 'l': + islong = 1; + goto next; + case 'p': + islong = 1; + case 'd': + case 'u': + case 'x': + r = ((c == 'u') || (c == 'd')) ? 10 : 16; + if (c == 'd') { + if (islong) + d = va_arg(ap, unsigned long); + else + d = va_arg(ap, unsigned); + if (d < 0) { + pchar(fd, '-'); + u = (unsigned long)(d * -1); + } else + u = (unsigned long)d; + } else { + if (islong) + u = va_arg(ap, unsigned long); + else + u = va_arg(ap, unsigned); + } + s = buf; + do { + *s++ = digits[u % r]; + } while (u /= r); + while (--s >= buf) + pchar(fd, *s); + continue; + } + } + pchar(fd, c); + } +out: + va_end(ap); +} + +/* + * Write a single character to stdout, in a thread-safe manner. + */ +static void +pchar(int fd, char c) +{ + + __sys_write(fd, &c, 1); +} + +/* + * Write a string to stdout, in a thread-safe manner. + */ +static void +pstr(int fd, const char *s) +{ + + __sys_write(fd, s, strlen(s)); +} + diff --git a/lib/libpthread/thread/thr_priority_queue.c b/lib/libpthread/thread/thr_priority_queue.c new file mode 100644 index 0000000..f750a01 --- /dev/null +++ b/lib/libpthread/thread/thr_priority_queue.c @@ -0,0 +1,324 @@ +/* + * Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>. + * 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 Daniel Eischen. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <stdlib.h> +#include <sys/queue.h> +#include <string.h> +#include <pthread.h> +#include "thr_private.h" + +/* Prototypes: */ +static void pq_insert_prio_list(pq_queue_t *pq, int prio); + +#if defined(_PTHREADS_INVARIANTS) + +#define PQ_IN_SCHEDQ (THR_FLAGS_IN_RUNQ | THR_FLAGS_IN_WAITQ) + +#define PQ_SET_ACTIVE(pq) (pq)->pq_flags |= PQF_ACTIVE +#define PQ_CLEAR_ACTIVE(pq) (pq)->pq_flags &= ~PQF_ACTIVE +#define PQ_ASSERT_ACTIVE(pq, msg) do { \ + if (((pq)->pq_flags & PQF_ACTIVE) == 0) \ + PANIC(msg); \ +} while (0) +#define PQ_ASSERT_INACTIVE(pq, msg) do { \ + if (((pq)->pq_flags & PQF_ACTIVE) != 0) \ + PANIC(msg); \ +} while (0) +#define PQ_ASSERT_IN_WAITQ(thrd, msg) do { \ + if (((thrd)->flags & THR_FLAGS_IN_WAITQ) == 0) \ + PANIC(msg); \ +} while (0) +#define PQ_ASSERT_IN_RUNQ(thrd, msg) do { \ + if (((thrd)->flags & THR_FLAGS_IN_RUNQ) == 0) \ + PANIC(msg); \ +} while (0) +#define PQ_ASSERT_NOT_QUEUED(thrd, msg) do { \ + if (((thrd)->flags & PQ_IN_SCHEDQ) != 0) \ + PANIC(msg); \ +} while (0) + +#else + +#define PQ_SET_ACTIVE(pq) +#define PQ_CLEAR_ACTIVE(pq) +#define PQ_ASSERT_ACTIVE(pq, msg) +#define PQ_ASSERT_INACTIVE(pq, msg) +#define PQ_ASSERT_IN_WAITQ(thrd, msg) +#define PQ_ASSERT_IN_RUNQ(thrd, msg) +#define PQ_ASSERT_NOT_QUEUED(thrd, msg) + +#endif + +int +_pq_alloc(pq_queue_t *pq, int minprio, int maxprio) +{ + int ret = 0; + int prioslots = maxprio - minprio + 1; + + if (pq == NULL) + ret = -1; + + /* Create the priority queue with (maxprio - minprio + 1) slots: */ + else if ((pq->pq_lists = + (pq_list_t *) malloc(sizeof(pq_list_t) * prioslots)) == NULL) + ret = -1; + + else { + /* Remember the queue size: */ + pq->pq_size = prioslots; + ret = _pq_init(pq); + } + return (ret); +} + +void +_pq_free(pq_queue_t *pq) +{ + if ((pq != NULL) && (pq->pq_lists != NULL)) + free(pq->pq_lists); +} + +int +_pq_init(pq_queue_t *pq) +{ + int i, ret = 0; + + if ((pq == NULL) || (pq->pq_lists == NULL)) + ret = -1; + + else { + /* Initialize the queue for each priority slot: */ + for (i = 0; i < pq->pq_size; i++) { + TAILQ_INIT(&pq->pq_lists[i].pl_head); + pq->pq_lists[i].pl_prio = i; + pq->pq_lists[i].pl_queued = 0; + } + /* Initialize the priority queue: */ + TAILQ_INIT(&pq->pq_queue); + pq->pq_flags = 0; + pq->pq_threads = 0; + } + return (ret); +} + +void +_pq_remove(pq_queue_t *pq, pthread_t pthread) +{ + int prio = pthread->active_priority; + + /* + * Make some assertions when debugging is enabled: + */ + PQ_ASSERT_INACTIVE(pq, "_pq_remove: pq_active"); + PQ_SET_ACTIVE(pq); + PQ_ASSERT_IN_RUNQ(pthread, "_pq_remove: Not in priority queue"); + + /* + * Remove this thread from priority list. Note that if + * the priority list becomes empty, it is not removed + * from the priority queue because another thread may be + * added to the priority list (resulting in a needless + * removal/insertion). Priority lists are only removed + * from the priority queue when _pq_first is called. + */ + TAILQ_REMOVE(&pq->pq_lists[prio].pl_head, pthread, pqe); + pq->pq_threads--; + /* This thread is now longer in the priority queue. */ + pthread->flags &= ~THR_FLAGS_IN_RUNQ; + + PQ_CLEAR_ACTIVE(pq); +} + + +void +_pq_insert_head(pq_queue_t *pq, pthread_t pthread) +{ + int prio; + + /* + * Make some assertions when debugging is enabled: + */ + PQ_ASSERT_INACTIVE(pq, "_pq_insert_head: pq_active"); + PQ_SET_ACTIVE(pq); + PQ_ASSERT_NOT_QUEUED(pthread, + "_pq_insert_head: Already in priority queue"); + + prio = pthread->active_priority; + TAILQ_INSERT_HEAD(&pq->pq_lists[prio].pl_head, pthread, pqe); + if (pq->pq_lists[prio].pl_queued == 0) + /* Insert the list into the priority queue: */ + pq_insert_prio_list(pq, prio); + pq->pq_threads++; + /* Mark this thread as being in the priority queue. */ + pthread->flags |= THR_FLAGS_IN_RUNQ; + + PQ_CLEAR_ACTIVE(pq); +} + + +void +_pq_insert_tail(pq_queue_t *pq, pthread_t pthread) +{ + int prio; + + /* + * Make some assertions when debugging is enabled: + */ + PQ_ASSERT_INACTIVE(pq, "_pq_insert_tail: pq_active"); + PQ_SET_ACTIVE(pq); + PQ_ASSERT_NOT_QUEUED(pthread, + "_pq_insert_tail: Already in priority queue"); + + prio = pthread->active_priority; + TAILQ_INSERT_TAIL(&pq->pq_lists[prio].pl_head, pthread, pqe); + if (pq->pq_lists[prio].pl_queued == 0) + /* Insert the list into the priority queue: */ + pq_insert_prio_list(pq, prio); + pq->pq_threads++; + /* Mark this thread as being in the priority queue. */ + pthread->flags |= THR_FLAGS_IN_RUNQ; + + PQ_CLEAR_ACTIVE(pq); +} + + +pthread_t +_pq_first(pq_queue_t *pq) +{ + pq_list_t *pql; + pthread_t pthread = NULL; + + /* + * Make some assertions when debugging is enabled: + */ + PQ_ASSERT_INACTIVE(pq, "_pq_first: pq_active"); + PQ_SET_ACTIVE(pq); + + while (((pql = TAILQ_FIRST(&pq->pq_queue)) != NULL) && + (pthread == NULL)) { + if ((pthread = TAILQ_FIRST(&pql->pl_head)) == NULL) { + /* + * The priority list is empty; remove the list + * from the queue. + */ + TAILQ_REMOVE(&pq->pq_queue, pql, pl_link); + + /* Mark the list as not being in the queue: */ + pql->pl_queued = 0; + } + } + + PQ_CLEAR_ACTIVE(pq); + return (pthread); +} + +/* + * Select a thread which is allowed to run by debugger, we probably + * should merge the function into _pq_first if that function is only + * used by scheduler to select a thread. + */ +pthread_t +_pq_first_debug(pq_queue_t *pq) +{ + pq_list_t *pql, *pqlnext = NULL; + pthread_t pthread = NULL; + + /* + * Make some assertions when debugging is enabled: + */ + PQ_ASSERT_INACTIVE(pq, "_pq_first: pq_active"); + PQ_SET_ACTIVE(pq); + + for (pql = TAILQ_FIRST(&pq->pq_queue); + pql != NULL && pthread == NULL; pql = pqlnext) { + if ((pthread = TAILQ_FIRST(&pql->pl_head)) == NULL) { + /* + * The priority list is empty; remove the list + * from the queue. + */ + pqlnext = TAILQ_NEXT(pql, pl_link); + TAILQ_REMOVE(&pq->pq_queue, pql, pl_link); + + /* Mark the list as not being in the queue: */ + pql->pl_queued = 0; + } else { + /* + * note there may be a suspension event during this + * test, If TMDF_SUSPEND is set after we tested it, + * we will run the thread, this seems be a problem, + * fortunatly, when we are being debugged, all context + * switch will be done by kse_switchin, that is a + * syscall, kse_switchin will check the flag again, + * the thread will be returned via upcall, so next + * time, UTS won't run the thread. + */ + while (pthread != NULL && !DBG_CAN_RUN(pthread)) { + pthread = TAILQ_NEXT(pthread, pqe); + } + if (pthread == NULL) + pqlnext = TAILQ_NEXT(pql, pl_link); + } + } + + PQ_CLEAR_ACTIVE(pq); + return (pthread); +} + +static void +pq_insert_prio_list(pq_queue_t *pq, int prio) +{ + pq_list_t *pql; + + /* + * Make some assertions when debugging is enabled: + */ + PQ_ASSERT_ACTIVE(pq, "pq_insert_prio_list: pq_active"); + + /* + * The priority queue is in descending priority order. Start at + * the beginning of the queue and find the list before which the + * new list should be inserted. + */ + pql = TAILQ_FIRST(&pq->pq_queue); + while ((pql != NULL) && (pql->pl_prio > prio)) + pql = TAILQ_NEXT(pql, pl_link); + + /* Insert the list: */ + if (pql == NULL) + TAILQ_INSERT_TAIL(&pq->pq_queue, &pq->pq_lists[prio], pl_link); + else + TAILQ_INSERT_BEFORE(pql, &pq->pq_lists[prio], pl_link); + + /* Mark this list as being in the queue: */ + pq->pq_lists[prio].pl_queued = 1; +} diff --git a/lib/libpthread/thread/thr_private.h b/lib/libpthread/thread/thr_private.h new file mode 100644 index 0000000..f839960 --- /dev/null +++ b/lib/libpthread/thread/thr_private.h @@ -0,0 +1,1320 @@ +/* + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * Private thread definitions for the uthread kernel. + * + * $FreeBSD$ + */ + +#ifndef _THR_PRIVATE_H +#define _THR_PRIVATE_H + +/* + * Include files. + */ +#include <setjmp.h> +#include <signal.h> +#include <stdio.h> +#include <sys/queue.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/cdefs.h> +#include <sys/kse.h> +#include <sched.h> +#include <ucontext.h> +#include <unistd.h> +#include <pthread.h> +#include <pthread_np.h> + +#ifndef LIBTHREAD_DB +#include "lock.h" +#include "pthread_md.h" +#endif + +/* + * Unfortunately, libpthread had symbol versioning before libc. + * But now libc has symbol versioning, we need to occupy the + * same version namespace in order to override some libc functions. + * So in order to avoid breaking binaries requiring symbols from + * LIBTHREAD_1_0, we need to provide a compatible interface for + * those symbols. + */ +#if defined(SYMBOL_VERSIONING) && defined(PIC) +#define SYM_LT10(sym) __CONCAT(sym, _lt10) +#define SYM_FB10(sym) __CONCAT(sym, _fb10) +#define SYM_FBP10(sym) __CONCAT(sym, _fbp10) +#define WEAK_REF(sym, alias) __weak_reference(sym, alias) +#define SYM_COMPAT(sym, impl, ver) __sym_compat(sym, impl, ver) +#define SYM_DEFAULT(sym, impl, ver) __sym_default(sym, impl, ver) + +#define LT10_COMPAT(sym) \ + WEAK_REF(sym, SYM_LT10(sym)); \ + SYM_COMPAT(sym, SYM_LT10(sym), LIBTHREAD_1_0) + +#define LT10_COMPAT_DEFAULT(sym) \ + LT10_COMPAT(sym); \ + WEAK_REF(sym, SYM_FB10(sym)); \ + SYM_DEFAULT(sym, SYM_FB10(sym), FBSD_1.0) + +#define LT10_COMPAT_PRIVATE(sym) \ + LT10_COMPAT(sym); \ + WEAK_REF(sym, SYM_FBP10(sym)); \ + SYM_DEFAULT(sym, SYM_FBP10(sym), FBSDprivate) +#else +#define LT10_COMPAT_DEFAULT(sym) +#define LT10_COMPAT_PRIVATE(sym) +#endif + +/* + * Evaluate the storage class specifier. + */ +#ifdef GLOBAL_PTHREAD_PRIVATE +#define SCLASS +#define SCLASS_PRESET(x...) = x +#else +#define SCLASS extern +#define SCLASS_PRESET(x...) +#endif + +/* + * Kernel fatal error handler macro. + */ +#define PANIC(string) _thr_exit(__FILE__, __LINE__, string) + + +/* Output debug messages like this: */ +#ifdef STDOUT_FILENO +#define stdout_debug(...) _thread_printf(STDOUT_FILENO, __VA_ARGS__) +#endif +#ifdef STDERR_FILENO +#define stderr_debug(...) _thread_printf(STDERR_FILENO, __VA_ARGS__) +#endif + +#define DBG_MUTEX 0x0001 +#define DBG_SIG 0x0002 +#define DBG_INFO_DUMP 0x0004 + +#ifdef _PTHREADS_INVARIANTS +#define THR_ASSERT(cond, msg) do { \ + if (!(cond)) \ + PANIC(msg); \ +} while (0) +#else +#define THR_ASSERT(cond, msg) +#endif + +/* + * State change macro without scheduling queue change: + */ +#define THR_SET_STATE(thrd, newstate) do { \ + (thrd)->state = newstate; \ + (thrd)->fname = __FILE__; \ + (thrd)->lineno = __LINE__; \ +} while (0) + + +#define TIMESPEC_ADD(dst, src, val) \ + do { \ + (dst)->tv_sec = (src)->tv_sec + (val)->tv_sec; \ + (dst)->tv_nsec = (src)->tv_nsec + (val)->tv_nsec; \ + if ((dst)->tv_nsec >= 1000000000) { \ + (dst)->tv_sec++; \ + (dst)->tv_nsec -= 1000000000; \ + } \ + } while (0) + +#define TIMESPEC_SUB(dst, src, val) \ + do { \ + (dst)->tv_sec = (src)->tv_sec - (val)->tv_sec; \ + (dst)->tv_nsec = (src)->tv_nsec - (val)->tv_nsec; \ + if ((dst)->tv_nsec < 0) { \ + (dst)->tv_sec--; \ + (dst)->tv_nsec += 1000000000; \ + } \ + } while (0) + +/* + * Priority queues. + * + * XXX It'd be nice if these were contained in uthread_priority_queue.[ch]. + */ +typedef struct pq_list { + TAILQ_HEAD(, pthread) pl_head; /* list of threads at this priority */ + TAILQ_ENTRY(pq_list) pl_link; /* link for queue of priority lists */ + int pl_prio; /* the priority of this list */ + int pl_queued; /* is this in the priority queue */ +} pq_list_t; + +typedef struct pq_queue { + TAILQ_HEAD(, pq_list) pq_queue; /* queue of priority lists */ + pq_list_t *pq_lists; /* array of all priority lists */ + int pq_size; /* number of priority lists */ +#define PQF_ACTIVE 0x0001 + int pq_flags; + int pq_threads; +} pq_queue_t; + +/* + * Each KSEG has a scheduling queue. For now, threads that exist in their + * own KSEG (system scope) will get a full priority queue. In the future + * this can be optimized for the single thread per KSEG case. + */ +struct sched_queue { + pq_queue_t sq_runq; + TAILQ_HEAD(, pthread) sq_waitq; /* waiting in userland */ +}; + +typedef struct kse_thr_mailbox *kse_critical_t; + +struct kse_group; + +#define MAX_KSE_LOCKLEVEL 5 +struct kse { + /* -- location and order specific items for gdb -- */ + struct kcb *k_kcb; + struct pthread *k_curthread; /* current thread */ + struct kse_group *k_kseg; /* parent KSEG */ + struct sched_queue *k_schedq; /* scheduling queue */ + /* -- end of location and order specific items -- */ + TAILQ_ENTRY(kse) k_qe; /* KSE list link entry */ + TAILQ_ENTRY(kse) k_kgqe; /* KSEG's KSE list entry */ + /* + * Items that are only modified by the kse, or that otherwise + * don't need to be locked when accessed + */ + struct lock k_lock; + struct lockuser k_lockusers[MAX_KSE_LOCKLEVEL]; + int k_locklevel; + stack_t k_stack; + int k_flags; +#define KF_STARTED 0x0001 /* kernel kse created */ +#define KF_INITIALIZED 0x0002 /* initialized on 1st upcall */ +#define KF_TERMINATED 0x0004 /* kse is terminated */ +#define KF_IDLE 0x0008 /* kse is idle */ +#define KF_SWITCH 0x0010 /* thread switch in UTS */ + int k_error; /* syscall errno in critical */ + int k_cpu; /* CPU ID when bound */ + int k_sigseqno; /* signal buffered count */ +}; + +#define KSE_SET_IDLE(kse) ((kse)->k_flags |= KF_IDLE) +#define KSE_CLEAR_IDLE(kse) ((kse)->k_flags &= ~KF_IDLE) +#define KSE_IS_IDLE(kse) (((kse)->k_flags & KF_IDLE) != 0) +#define KSE_SET_SWITCH(kse) ((kse)->k_flags |= KF_SWITCH) +#define KSE_CLEAR_SWITCH(kse) ((kse)->k_flags &= ~KF_SWITCH) +#define KSE_IS_SWITCH(kse) (((kse)->k_flags & KF_SWITCH) != 0) + +/* + * Each KSE group contains one or more KSEs in which threads can run. + * At least for now, there is one scheduling queue per KSE group; KSEs + * within the same KSE group compete for threads from the same scheduling + * queue. A scope system thread has one KSE in one KSE group; the group + * does not use its scheduling queue. + */ +struct kse_group { + TAILQ_HEAD(, kse) kg_kseq; /* list of KSEs in group */ + TAILQ_HEAD(, pthread) kg_threadq; /* list of threads in group */ + TAILQ_ENTRY(kse_group) kg_qe; /* link entry */ + struct sched_queue kg_schedq; /* scheduling queue */ + struct lock kg_lock; + int kg_threadcount; /* # of assigned threads */ + int kg_ksecount; /* # of assigned KSEs */ + int kg_idle_kses; + int kg_flags; +#define KGF_SINGLE_THREAD 0x0001 /* scope system kse group */ +#define KGF_SCHEDQ_INITED 0x0002 /* has an initialized schedq */ +}; + +/* + * Add/remove threads from a KSE's scheduling queue. + * For now the scheduling queue is hung off the KSEG. + */ +#define KSEG_THRQ_ADD(kseg, thr) \ +do { \ + TAILQ_INSERT_TAIL(&(kseg)->kg_threadq, thr, kle);\ + (kseg)->kg_threadcount++; \ +} while (0) + +#define KSEG_THRQ_REMOVE(kseg, thr) \ +do { \ + TAILQ_REMOVE(&(kseg)->kg_threadq, thr, kle); \ + (kseg)->kg_threadcount--; \ +} while (0) + + +/* + * Lock acquire and release for KSEs. + */ +#define KSE_LOCK_ACQUIRE(kse, lck) \ +do { \ + if ((kse)->k_locklevel < MAX_KSE_LOCKLEVEL) { \ + (kse)->k_locklevel++; \ + _lock_acquire((lck), \ + &(kse)->k_lockusers[(kse)->k_locklevel - 1], 0); \ + } \ + else \ + PANIC("Exceeded maximum lock level"); \ +} while (0) + +#define KSE_LOCK_RELEASE(kse, lck) \ +do { \ + if ((kse)->k_locklevel > 0) { \ + _lock_release((lck), \ + &(kse)->k_lockusers[(kse)->k_locklevel - 1]); \ + (kse)->k_locklevel--; \ + } \ +} while (0) + +/* + * Lock our own KSEG. + */ +#define KSE_LOCK(curkse) \ + KSE_LOCK_ACQUIRE(curkse, &(curkse)->k_kseg->kg_lock) +#define KSE_UNLOCK(curkse) \ + KSE_LOCK_RELEASE(curkse, &(curkse)->k_kseg->kg_lock) + +/* + * Lock a potentially different KSEG. + */ +#define KSE_SCHED_LOCK(curkse, kseg) \ + KSE_LOCK_ACQUIRE(curkse, &(kseg)->kg_lock) +#define KSE_SCHED_UNLOCK(curkse, kseg) \ + KSE_LOCK_RELEASE(curkse, &(kseg)->kg_lock) + +/* + * Waiting queue manipulation macros (using pqe link): + */ +#define KSE_WAITQ_REMOVE(kse, thrd) \ +do { \ + if (((thrd)->flags & THR_FLAGS_IN_WAITQ) != 0) { \ + TAILQ_REMOVE(&(kse)->k_schedq->sq_waitq, thrd, pqe); \ + (thrd)->flags &= ~THR_FLAGS_IN_WAITQ; \ + } \ +} while (0) +#define KSE_WAITQ_INSERT(kse, thrd) kse_waitq_insert(thrd) +#define KSE_WAITQ_FIRST(kse) TAILQ_FIRST(&(kse)->k_schedq->sq_waitq) + +#define KSE_WAKEUP(kse) kse_wakeup(&(kse)->k_kcb->kcb_kmbx) + +/* + * TailQ initialization values. + */ +#define TAILQ_INITIALIZER { NULL, NULL } + +/* + * lock initialization values. + */ +#define LCK_INITIALIZER { NULL, NULL, LCK_DEFAULT } + +struct pthread_mutex { + /* + * Lock for accesses to this structure. + */ + struct lock m_lock; + enum pthread_mutextype m_type; + int m_protocol; + TAILQ_HEAD(mutex_head, pthread) m_queue; + struct pthread *m_owner; + long m_flags; + int m_count; + int m_refcount; + + /* + * Used for priority inheritence and protection. + * + * m_prio - For priority inheritence, the highest active + * priority (threads locking the mutex inherit + * this priority). For priority protection, the + * ceiling priority of this mutex. + * m_saved_prio - mutex owners inherited priority before + * taking the mutex, restored when the owner + * unlocks the mutex. + */ + int m_prio; + int m_saved_prio; + + /* + * Link for list of all mutexes a thread currently owns. + */ + TAILQ_ENTRY(pthread_mutex) m_qe; +}; + +/* + * Flags for mutexes. + */ +#define MUTEX_FLAGS_PRIVATE 0x01 +#define MUTEX_FLAGS_INITED 0x02 +#define MUTEX_FLAGS_BUSY 0x04 + +/* + * Static mutex initialization values. + */ +#define PTHREAD_MUTEX_STATIC_INITIALIZER \ + { LCK_INITIALIZER, PTHREAD_MUTEX_DEFAULT, PTHREAD_PRIO_NONE, \ + TAILQ_INITIALIZER, NULL, MUTEX_FLAGS_PRIVATE, 0, 0, 0, 0, \ + TAILQ_INITIALIZER } + +struct pthread_mutex_attr { + enum pthread_mutextype m_type; + int m_protocol; + int m_ceiling; + long m_flags; +}; + +#define PTHREAD_MUTEXATTR_STATIC_INITIALIZER \ + { PTHREAD_MUTEX_DEFAULT, PTHREAD_PRIO_NONE, 0, MUTEX_FLAGS_PRIVATE } + +/* + * Condition variable definitions. + */ +enum pthread_cond_type { + COND_TYPE_FAST, + COND_TYPE_MAX +}; + +struct pthread_cond { + /* + * Lock for accesses to this structure. + */ + struct lock c_lock; + enum pthread_cond_type c_type; + TAILQ_HEAD(cond_head, pthread) c_queue; + struct pthread_mutex *c_mutex; + long c_flags; + long c_seqno; +}; + +struct pthread_cond_attr { + enum pthread_cond_type c_type; + long c_flags; +}; + +struct pthread_barrier { + pthread_mutex_t b_lock; + pthread_cond_t b_cond; + int b_count; + int b_waiters; + int b_generation; +}; + +struct pthread_barrierattr { + int pshared; +}; + +struct pthread_spinlock { + volatile int s_lock; + pthread_t s_owner; +}; + +/* + * Flags for condition variables. + */ +#define COND_FLAGS_PRIVATE 0x01 +#define COND_FLAGS_INITED 0x02 +#define COND_FLAGS_BUSY 0x04 + +/* + * Static cond initialization values. + */ +#define PTHREAD_COND_STATIC_INITIALIZER \ + { LCK_INITIALIZER, COND_TYPE_FAST, TAILQ_INITIALIZER, \ + NULL, NULL, 0, 0 } + +/* + * Cleanup definitions. + */ +struct pthread_cleanup { + struct pthread_cleanup *next; + void (*routine) (void *); + void *routine_arg; + int onstack; +}; + +#define THR_CLEANUP_PUSH(td, func, arg) { \ + struct pthread_cleanup __cup; \ + \ + __cup.routine = func; \ + __cup.routine_arg = arg; \ + __cup.onstack = 1; \ + __cup.next = (td)->cleanup; \ + (td)->cleanup = &__cup; + +#define THR_CLEANUP_POP(td, exec) \ + (td)->cleanup = __cup.next; \ + if ((exec) != 0) \ + __cup.routine(__cup.routine_arg); \ +} + +struct pthread_atfork { + TAILQ_ENTRY(pthread_atfork) qe; + void (*prepare)(void); + void (*parent)(void); + void (*child)(void); +}; + +struct pthread_attr { + int sched_policy; + int sched_inherit; + int sched_interval; + int prio; + int suspend; +#define THR_STACK_USER 0x100 /* 0xFF reserved for <pthread.h> */ +#define THR_SIGNAL_THREAD 0x200 /* This is a signal thread */ + int flags; + void *arg_attr; + void (*cleanup_attr) (void *); + void *stackaddr_attr; + size_t stacksize_attr; + size_t guardsize_attr; +}; + +/* + * Thread creation state attributes. + */ +#define THR_CREATE_RUNNING 0 +#define THR_CREATE_SUSPENDED 1 + +/* + * Miscellaneous definitions. + */ +#define THR_STACK32_DEFAULT (1 * 1024 * 1024) +#define THR_STACK64_DEFAULT (2 * 1024 * 1024) + +/* + * Maximum size of initial thread's stack. This perhaps deserves to be larger + * than the stacks of other threads, since many applications are likely to run + * almost entirely on this stack. + */ +#define THR_STACK32_INITIAL (2 * 1024 * 1024) +#define THR_STACK64_INITIAL (4 * 1024 * 1024) + +/* + * Define the different priority ranges. All applications have thread + * priorities constrained within 0-31. The threads library raises the + * priority when delivering signals in order to ensure that signal + * delivery happens (from the POSIX spec) "as soon as possible". + * In the future, the threads library will also be able to map specific + * threads into real-time (cooperating) processes or kernel threads. + * The RT and SIGNAL priorities will be used internally and added to + * thread base priorities so that the scheduling queue can handle both + * normal and RT priority threads with and without signal handling. + * + * The approach taken is that, within each class, signal delivery + * always has priority over thread execution. + */ +#define THR_DEFAULT_PRIORITY 15 +#define THR_MIN_PRIORITY 0 +#define THR_MAX_PRIORITY 31 /* 0x1F */ +#define THR_SIGNAL_PRIORITY 32 /* 0x20 */ +#define THR_RT_PRIORITY 64 /* 0x40 */ +#define THR_FIRST_PRIORITY THR_MIN_PRIORITY +#define THR_LAST_PRIORITY \ + (THR_MAX_PRIORITY + THR_SIGNAL_PRIORITY + THR_RT_PRIORITY) +#define THR_BASE_PRIORITY(prio) ((prio) & THR_MAX_PRIORITY) + +/* + * Clock resolution in microseconds. + */ +#define CLOCK_RES_USEC 10000 + +/* + * Time slice period in microseconds. + */ +#define TIMESLICE_USEC 20000 + +/* + * XXX - Define a thread-safe macro to get the current time of day + * which is updated at regular intervals by something. + * + * For now, we just make the system call to get the time. + */ +#define KSE_GET_TOD(curkse, tsp) \ +do { \ + *tsp = (curkse)->k_kcb->kcb_kmbx.km_timeofday; \ + if ((tsp)->tv_sec == 0) \ + clock_gettime(CLOCK_REALTIME, tsp); \ +} while (0) + +struct pthread_rwlockattr { + int pshared; +}; + +struct pthread_rwlock { + pthread_mutex_t lock; /* monitor lock */ + pthread_cond_t read_signal; + pthread_cond_t write_signal; + int state; /* 0 = idle >0 = # of readers -1 = writer */ + int blocked_writers; +}; + +/* + * Thread states. + */ +enum pthread_state { + PS_RUNNING, + PS_LOCKWAIT, + PS_MUTEX_WAIT, + PS_COND_WAIT, + PS_SLEEP_WAIT, + PS_SIGSUSPEND, + PS_SIGWAIT, + PS_JOIN, + PS_SUSPENDED, + PS_DEAD, + PS_DEADLOCK, + PS_STATE_MAX +}; + +struct sigwait_data { + sigset_t *waitset; + siginfo_t *siginfo; /* used to save siginfo for sigwaitinfo() */ +}; + +union pthread_wait_data { + pthread_mutex_t mutex; + pthread_cond_t cond; + struct lock *lock; + struct sigwait_data *sigwait; +}; + +/* + * Define a continuation routine that can be used to perform a + * transfer of control: + */ +typedef void (*thread_continuation_t) (void *); + +/* + * This stores a thread's state prior to running a signal handler. + * It is used when a signal is delivered to a thread blocked in + * userland. If the signal handler returns normally, the thread's + * state is restored from here. + */ +struct pthread_sigframe { + int psf_valid; + int psf_flags; + int psf_cancelflags; + int psf_interrupted; + int psf_timeout; + int psf_signo; + enum pthread_state psf_state; + union pthread_wait_data psf_wait_data; + struct timespec psf_wakeup_time; + sigset_t psf_sigset; + sigset_t psf_sigmask; + int psf_seqno; + thread_continuation_t psf_continuation; +}; + +struct join_status { + struct pthread *thread; + void *ret; + int error; +}; + +struct pthread_specific_elem { + const void *data; + int seqno; +}; + +struct pthread_key { + volatile int allocated; + volatile int count; + int seqno; + void (*destructor) (void *); +}; + +#define MAX_THR_LOCKLEVEL 5 +/* + * Thread structure. + */ +struct pthread { + /* Thread control block */ + struct tcb *tcb; + + /* + * Magic value to help recognize a valid thread structure + * from an invalid one: + */ +#define THR_MAGIC ((u_int32_t) 0xd09ba115) + u_int32_t magic; + char *name; + u_int64_t uniqueid; /* for gdb */ + + /* Queue entry for list of all threads: */ + TAILQ_ENTRY(pthread) tle; /* link for all threads in process */ + TAILQ_ENTRY(pthread) kle; /* link for all threads in KSE/KSEG */ + + /* Queue entry for GC lists: */ + TAILQ_ENTRY(pthread) gcle; + + /* Hash queue entry */ + LIST_ENTRY(pthread) hle; + + /* + * Lock for accesses to this thread structure. + */ + struct lock lock; + struct lockuser lockusers[MAX_THR_LOCKLEVEL]; + int locklevel; + kse_critical_t critical[MAX_KSE_LOCKLEVEL]; + struct kse *kse; + struct kse_group *kseg; + + /* + * Thread start routine, argument, stack pointer and thread + * attributes. + */ + void *(*start_routine)(void *); + void *arg; + struct pthread_attr attr; + + int active; /* thread running */ + int blocked; /* thread blocked in kernel */ + int need_switchout; + + /* + * Used for tracking delivery of signal handlers. + */ + siginfo_t *siginfo; + thread_continuation_t sigbackout; + + /* + * Cancelability flags - the lower 2 bits are used by cancel + * definitions in pthread.h + */ +#define THR_AT_CANCEL_POINT 0x0004 +#define THR_CANCELLING 0x0008 +#define THR_CANCEL_NEEDED 0x0010 + int cancelflags; + + thread_continuation_t continuation; + + /* + * The thread's base and pending signal masks. The active + * signal mask is stored in the thread's context (in mailbox). + */ + sigset_t sigmask; + sigset_t sigpend; + sigset_t *oldsigmask; + volatile int check_pending; + int refcount; + + /* Thread state: */ + enum pthread_state state; + volatile int lock_switch; + + /* + * Number of microseconds accumulated by this thread when + * time slicing is active. + */ + long slice_usec; + + /* + * Time to wake up thread. This is used for sleeping threads and + * for any operation which may time out (such as select). + */ + struct timespec wakeup_time; + + /* TRUE if operation has timed out. */ + int timeout; + + /* + * Error variable used instead of errno. The function __error() + * returns a pointer to this. + */ + int error; + + /* + * The joiner is the thread that is joining to this thread. The + * join status keeps track of a join operation to another thread. + */ + struct pthread *joiner; + struct join_status join_status; + + /* + * The current thread can belong to only one scheduling queue at + * a time (ready or waiting queue). It can also belong to: + * + * o A queue of threads waiting for a mutex + * o A queue of threads waiting for a condition variable + * + * It is possible for a thread to belong to more than one of the + * above queues if it is handling a signal. A thread may only + * enter a mutex or condition variable queue when it is not + * being called from a signal handler. If a thread is a member + * of one of these queues when a signal handler is invoked, it + * must be removed from the queue before invoking the handler + * and then added back to the queue after return from the handler. + * + * Use pqe for the scheduling queue link (both ready and waiting), + * sqe for synchronization (mutex, condition variable, and join) + * queue links, and qe for all other links. + */ + TAILQ_ENTRY(pthread) pqe; /* priority, wait queues link */ + TAILQ_ENTRY(pthread) sqe; /* synchronization queue link */ + + /* Wait data. */ + union pthread_wait_data data; + + /* + * Set to TRUE if a blocking operation was + * interrupted by a signal: + */ + int interrupted; + + /* + * Set to non-zero when this thread has entered a critical + * region. We allow for recursive entries into critical regions. + */ + int critical_count; + + /* + * Set to TRUE if this thread should yield after leaving a + * critical region to check for signals, messages, etc. + */ + int critical_yield; + + int sflags; +#define THR_FLAGS_IN_SYNCQ 0x0001 + + /* Miscellaneous flags; only set with scheduling lock held. */ + int flags; +#define THR_FLAGS_PRIVATE 0x0001 +#define THR_FLAGS_IN_WAITQ 0x0002 /* in waiting queue using pqe link */ +#define THR_FLAGS_IN_RUNQ 0x0004 /* in run queue using pqe link */ +#define THR_FLAGS_EXITING 0x0008 /* thread is exiting */ +#define THR_FLAGS_SUSPENDED 0x0010 /* thread is suspended */ + + /* Thread list flags; only set with thread list lock held. */ +#define TLFLAGS_GC_SAFE 0x0001 /* thread safe for cleaning */ +#define TLFLAGS_IN_TDLIST 0x0002 /* thread in all thread list */ +#define TLFLAGS_IN_GCLIST 0x0004 /* thread in gc list */ + int tlflags; + + /* + * Base priority is the user setable and retrievable priority + * of the thread. It is only affected by explicit calls to + * set thread priority and upon thread creation via a thread + * attribute or default priority. + */ + char base_priority; + + /* + * Inherited priority is the priority a thread inherits by + * taking a priority inheritence or protection mutex. It + * is not affected by base priority changes. Inherited + * priority defaults to and remains 0 until a mutex is taken + * that is being waited on by any other thread whose priority + * is non-zero. + */ + char inherited_priority; + + /* + * Active priority is always the maximum of the threads base + * priority and inherited priority. When there is a change + * in either the base or inherited priority, the active + * priority must be recalculated. + */ + char active_priority; + + /* Number of priority ceiling or protection mutexes owned. */ + int priority_mutex_count; + + /* Number rwlocks rdlocks held. */ + int rdlock_count; + + /* + * Queue of currently owned mutexes. + */ + TAILQ_HEAD(, pthread_mutex) mutexq; + + void *ret; + struct pthread_specific_elem *specific; + int specific_data_count; + + /* Alternative stack for sigaltstack() */ + stack_t sigstk; + + /* + * Current locks bitmap for rtld. + */ + int rtld_bits; + + /* Cleanup handlers Link List */ + struct pthread_cleanup *cleanup; + char *fname; /* Ptr to source file name */ + int lineno; /* Source line number. */ +}; + +/* + * Critical regions can also be detected by looking at the threads + * current lock level. Ensure these macros increment and decrement + * the lock levels such that locks can not be held with a lock level + * of 0. + */ +#define THR_IN_CRITICAL(thrd) \ + (((thrd)->locklevel > 0) || \ + ((thrd)->critical_count > 0)) + +#define THR_YIELD_CHECK(thrd) \ +do { \ + if (!THR_IN_CRITICAL(thrd)) { \ + if (__predict_false(_libkse_debug)) \ + _thr_debug_check_yield(thrd); \ + if ((thrd)->critical_yield != 0) \ + _thr_sched_switch(thrd); \ + if ((thrd)->check_pending != 0) \ + _thr_sig_check_pending(thrd); \ + } \ +} while (0) + +#define THR_LOCK_ACQUIRE(thrd, lck) \ +do { \ + if ((thrd)->locklevel < MAX_THR_LOCKLEVEL) { \ + THR_DEACTIVATE_LAST_LOCK(thrd); \ + (thrd)->locklevel++; \ + _lock_acquire((lck), \ + &(thrd)->lockusers[(thrd)->locklevel - 1], \ + (thrd)->active_priority); \ + } else \ + PANIC("Exceeded maximum lock level"); \ +} while (0) + +#define THR_LOCK_RELEASE(thrd, lck) \ +do { \ + if ((thrd)->locklevel > 0) { \ + _lock_release((lck), \ + &(thrd)->lockusers[(thrd)->locklevel - 1]); \ + (thrd)->locklevel--; \ + THR_ACTIVATE_LAST_LOCK(thrd); \ + if ((thrd)->locklevel == 0) \ + THR_YIELD_CHECK(thrd); \ + } \ +} while (0) + +#define THR_ACTIVATE_LAST_LOCK(thrd) \ +do { \ + if ((thrd)->locklevel > 0) \ + _lockuser_setactive( \ + &(thrd)->lockusers[(thrd)->locklevel - 1], 1); \ +} while (0) + +#define THR_DEACTIVATE_LAST_LOCK(thrd) \ +do { \ + if ((thrd)->locklevel > 0) \ + _lockuser_setactive( \ + &(thrd)->lockusers[(thrd)->locklevel - 1], 0); \ +} while (0) + +/* + * For now, threads will have their own lock separate from their + * KSE scheduling lock. + */ +#define THR_LOCK(thr) THR_LOCK_ACQUIRE(thr, &(thr)->lock) +#define THR_UNLOCK(thr) THR_LOCK_RELEASE(thr, &(thr)->lock) +#define THR_THREAD_LOCK(curthrd, thr) THR_LOCK_ACQUIRE(curthrd, &(thr)->lock) +#define THR_THREAD_UNLOCK(curthrd, thr) THR_LOCK_RELEASE(curthrd, &(thr)->lock) + +/* + * Priority queue manipulation macros (using pqe link). We use + * the thread's kseg link instead of the kse link because a thread + * does not (currently) have a statically assigned kse. + */ +#define THR_RUNQ_INSERT_HEAD(thrd) \ + _pq_insert_head(&(thrd)->kseg->kg_schedq.sq_runq, thrd) +#define THR_RUNQ_INSERT_TAIL(thrd) \ + _pq_insert_tail(&(thrd)->kseg->kg_schedq.sq_runq, thrd) +#define THR_RUNQ_REMOVE(thrd) \ + _pq_remove(&(thrd)->kseg->kg_schedq.sq_runq, thrd) + +/* + * Macros to insert/remove threads to the all thread list and + * the gc list. + */ +#define THR_LIST_ADD(thrd) do { \ + if (((thrd)->tlflags & TLFLAGS_IN_TDLIST) == 0) { \ + TAILQ_INSERT_HEAD(&_thread_list, thrd, tle); \ + _thr_hash_add(thrd); \ + (thrd)->tlflags |= TLFLAGS_IN_TDLIST; \ + } \ +} while (0) +#define THR_LIST_REMOVE(thrd) do { \ + if (((thrd)->tlflags & TLFLAGS_IN_TDLIST) != 0) { \ + TAILQ_REMOVE(&_thread_list, thrd, tle); \ + _thr_hash_remove(thrd); \ + (thrd)->tlflags &= ~TLFLAGS_IN_TDLIST; \ + } \ +} while (0) +#define THR_GCLIST_ADD(thrd) do { \ + if (((thrd)->tlflags & TLFLAGS_IN_GCLIST) == 0) { \ + TAILQ_INSERT_HEAD(&_thread_gc_list, thrd, gcle);\ + (thrd)->tlflags |= TLFLAGS_IN_GCLIST; \ + _gc_count++; \ + } \ +} while (0) +#define THR_GCLIST_REMOVE(thrd) do { \ + if (((thrd)->tlflags & TLFLAGS_IN_GCLIST) != 0) { \ + TAILQ_REMOVE(&_thread_gc_list, thrd, gcle); \ + (thrd)->tlflags &= ~TLFLAGS_IN_GCLIST; \ + _gc_count--; \ + } \ +} while (0) + +#define GC_NEEDED() (atomic_load_acq_int(&_gc_count) >= 5) + +/* + * Locking the scheduling queue for another thread uses that thread's + * KSEG lock. + */ +#define THR_SCHED_LOCK(curthr, thr) do { \ + (curthr)->critical[(curthr)->locklevel] = _kse_critical_enter(); \ + (curthr)->locklevel++; \ + KSE_SCHED_LOCK((curthr)->kse, (thr)->kseg); \ +} while (0) + +#define THR_SCHED_UNLOCK(curthr, thr) do { \ + KSE_SCHED_UNLOCK((curthr)->kse, (thr)->kseg); \ + (curthr)->locklevel--; \ + _kse_critical_leave((curthr)->critical[(curthr)->locklevel]); \ +} while (0) + +/* Take the scheduling lock with the intent to call the scheduler. */ +#define THR_LOCK_SWITCH(curthr) do { \ + (void)_kse_critical_enter(); \ + KSE_SCHED_LOCK((curthr)->kse, (curthr)->kseg); \ +} while (0) +#define THR_UNLOCK_SWITCH(curthr) do { \ + KSE_SCHED_UNLOCK((curthr)->kse, (curthr)->kseg);\ +} while (0) + +#define THR_CRITICAL_ENTER(thr) (thr)->critical_count++ +#define THR_CRITICAL_LEAVE(thr) do { \ + (thr)->critical_count--; \ + if (((thr)->critical_yield != 0) && \ + ((thr)->critical_count == 0)) { \ + (thr)->critical_yield = 0; \ + _thr_sched_switch(thr); \ + } \ +} while (0) + +#define THR_IS_ACTIVE(thrd) \ + ((thrd)->kse != NULL) && ((thrd)->kse->k_curthread == (thrd)) + +#define THR_IN_SYNCQ(thrd) (((thrd)->sflags & THR_FLAGS_IN_SYNCQ) != 0) + +#define THR_IS_SUSPENDED(thrd) \ + (((thrd)->state == PS_SUSPENDED) || \ + (((thrd)->flags & THR_FLAGS_SUSPENDED) != 0)) +#define THR_IS_EXITING(thrd) (((thrd)->flags & THR_FLAGS_EXITING) != 0) +#define DBG_CAN_RUN(thrd) (((thrd)->tcb->tcb_tmbx.tm_dflags & \ + TMDF_SUSPEND) == 0) + +extern int __isthreaded; + +static inline int +_kse_isthreaded(void) +{ + return (__isthreaded != 0); +} + +/* + * Global variables for the pthread kernel. + */ + +SCLASS void *_usrstack SCLASS_PRESET(NULL); +SCLASS struct kse *_kse_initial SCLASS_PRESET(NULL); +SCLASS struct pthread *_thr_initial SCLASS_PRESET(NULL); +/* For debugger */ +SCLASS int _libkse_debug SCLASS_PRESET(0); +SCLASS int _thread_activated SCLASS_PRESET(0); +SCLASS int _thread_scope_system SCLASS_PRESET(0); + +/* List of all threads: */ +SCLASS TAILQ_HEAD(, pthread) _thread_list + SCLASS_PRESET(TAILQ_HEAD_INITIALIZER(_thread_list)); + +/* List of threads needing GC: */ +SCLASS TAILQ_HEAD(, pthread) _thread_gc_list + SCLASS_PRESET(TAILQ_HEAD_INITIALIZER(_thread_gc_list)); + +SCLASS int _thread_active_threads SCLASS_PRESET(1); + +SCLASS TAILQ_HEAD(atfork_head, pthread_atfork) _thr_atfork_list; +SCLASS pthread_mutex_t _thr_atfork_mutex; + +/* Default thread attributes: */ +SCLASS struct pthread_attr _pthread_attr_default + SCLASS_PRESET({ + SCHED_RR, 0, TIMESLICE_USEC, THR_DEFAULT_PRIORITY, + THR_CREATE_RUNNING, PTHREAD_CREATE_JOINABLE, NULL, + NULL, NULL, /* stacksize */0, /* guardsize */0 + }); + +/* Default mutex attributes: */ +SCLASS struct pthread_mutex_attr _pthread_mutexattr_default + SCLASS_PRESET({PTHREAD_MUTEX_DEFAULT, PTHREAD_PRIO_NONE, 0, 0 }); + +/* Default condition variable attributes: */ +SCLASS struct pthread_cond_attr _pthread_condattr_default + SCLASS_PRESET({COND_TYPE_FAST, 0}); + +/* Clock resolution in usec. */ +SCLASS int _clock_res_usec SCLASS_PRESET(CLOCK_RES_USEC); + +/* Array of signal actions for this process: */ +SCLASS struct sigaction _thread_sigact[_SIG_MAXSIG]; + +/* + * Lock for above count of dummy handlers and for the process signal + * mask and pending signal sets. + */ +SCLASS struct lock _thread_signal_lock; + +/* Pending signals and mask for this process: */ +SCLASS sigset_t _thr_proc_sigpending; +SCLASS siginfo_t _thr_proc_siginfo[_SIG_MAXSIG]; + +SCLASS pid_t _thr_pid SCLASS_PRESET(0); + +/* Garbage collector lock. */ +SCLASS struct lock _gc_lock; +SCLASS int _gc_check SCLASS_PRESET(0); +SCLASS int _gc_count SCLASS_PRESET(0); + +SCLASS struct lock _mutex_static_lock; +SCLASS struct lock _rwlock_static_lock; +SCLASS struct lock _keytable_lock; +SCLASS struct lock _thread_list_lock; +SCLASS int _thr_guard_default; +SCLASS int _thr_stack_default; +SCLASS int _thr_stack_initial; +SCLASS int _thr_page_size; +SCLASS pthread_t _thr_sig_daemon; +SCLASS int _thr_debug_flags SCLASS_PRESET(0); + +/* Undefine the storage class and preset specifiers: */ +#undef SCLASS +#undef SCLASS_PRESET + + +/* + * Function prototype definitions. + */ +__BEGIN_DECLS +int _cond_reinit(pthread_cond_t *); +struct kse *_kse_alloc(struct pthread *, int sys_scope); +kse_critical_t _kse_critical_enter(void); +void _kse_critical_leave(kse_critical_t); +int _kse_in_critical(void); +void _kse_free(struct pthread *, struct kse *); +void _kse_init(void); +struct kse_group *_kseg_alloc(struct pthread *); +void _kse_lock_wait(struct lock *, struct lockuser *lu); +void _kse_lock_wakeup(struct lock *, struct lockuser *lu); +void _kse_single_thread(struct pthread *); +int _kse_setthreaded(int); +void _kseg_free(struct kse_group *); +int _mutex_cv_lock(pthread_mutex_t *); +int _mutex_cv_unlock(pthread_mutex_t *); +void _mutex_notify_priochange(struct pthread *, struct pthread *, int); +int _mutex_reinit(struct pthread_mutex *); +void _mutex_unlock_private(struct pthread *); +void _libpthread_init(struct pthread *); +int _pq_alloc(struct pq_queue *, int, int); +void _pq_free(struct pq_queue *); +int _pq_init(struct pq_queue *); +void _pq_remove(struct pq_queue *pq, struct pthread *); +void _pq_insert_head(struct pq_queue *pq, struct pthread *); +void _pq_insert_tail(struct pq_queue *pq, struct pthread *); +struct pthread *_pq_first(struct pq_queue *pq); +struct pthread *_pq_first_debug(struct pq_queue *pq); +void *_pthread_getspecific(pthread_key_t); +int _pthread_key_create(pthread_key_t *, void (*) (void *)); +int _pthread_key_delete(pthread_key_t); +int _pthread_mutex_destroy(pthread_mutex_t *); +int _pthread_mutex_init(pthread_mutex_t *, const pthread_mutexattr_t *); +int _pthread_mutex_lock(pthread_mutex_t *); +int _pthread_mutex_trylock(pthread_mutex_t *); +int _pthread_mutex_unlock(pthread_mutex_t *); +int _pthread_mutexattr_init(pthread_mutexattr_t *); +int _pthread_mutexattr_destroy(pthread_mutexattr_t *); +int _pthread_mutexattr_settype(pthread_mutexattr_t *, int); +int _pthread_once(pthread_once_t *, void (*) (void)); +int _pthread_rwlock_init(pthread_rwlock_t *, const pthread_rwlockattr_t *); +int _pthread_rwlock_destroy (pthread_rwlock_t *); +struct pthread *_pthread_self(void); +int _pthread_setspecific(pthread_key_t, const void *); +void _pthread_yield(void); +void _pthread_cleanup_push(void (*routine) (void *), void *routine_arg); +void _pthread_cleanup_pop(int execute); +struct pthread *_thr_alloc(struct pthread *); +void _thr_exit(char *, int, char *); +void _thr_exit_cleanup(void); +void _thr_lock_wait(struct lock *lock, struct lockuser *lu); +void _thr_lock_wakeup(struct lock *lock, struct lockuser *lu); +void _thr_mutex_reinit(pthread_mutex_t *); +int _thr_ref_add(struct pthread *, struct pthread *, int); +void _thr_ref_delete(struct pthread *, struct pthread *); +void _thr_rtld_init(void); +void _thr_rtld_fini(void); +int _thr_schedule_add(struct pthread *, struct pthread *); +void _thr_schedule_remove(struct pthread *, struct pthread *); +void _thr_setrunnable(struct pthread *curthread, struct pthread *thread); +struct kse_mailbox *_thr_setrunnable_unlocked(struct pthread *thread); +struct kse_mailbox *_thr_sig_add(struct pthread *, int, siginfo_t *); +void _thr_sig_dispatch(struct kse *, int, siginfo_t *); +int _thr_stack_alloc(struct pthread_attr *); +void _thr_stack_free(struct pthread_attr *); +void _thr_exit_cleanup(void); +void _thr_free(struct pthread *, struct pthread *); +void _thr_gc(struct pthread *); +void _thr_panic_exit(char *, int, char *); +void _thread_cleanupspecific(void); +void _thread_dump_info(void); +void _thread_printf(int, const char *, ...); +void _thr_sched_switch(struct pthread *); +void _thr_sched_switch_unlocked(struct pthread *); +void _thr_set_timeout(const struct timespec *); +void _thr_seterrno(struct pthread *, int); +void _thr_sig_handler(int, siginfo_t *, ucontext_t *); +void _thr_sig_check_pending(struct pthread *); +void _thr_sig_rundown(struct pthread *, ucontext_t *); +void _thr_sig_send(struct pthread *pthread, int sig); +void _thr_sigframe_restore(struct pthread *thread, struct pthread_sigframe *psf); +void _thr_spinlock_init(void); +void _thr_cancel_enter(struct pthread *); +void _thr_cancel_leave(struct pthread *, int); +int _thr_setconcurrency(int new_level); +int _thr_setmaxconcurrency(void); +void _thr_critical_enter(struct pthread *); +void _thr_critical_leave(struct pthread *); +int _thr_start_sig_daemon(void); +int _thr_getprocsig(int sig, siginfo_t *siginfo); +int _thr_getprocsig_unlocked(int sig, siginfo_t *siginfo); +void _thr_signal_init(void); +void _thr_signal_deinit(void); +void _thr_hash_add(struct pthread *); +void _thr_hash_remove(struct pthread *); +struct pthread *_thr_hash_find(struct pthread *); +void _thr_finish_cancellation(void *arg); +int _thr_sigonstack(void *sp); +void _thr_debug_check_yield(struct pthread *); + +/* + * Aliases for _pthread functions. Should be called instead of + * originals if PLT replocation is unwanted at runtme. + */ +int _thr_cond_broadcast(pthread_cond_t *); +int _thr_cond_signal(pthread_cond_t *); +int _thr_cond_wait(pthread_cond_t *, pthread_mutex_t *); +int _thr_mutex_lock(pthread_mutex_t *); +int _thr_mutex_unlock(pthread_mutex_t *); +int _thr_rwlock_rdlock (pthread_rwlock_t *); +int _thr_rwlock_wrlock (pthread_rwlock_t *); +int _thr_rwlock_unlock (pthread_rwlock_t *); + +/* #include <sys/aio.h> */ +#ifdef _SYS_AIO_H_ +int __sys_aio_suspend(const struct aiocb * const[], int, const struct timespec *); +#endif + +/* #include <fcntl.h> */ +#ifdef _SYS_FCNTL_H_ +int __sys_fcntl(int, int, ...); +int __sys_open(const char *, int, ...); +#endif + +/* #include <sys/ioctl.h> */ +#ifdef _SYS_IOCTL_H_ +int __sys_ioctl(int, unsigned long, ...); +#endif + +/* #inclde <sched.h> */ +#ifdef _SCHED_H_ +int __sys_sched_yield(void); +#endif + +/* #include <signal.h> */ +#ifdef _SIGNAL_H_ +int __sys_kill(pid_t, int); +int __sys_sigaction(int, const struct sigaction *, struct sigaction *); +int __sys_sigpending(sigset_t *); +int __sys_sigprocmask(int, const sigset_t *, sigset_t *); +int __sys_sigsuspend(const sigset_t *); +int __sys_sigreturn(ucontext_t *); +int __sys_sigaltstack(const struct sigaltstack *, struct sigaltstack *); +#endif + +/* #include <sys/socket.h> */ +#ifdef _SYS_SOCKET_H_ +int __sys_accept(int, struct sockaddr *, socklen_t *); +int __sys_connect(int, const struct sockaddr *, socklen_t); +int __sys_sendfile(int, int, off_t, size_t, struct sf_hdtr *, + off_t *, int); +#endif + +/* #include <sys/uio.h> */ +#ifdef _SYS_UIO_H_ +ssize_t __sys_readv(int, const struct iovec *, int); +ssize_t __sys_writev(int, const struct iovec *, int); +#endif + +/* #include <time.h> */ +#ifdef _TIME_H_ +int __sys_nanosleep(const struct timespec *, struct timespec *); +#endif + +/* #include <unistd.h> */ +#ifdef _UNISTD_H_ +int __sys_close(int); +int __sys_execve(const char *, char * const *, char * const *); +int __sys_fork(void); +int __sys_fsync(int); +pid_t __sys_getpid(void); +int __sys_select(int, fd_set *, fd_set *, fd_set *, struct timeval *); +ssize_t __sys_read(int, void *, size_t); +ssize_t __sys_write(int, const void *, size_t); +void __sys_exit(int); +int __sys_sigwait(const sigset_t *, int *); +int __sys_sigtimedwait(sigset_t *, siginfo_t *, struct timespec *); +#endif + +/* #include <poll.h> */ +#ifdef _SYS_POLL_H_ +int __sys_poll(struct pollfd *, unsigned, int); +#endif + +/* #include <sys/mman.h> */ +#ifdef _SYS_MMAN_H_ +int __sys_msync(void *, size_t, int); +#endif + +#endif /* !_THR_PRIVATE_H */ diff --git a/lib/libpthread/thread/thr_pselect.c b/lib/libpthread/thread/thr_pselect.c new file mode 100644 index 0000000..a0e9410 --- /dev/null +++ b/lib/libpthread/thread/thr_pselect.c @@ -0,0 +1,60 @@ +/*- + * Copyright (c) 2002 Daniel M. Eischen <deischen@freebsd.org> + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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> +__FBSDID("$FreeBSD$"); + +#include <sys/select.h> +#include <sys/time.h> + +#include <errno.h> +#include <signal.h> +#include <pthread.h> + +#include "thr_private.h" + +extern int __pselect(int count, fd_set *rfds, fd_set *wfds, fd_set *efds, + const struct timespec *timo, const sigset_t *mask); + +LT10_COMPAT_PRIVATE(_pselect); +LT10_COMPAT_DEFAULT(pselect); + +__weak_reference(_pselect, pselect); + +int +_pselect(int count, fd_set *rfds, fd_set *wfds, fd_set *efds, + const struct timespec *timo, const sigset_t *mask) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_cancel_enter(curthread); + ret = __pselect(count, rfds, wfds, efds, timo, mask); + _thr_cancel_leave(curthread, 1); + + return (ret); +} diff --git a/lib/libpthread/thread/thr_pspinlock.c b/lib/libpthread/thread/thr_pspinlock.c new file mode 100644 index 0000000..5836fde --- /dev/null +++ b/lib/libpthread/thread/thr_pspinlock.c @@ -0,0 +1,174 @@ +/*- + * Copyright (c) 2003 David Xu <davidxu@freebsd.org> + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <errno.h> +#include <pthread.h> +#include <stdint.h> +#include <stdlib.h> + +#include "atomic_ops.h" +#include "thr_private.h" + +#define SPIN_COUNT 10000 + +LT10_COMPAT_PRIVATE(_pthread_spin_init); +LT10_COMPAT_DEFAULT(pthread_spin_init); +LT10_COMPAT_PRIVATE(_pthread_spin_destroy); +LT10_COMPAT_DEFAULT(pthread_spin_destroy); +LT10_COMPAT_PRIVATE(_pthread_spin_trylock); +LT10_COMPAT_DEFAULT(pthread_spin_trylock); +LT10_COMPAT_PRIVATE(_pthread_spin_lock); +LT10_COMPAT_DEFAULT(pthread_spin_lock); +LT10_COMPAT_PRIVATE(_pthread_spin_unlock); +LT10_COMPAT_DEFAULT(pthread_spin_unlock); + +__weak_reference(_pthread_spin_init, pthread_spin_init); +__weak_reference(_pthread_spin_destroy, pthread_spin_destroy); +__weak_reference(_pthread_spin_trylock, pthread_spin_trylock); +__weak_reference(_pthread_spin_lock, pthread_spin_lock); +__weak_reference(_pthread_spin_unlock, pthread_spin_unlock); + +int +_pthread_spin_init(pthread_spinlock_t *lock, int pshared) +{ + struct pthread_spinlock *lck; + int ret; + + if (lock == NULL || pshared != PTHREAD_PROCESS_PRIVATE) + ret = EINVAL; + else if ((lck = malloc(sizeof(struct pthread_spinlock))) == NULL) + ret = ENOMEM; + else { + lck->s_lock = 0; + lck->s_owner= NULL; + *lock = lck; + ret = 0; + } + + return (ret); +} + +int +_pthread_spin_destroy(pthread_spinlock_t *lock) +{ + int ret; + + if (lock == NULL || *lock == NULL) + ret = EINVAL; + else if ((*lock)->s_owner != NULL) + ret = EBUSY; + else { + free(*lock); + *lock = NULL; + ret = 0; + } + + return (ret); +} + +int +_pthread_spin_trylock(pthread_spinlock_t *lock) +{ + struct pthread_spinlock *lck; + struct pthread *self = _pthread_self(); + int oldval, ret; + + if (lock == NULL || (lck = *lock) == NULL) + ret = EINVAL; + else if (lck->s_owner == self) + ret = EDEADLK; + else if (lck->s_lock != 0) + ret = EBUSY; + else { + atomic_swap_int((int *)&(lck)->s_lock, 1, &oldval); + if (oldval) + ret = EBUSY; + else { + lck->s_owner = _pthread_self(); + ret = 0; + } + } + return (ret); +} + +int +_pthread_spin_lock(pthread_spinlock_t *lock) +{ + struct pthread_spinlock *lck; + struct pthread *self = _pthread_self(); + int count, oldval, ret; + + if (lock == NULL || (lck = *lock) == NULL) + ret = EINVAL; + else if (lck->s_owner == self) + ret = EDEADLK; + else { + do { + count = SPIN_COUNT; + while (lck->s_lock) { +#ifdef __i386__ + /* tell cpu we are spinning */ + __asm __volatile("pause"); +#endif + if (--count <= 0) { + count = SPIN_COUNT; + _pthread_yield(); + } + } + atomic_swap_int((int *)&(lck)->s_lock, 1, &oldval); + } while (oldval); + + lck->s_owner = self; + ret = 0; + } + + return (ret); +} + +int +_pthread_spin_unlock(pthread_spinlock_t *lock) +{ + struct pthread_spinlock *lck; + int ret; + + if (lock == NULL || (lck = *lock) == NULL) + ret = EINVAL; + else { + if (lck->s_owner != _pthread_self()) + ret = EPERM; + else { + lck->s_owner = NULL; + atomic_swap_int((int *)&lck->s_lock, 0, &ret); + ret = 0; + } + } + + return (ret); +} + diff --git a/lib/libpthread/thread/thr_raise.c b/lib/libpthread/thread/thr_raise.c new file mode 100644 index 0000000..ad4aa39 --- /dev/null +++ b/lib/libpthread/thread/thr_raise.c @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2003 David Xu<davidxu@freebsd.org>. + * 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(s), this list of conditions and the following disclaimer as + * the first lines of this file unmodified other than the possible + * addition of one or more copyright notices. + * 2. Redistributions in binary form must reproduce the above copyright + * notice(s), this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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. + * + * $FreeBSD$ + */ + +#include <pthread.h> +#include <errno.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_raise); +LT10_COMPAT_DEFAULT(raise); + +__weak_reference(_raise, raise); + +int +_raise(int sig) +{ + int ret; + + if (!_kse_isthreaded()) + ret = kill(getpid(), sig); + else { + ret = pthread_kill(pthread_self(), sig); + if (ret != 0) { + errno = ret; + ret = -1; + } + } + return (ret); +} diff --git a/lib/libpthread/thread/thr_read.c b/lib/libpthread/thread/thr_read.c new file mode 100644 index 0000000..dc29d1b --- /dev/null +++ b/lib/libpthread/thread/thr_read.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + * + */ +#include <sys/types.h> +#include <sys/fcntl.h> +#include <sys/uio.h> +#include <errno.h> +#include <unistd.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(__read); +LT10_COMPAT_DEFAULT(read); + +__weak_reference(__read, read); + +ssize_t +__read(int fd, void *buf, size_t nbytes) +{ + struct pthread *curthread = _get_curthread(); + ssize_t ret; + + _thr_cancel_enter(curthread); + ret = __sys_read(fd, buf, nbytes); + _thr_cancel_leave(curthread, 1); + + return ret; +} diff --git a/lib/libpthread/thread/thr_readv.c b/lib/libpthread/thread/thr_readv.c new file mode 100644 index 0000000..c2d9360 --- /dev/null +++ b/lib/libpthread/thread/thr_readv.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + * + */ +#include <sys/types.h> +#include <sys/fcntl.h> +#include <sys/uio.h> +#include <errno.h> +#include <unistd.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(__readv); +LT10_COMPAT_DEFAULT(readv); + +__weak_reference(__readv, readv); + +ssize_t +__readv(int fd, const struct iovec *iov, int iovcnt) +{ + struct pthread *curthread = _get_curthread(); + ssize_t ret; + + _thr_cancel_enter(curthread); + ret = __sys_readv(fd, iov, iovcnt); + _thr_cancel_leave(curthread, 1); + + return ret; +} diff --git a/lib/libpthread/thread/thr_resume_np.c b/lib/libpthread/thread/thr_resume_np.c new file mode 100644 index 0000000..70c5f17 --- /dev/null +++ b/lib/libpthread/thread/thr_resume_np.c @@ -0,0 +1,112 @@ +/* + * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +static struct kse_mailbox *resume_common(struct pthread *); + +LT10_COMPAT_PRIVATE(_pthread_resume_np); +LT10_COMPAT_DEFAULT(pthread_resume_np); +LT10_COMPAT_PRIVATE(_pthread_resume_all_np); +LT10_COMPAT_DEFAULT(pthread_resume_all_np); + +__weak_reference(_pthread_resume_np, pthread_resume_np); +__weak_reference(_pthread_resume_all_np, pthread_resume_all_np); + + +/* Resume a thread: */ +int +_pthread_resume_np(pthread_t thread) +{ + struct pthread *curthread = _get_curthread(); + struct kse_mailbox *kmbx; + int ret; + + /* Add a reference to the thread: */ + if ((ret = _thr_ref_add(curthread, thread, /*include dead*/0)) == 0) { + /* Lock the threads scheduling queue: */ + THR_SCHED_LOCK(curthread, thread); + kmbx = resume_common(thread); + THR_SCHED_UNLOCK(curthread, thread); + _thr_ref_delete(curthread, thread); + if (kmbx != NULL) + kse_wakeup(kmbx); + } + return (ret); +} + +void +_pthread_resume_all_np(void) +{ + struct pthread *curthread = _get_curthread(); + struct pthread *thread; + struct kse_mailbox *kmbx; + kse_critical_t crit; + + /* Take the thread list lock: */ + crit = _kse_critical_enter(); + KSE_LOCK_ACQUIRE(curthread->kse, &_thread_list_lock); + + TAILQ_FOREACH(thread, &_thread_list, tle) { + if (thread != curthread) { + THR_SCHED_LOCK(curthread, thread); + kmbx = resume_common(thread); + THR_SCHED_UNLOCK(curthread, thread); + if (kmbx != NULL) + kse_wakeup(kmbx); + } + } + + /* Release the thread list lock: */ + KSE_LOCK_RELEASE(curthread->kse, &_thread_list_lock); + _kse_critical_leave(crit); +} + +static struct kse_mailbox * +resume_common(struct pthread *thread) +{ + /* Clear the suspend flag: */ + thread->flags &= ~THR_FLAGS_SUSPENDED; + + /* + * If the thread's state is suspended, that means it is + * now runnable but not in any scheduling queue. Set the + * state to running and insert it into the run queue. + */ + if (thread->state == PS_SUSPENDED) + return (_thr_setrunnable_unlocked(thread)); + else + return (NULL); +} diff --git a/lib/libpthread/thread/thr_rtld.c b/lib/libpthread/thread/thr_rtld.c new file mode 100644 index 0000000..e813073 --- /dev/null +++ b/lib/libpthread/thread/thr_rtld.c @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2001 Alexander Kabaev + * 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 + * in this position and unchanged. + * 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. 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. + * + * $FreeBSD$ + */ +#include <sys/cdefs.h> +#include <stdlib.h> + +#include "rtld_lock.h" +#include "thr_private.h" + +static int _thr_rtld_clr_flag(int); +static void *_thr_rtld_lock_create(void); +static void _thr_rtld_lock_destroy(void *); +static void _thr_rtld_lock_release(void *); +static void _thr_rtld_rlock_acquire(void *); +static int _thr_rtld_set_flag(int); +static void _thr_rtld_wlock_acquire(void *); + +#ifdef NOTYET +static void * +_thr_rtld_lock_create(void) +{ + pthread_rwlock_t prwlock; + if (_pthread_rwlock_init(&prwlock, NULL)) + return (NULL); + return (prwlock); +} + +static void +_thr_rtld_lock_destroy(void *lock) +{ + pthread_rwlock_t prwlock; + + prwlock = (pthread_rwlock_t)lock; + if (prwlock != NULL) + _pthread_rwlock_destroy(&prwlock); +} + +static void +_thr_rtld_rlock_acquire(void *lock) +{ + pthread_rwlock_t prwlock; + + prwlock = (pthread_rwlock_t)lock; + _thr_rwlock_rdlock(&prwlock); +} + +static void +_thr_rtld_wlock_acquire(void *lock) +{ + pthread_rwlock_t prwlock; + + prwlock = (pthread_rwlock_t)lock; + _thr_rwlock_wrlock(&prwlock); +} + +static void +_thr_rtld_lock_release(void *lock) +{ + pthread_rwlock_t prwlock; + + prwlock = (pthread_rwlock_t)lock; + _thr_rwlock_unlock(&prwlock); +} + + +static int +_thr_rtld_set_flag(int mask) +{ + struct pthread *curthread; + int bits; + + curthread = _get_curthread(); + if (curthread != NULL) { + bits = curthread->rtld_bits; + curthread->rtld_bits |= mask; + } else { + bits = 0; + PANIC("No current thread in rtld call"); + } + + return (bits); +} + +static int +_thr_rtld_clr_flag(int mask) +{ + struct pthread *curthread; + int bits; + + curthread = _get_curthread(); + if (curthread != NULL) { + bits = curthread->rtld_bits; + curthread->rtld_bits &= ~mask; + } else { + bits = 0; + PANIC("No current thread in rtld call"); + } + return (bits); +} + +void +_thr_rtld_init(void) +{ + struct RtldLockInfo li; + + li.lock_create = _thr_rtld_lock_create; + li.lock_destroy = _thr_rtld_lock_destroy; + li.rlock_acquire = _thr_rtld_rlock_acquire; + li.wlock_acquire = _thr_rtld_wlock_acquire; + li.lock_release = _thr_rtld_lock_release; + li.thread_set_flag = _thr_rtld_set_flag; + li.thread_clr_flag = _thr_rtld_clr_flag; + li.at_fork = NULL; + _rtld_thread_init(&li); +} + +void +_thr_rtld_fini(void) +{ + _rtld_thread_init(NULL); +} +#endif + +struct rtld_kse_lock { + struct lock lck; + struct kse *owner; + kse_critical_t crit; + int count; + int write; +}; + +static void * +_thr_rtld_lock_create(void) +{ + struct rtld_kse_lock *l; + + if ((l = malloc(sizeof(struct rtld_kse_lock))) != NULL) { + _lock_init(&l->lck, LCK_ADAPTIVE, _kse_lock_wait, + _kse_lock_wakeup); + l->owner = NULL; + l->count = 0; + l->write = 0; + } + return (l); +} + +static void +_thr_rtld_lock_destroy(void *lock) +{ + /* XXX We really can not free memory after a fork() */ +#if 0 + struct rtld_kse_lock *l; + + l = (struct rtld_kse_lock *)lock; + _lock_destroy(&l->lck); + free(l); +#endif + return; +} + +static void +_thr_rtld_rlock_acquire(void *lock) +{ + struct rtld_kse_lock *l; + kse_critical_t crit; + struct kse *curkse; + + l = (struct rtld_kse_lock *)lock; + crit = _kse_critical_enter(); + curkse = _get_curkse(); + if (l->owner == curkse) { + l->count++; + _kse_critical_leave(crit); /* probably not necessary */ + } else { + KSE_LOCK_ACQUIRE(curkse, &l->lck); + l->crit = crit; + l->owner = curkse; + l->count = 1; + l->write = 0; + } +} + +static void +_thr_rtld_wlock_acquire(void *lock) +{ + struct rtld_kse_lock *l; + kse_critical_t crit; + struct kse *curkse; + + l = (struct rtld_kse_lock *)lock; + crit = _kse_critical_enter(); + curkse = _get_curkse(); + if (l->owner == curkse) { + _kse_critical_leave(crit); + PANIC("Recursive write lock attempt on rtld lock"); + } else { + KSE_LOCK_ACQUIRE(curkse, &l->lck); + l->crit = crit; + l->owner = curkse; + l->count = 1; + l->write = 1; + } +} + +static void +_thr_rtld_lock_release(void *lock) +{ + struct rtld_kse_lock *l; + kse_critical_t crit; + struct kse *curkse; + + l = (struct rtld_kse_lock *)lock; + crit = _kse_critical_enter(); + curkse = _get_curkse(); + if (l->owner != curkse) { + /* + * We might want to forcibly unlock the rtld lock + * and/or disable threaded mode so there is better + * chance that the panic will work. Otherwise, + * we could end up trying to take the rtld lock + * again. + */ + _kse_critical_leave(crit); + PANIC("Attempt to unlock rtld lock when not owner."); + } else { + l->count--; + if (l->count == 0) { + /* + * If there ever is a count associated with + * _kse_critical_leave(), we'll need to add + * another call to it here with the crit + * value from above. + */ + crit = l->crit; + l->owner = NULL; + l->write = 0; + KSE_LOCK_RELEASE(curkse, &l->lck); + } + _kse_critical_leave(crit); + } +} + + +static int +_thr_rtld_set_flag(int mask) +{ + return (0); +} + +static int +_thr_rtld_clr_flag(int mask) +{ + return (0); +} + +void +_thr_rtld_init(void) +{ + struct RtldLockInfo li; + + li.lock_create = _thr_rtld_lock_create; + li.lock_destroy = _thr_rtld_lock_destroy; + li.rlock_acquire = _thr_rtld_rlock_acquire; + li.wlock_acquire = _thr_rtld_wlock_acquire; + li.lock_release = _thr_rtld_lock_release; + li.thread_set_flag = _thr_rtld_set_flag; + li.thread_clr_flag = _thr_rtld_clr_flag; + li.at_fork = NULL; + _rtld_thread_init(&li); +} + +void +_thr_rtld_fini(void) +{ + _rtld_thread_init(NULL); +} diff --git a/lib/libpthread/thread/thr_rwlock.c b/lib/libpthread/thread/thr_rwlock.c new file mode 100644 index 0000000..a0b36de --- /dev/null +++ b/lib/libpthread/thread/thr_rwlock.c @@ -0,0 +1,438 @@ +/*- + * Copyright (c) 1998 Alex Nash + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include <errno.h> +#include <limits.h> +#include <stdlib.h> + +#include "namespace.h" +#include <pthread.h> +#include "un-namespace.h" +#include "thr_private.h" + +/* maximum number of times a read lock may be obtained */ +#define MAX_READ_LOCKS (INT_MAX - 1) + +LT10_COMPAT_PRIVATE(_pthread_rwlock_destroy); +LT10_COMPAT_DEFAULT(pthread_rwlock_destroy); +LT10_COMPAT_PRIVATE(_pthread_rwlock_init); +LT10_COMPAT_DEFAULT(pthread_rwlock_init); +LT10_COMPAT_PRIVATE(_pthread_rwlock_rdlock); +LT10_COMPAT_DEFAULT(pthread_rwlock_rdlock); +LT10_COMPAT_PRIVATE(_pthread_rwlock_timedrdlock); +LT10_COMPAT_DEFAULT(pthread_rwlock_timedrdlock); +LT10_COMPAT_PRIVATE(_pthread_rwlock_tryrdlock); +LT10_COMPAT_DEFAULT(pthread_rwlock_tryrdlock); +LT10_COMPAT_PRIVATE(_pthread_rwlock_trywrlock); +LT10_COMPAT_DEFAULT(pthread_rwlock_trywrlock); +LT10_COMPAT_PRIVATE(_pthread_rwlock_unlock); +LT10_COMPAT_DEFAULT(pthread_rwlock_unlock); +LT10_COMPAT_PRIVATE(_pthread_rwlock_wrlock); +LT10_COMPAT_DEFAULT(pthread_rwlock_wrlock); +LT10_COMPAT_PRIVATE(_pthread_rwlock_timedwrlock); +LT10_COMPAT_DEFAULT(pthread_rwlock_timedwrlock); + +__weak_reference(_pthread_rwlock_destroy, pthread_rwlock_destroy); +__weak_reference(_pthread_rwlock_init, pthread_rwlock_init); +__weak_reference(_pthread_rwlock_rdlock, pthread_rwlock_rdlock); +__weak_reference(_pthread_rwlock_timedrdlock, pthread_rwlock_timedrdlock); +__weak_reference(_pthread_rwlock_tryrdlock, pthread_rwlock_tryrdlock); +__weak_reference(_pthread_rwlock_trywrlock, pthread_rwlock_trywrlock); +__weak_reference(_pthread_rwlock_unlock, pthread_rwlock_unlock); +__weak_reference(_pthread_rwlock_wrlock, pthread_rwlock_wrlock); +__weak_reference(_pthread_rwlock_timedwrlock, pthread_rwlock_timedwrlock); + +/* + * Prototypes + */ +static int init_static(pthread_rwlock_t *rwlock); + + +static int +init_static(pthread_rwlock_t *rwlock) +{ + struct pthread *thread = _get_curthread(); + int ret; + + THR_LOCK_ACQUIRE(thread, &_rwlock_static_lock); + + if (*rwlock == NULL) + ret = _pthread_rwlock_init(rwlock, NULL); + else + ret = 0; + + THR_LOCK_RELEASE(thread, &_rwlock_static_lock); + return (ret); +} + +int +_pthread_rwlock_destroy (pthread_rwlock_t *rwlock) +{ + int ret; + + if (rwlock == NULL) + ret = EINVAL; + else { + pthread_rwlock_t prwlock; + + prwlock = *rwlock; + + _pthread_mutex_destroy(&prwlock->lock); + _pthread_cond_destroy(&prwlock->read_signal); + _pthread_cond_destroy(&prwlock->write_signal); + free(prwlock); + + *rwlock = NULL; + + ret = 0; + } + return (ret); +} + +int +_pthread_rwlock_init (pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr) +{ + pthread_rwlock_t prwlock; + int ret; + + /* allocate rwlock object */ + prwlock = (pthread_rwlock_t)malloc(sizeof(struct pthread_rwlock)); + + if (prwlock == NULL) + return (ENOMEM); + + /* initialize the lock */ + if ((ret = _pthread_mutex_init(&prwlock->lock, NULL)) != 0) + free(prwlock); + else { + /* initialize the read condition signal */ + ret = _pthread_cond_init(&prwlock->read_signal, NULL); + + if (ret != 0) { + _pthread_mutex_destroy(&prwlock->lock); + free(prwlock); + } else { + /* initialize the write condition signal */ + ret = _pthread_cond_init(&prwlock->write_signal, NULL); + + if (ret != 0) { + _pthread_cond_destroy(&prwlock->read_signal); + _pthread_mutex_destroy(&prwlock->lock); + free(prwlock); + } else { + /* success */ + prwlock->state = 0; + prwlock->blocked_writers = 0; + + *rwlock = prwlock; + } + } + } + + return (ret); +} + +static int +rwlock_rdlock_common (pthread_rwlock_t *rwlock, const struct timespec *abstime) +{ + pthread_rwlock_t prwlock; + struct pthread *curthread; + int ret; + + if (rwlock == NULL) + return (EINVAL); + + prwlock = *rwlock; + + /* check for static initialization */ + if (prwlock == NULL) { + if ((ret = init_static(rwlock)) != 0) + return (ret); + + prwlock = *rwlock; + } + + /* grab the monitor lock */ + if ((ret = _thr_mutex_lock(&prwlock->lock)) != 0) + return (ret); + + /* check lock count */ + if (prwlock->state == MAX_READ_LOCKS) { + _thr_mutex_unlock(&prwlock->lock); + return (EAGAIN); + } + + curthread = _get_curthread(); + if ((curthread->rdlock_count > 0) && (prwlock->state > 0)) { + /* + * To avoid having to track all the rdlocks held by + * a thread or all of the threads that hold a rdlock, + * we keep a simple count of all the rdlocks held by + * a thread. If a thread holds any rdlocks it is + * possible that it is attempting to take a recursive + * rdlock. If there are blocked writers and precedence + * is given to them, then that would result in the thread + * deadlocking. So allowing a thread to take the rdlock + * when it already has one or more rdlocks avoids the + * deadlock. I hope the reader can follow that logic ;-) + */ + ; /* nothing needed */ + } else { + /* give writers priority over readers */ + while (prwlock->blocked_writers || prwlock->state < 0) { + if (abstime) + ret = _pthread_cond_timedwait + (&prwlock->read_signal, + &prwlock->lock, abstime); + else + ret = _thr_cond_wait(&prwlock->read_signal, + &prwlock->lock); + if (ret != 0) { + /* can't do a whole lot if this fails */ + _thr_mutex_unlock(&prwlock->lock); + return (ret); + } + } + } + + curthread->rdlock_count++; + prwlock->state++; /* indicate we are locked for reading */ + + /* + * Something is really wrong if this call fails. Returning + * error won't do because we've already obtained the read + * lock. Decrementing 'state' is no good because we probably + * don't have the monitor lock. + */ + _thr_mutex_unlock(&prwlock->lock); + + return (ret); +} + +int +_pthread_rwlock_rdlock (pthread_rwlock_t *rwlock) +{ + return (rwlock_rdlock_common(rwlock, NULL)); +} + +__strong_reference(_pthread_rwlock_rdlock, _thr_rwlock_rdlock); + +int +_pthread_rwlock_timedrdlock (pthread_rwlock_t *rwlock, + const struct timespec *abstime) +{ + return (rwlock_rdlock_common(rwlock, abstime)); +} + +int +_pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock) +{ + struct pthread *curthread; + pthread_rwlock_t prwlock; + int ret; + + if (rwlock == NULL) + return (EINVAL); + + prwlock = *rwlock; + + /* check for static initialization */ + if (prwlock == NULL) { + if ((ret = init_static(rwlock)) != 0) + return (ret); + + prwlock = *rwlock; + } + + /* grab the monitor lock */ + if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0) + return (ret); + + curthread = _get_curthread(); + if (prwlock->state == MAX_READ_LOCKS) + ret = EAGAIN; + else if ((curthread->rdlock_count > 0) && (prwlock->state > 0)) { + /* see comment for pthread_rwlock_rdlock() */ + curthread->rdlock_count++; + prwlock->state++; + } + /* give writers priority over readers */ + else if (prwlock->blocked_writers || prwlock->state < 0) + ret = EBUSY; + else { + curthread->rdlock_count++; + prwlock->state++; /* indicate we are locked for reading */ + } + + /* see the comment on this in pthread_rwlock_rdlock */ + _pthread_mutex_unlock(&prwlock->lock); + + return (ret); +} + +int +_pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock) +{ + pthread_rwlock_t prwlock; + int ret; + + if (rwlock == NULL) + return (EINVAL); + + prwlock = *rwlock; + + /* check for static initialization */ + if (prwlock == NULL) { + if ((ret = init_static(rwlock)) != 0) + return (ret); + + prwlock = *rwlock; + } + + /* grab the monitor lock */ + if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0) + return (ret); + + if (prwlock->state != 0) + ret = EBUSY; + else + /* indicate we are locked for writing */ + prwlock->state = -1; + + /* see the comment on this in pthread_rwlock_rdlock */ + _pthread_mutex_unlock(&prwlock->lock); + + return (ret); +} + +int +_pthread_rwlock_unlock (pthread_rwlock_t *rwlock) +{ + struct pthread *curthread; + pthread_rwlock_t prwlock; + int ret; + + if (rwlock == NULL) + return (EINVAL); + + prwlock = *rwlock; + + if (prwlock == NULL) + return (EINVAL); + + /* grab the monitor lock */ + if ((ret = _thr_mutex_lock(&prwlock->lock)) != 0) + return (ret); + + curthread = _get_curthread(); + if (prwlock->state > 0) { + curthread->rdlock_count--; + prwlock->state--; + if (prwlock->state == 0 && prwlock->blocked_writers) + ret = _thr_cond_signal(&prwlock->write_signal); + } else if (prwlock->state < 0) { + prwlock->state = 0; + + if (prwlock->blocked_writers) + ret = _thr_cond_signal(&prwlock->write_signal); + else + ret = _thr_cond_broadcast(&prwlock->read_signal); + } else + ret = EINVAL; + + /* see the comment on this in pthread_rwlock_rdlock */ + _thr_mutex_unlock(&prwlock->lock); + + return (ret); +} + +__strong_reference(_pthread_rwlock_unlock, _thr_rwlock_unlock); + +static int +rwlock_wrlock_common (pthread_rwlock_t *rwlock, const struct timespec *abstime) +{ + pthread_rwlock_t prwlock; + int ret; + + if (rwlock == NULL) + return (EINVAL); + + prwlock = *rwlock; + + /* check for static initialization */ + if (prwlock == NULL) { + if ((ret = init_static(rwlock)) != 0) + return (ret); + + prwlock = *rwlock; + } + + /* grab the monitor lock */ + if ((ret = _thr_mutex_lock(&prwlock->lock)) != 0) + return (ret); + + while (prwlock->state != 0) { + prwlock->blocked_writers++; + + if (abstime != NULL) + ret = _pthread_cond_timedwait(&prwlock->write_signal, + &prwlock->lock, abstime); + else + ret = _thr_cond_wait(&prwlock->write_signal, + &prwlock->lock); + if (ret != 0) { + prwlock->blocked_writers--; + _thr_mutex_unlock(&prwlock->lock); + return (ret); + } + + prwlock->blocked_writers--; + } + + /* indicate we are locked for writing */ + prwlock->state = -1; + + /* see the comment on this in pthread_rwlock_rdlock */ + _thr_mutex_unlock(&prwlock->lock); + + return (ret); +} + +int +_pthread_rwlock_wrlock (pthread_rwlock_t *rwlock) +{ + return (rwlock_wrlock_common (rwlock, NULL)); +} +__strong_reference(_pthread_rwlock_wrlock, _thr_rwlock_wrlock); + +int +_pthread_rwlock_timedwrlock (pthread_rwlock_t *rwlock, + const struct timespec *abstime) +{ + return (rwlock_wrlock_common (rwlock, abstime)); +} diff --git a/lib/libpthread/thread/thr_rwlockattr.c b/lib/libpthread/thread/thr_rwlockattr.c new file mode 100644 index 0000000..174b28f --- /dev/null +++ b/lib/libpthread/thread/thr_rwlockattr.c @@ -0,0 +1,107 @@ +/*- + * Copyright (c) 1998 Alex Nash + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include <errno.h> +#include <stdlib.h> + +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_rwlockattr_destroy); +LT10_COMPAT_DEFAULT(pthread_rwlockattr_destroy); +LT10_COMPAT_PRIVATE(_pthread_rwlockattr_getpshared); +LT10_COMPAT_DEFAULT(pthread_rwlockattr_getpshared); +LT10_COMPAT_PRIVATE(_pthread_rwlockattr_init); +LT10_COMPAT_DEFAULT(pthread_rwlockattr_init); +LT10_COMPAT_PRIVATE(_pthread_rwlockattr_setpshared); +LT10_COMPAT_DEFAULT(pthread_rwlockattr_setpshared); + +__weak_reference(_pthread_rwlockattr_destroy, pthread_rwlockattr_destroy); +__weak_reference(_pthread_rwlockattr_getpshared, pthread_rwlockattr_getpshared); +__weak_reference(_pthread_rwlockattr_init, pthread_rwlockattr_init); +__weak_reference(_pthread_rwlockattr_setpshared, pthread_rwlockattr_setpshared); + +int +_pthread_rwlockattr_destroy(pthread_rwlockattr_t *rwlockattr) +{ + pthread_rwlockattr_t prwlockattr; + + if (rwlockattr == NULL) + return(EINVAL); + + prwlockattr = *rwlockattr; + + if (prwlockattr == NULL) + return(EINVAL); + + free(prwlockattr); + + return(0); +} + +int +_pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *rwlockattr, + int *pshared) +{ + *pshared = (*rwlockattr)->pshared; + + return(0); +} + +int +_pthread_rwlockattr_init(pthread_rwlockattr_t *rwlockattr) +{ + pthread_rwlockattr_t prwlockattr; + + if (rwlockattr == NULL) + return(EINVAL); + + prwlockattr = (pthread_rwlockattr_t) + malloc(sizeof(struct pthread_rwlockattr)); + + if (prwlockattr == NULL) + return(ENOMEM); + + prwlockattr->pshared = PTHREAD_PROCESS_PRIVATE; + *rwlockattr = prwlockattr; + + return(0); +} + +int +_pthread_rwlockattr_setpshared(pthread_rwlockattr_t *rwlockattr, int pshared) +{ + /* Only PTHREAD_PROCESS_PRIVATE is supported. */ + if (pshared != PTHREAD_PROCESS_PRIVATE) + return(EINVAL); + + (*rwlockattr)->pshared = pshared; + + return(0); +} + diff --git a/lib/libpthread/thread/thr_select.c b/lib/libpthread/thread/thr_select.c new file mode 100644 index 0000000..97bcc37 --- /dev/null +++ b/lib/libpthread/thread/thr_select.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <unistd.h> +#include <errno.h> +#include <poll.h> +#include <stdlib.h> +#include <string.h> +#include <sys/param.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/fcntl.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(__select); +LT10_COMPAT_DEFAULT(select); + +__weak_reference(__select, select); + +int +__select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + struct timeval *timeout) +{ + struct pthread *curthread = _get_curthread(); + struct timespec ts; + int ret; + + if (numfds == 0 && timeout != NULL) { + TIMEVAL_TO_TIMESPEC(timeout, &ts); + return nanosleep(&ts, NULL); + } else { + _thr_cancel_enter(curthread); + ret = __sys_select(numfds, readfds, writefds, exceptfds, timeout); + _thr_cancel_leave(curthread, 1); + } + return ret; +} diff --git a/lib/libpthread/thread/thr_self.c b/lib/libpthread/thread/thr_self.c new file mode 100644 index 0000000..28ac613 --- /dev/null +++ b/lib/libpthread/thread/thr_self.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_self); +LT10_COMPAT_DEFAULT(pthread_self); + +__weak_reference(_pthread_self, pthread_self); + +pthread_t +_pthread_self(void) +{ + if (_thr_initial == NULL) + _libpthread_init(NULL); + + /* Return the running thread pointer: */ + return (_get_curthread()); +} diff --git a/lib/libpthread/thread/thr_sem.c b/lib/libpthread/thread/thr_sem.c new file mode 100644 index 0000000..21746bbe --- /dev/null +++ b/lib/libpthread/thread/thr_sem.c @@ -0,0 +1,267 @@ +/* + * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>. + * 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(s), this list of conditions and the following disclaimer as + * the first lines of this file unmodified other than the possible + * addition of one or more copyright notices. + * 2. Redistributions in binary form must reproduce the above copyright + * notice(s), this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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. + * + * $FreeBSD$ + */ + +#include "namespace.h" +#include <sys/types.h> +#include <sys/queue.h> +#include <errno.h> +#include <fcntl.h> +#include <pthread.h> +#include <semaphore.h> +#include <stdlib.h> +#include <time.h> +#include <_semaphore.h> +#include "un-namespace.h" +#include "libc_private.h" +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_sem_init); +LT10_COMPAT_DEFAULT(sem_init); +LT10_COMPAT_PRIVATE(_sem_wait); +LT10_COMPAT_DEFAULT(sem_wait); +LT10_COMPAT_PRIVATE(_sem_timedwait); +LT10_COMPAT_DEFAULT(sem_timedwait); +LT10_COMPAT_PRIVATE(_sem_post); +LT10_COMPAT_DEFAULT(sem_post); + +__weak_reference(_sem_init, sem_init); +__weak_reference(_sem_wait, sem_wait); +__weak_reference(_sem_timedwait, sem_timedwait); +__weak_reference(_sem_post, sem_post); + + +static inline int +sem_check_validity(sem_t *sem) +{ + + if ((sem != NULL) && ((*sem)->magic == SEM_MAGIC)) + return (0); + else { + errno = EINVAL; + return (-1); + } +} + +static void +decrease_nwaiters(void *arg) +{ + sem_t *sem = (sem_t *)arg; + + (*sem)->nwaiters--; + /* + * this function is called from cancellation point, + * the mutex should already be hold. + */ + _pthread_mutex_unlock(&(*sem)->lock); +} + +static sem_t +sem_alloc(unsigned int value, semid_t semid, int system_sem) +{ + sem_t sem; + + if (value > SEM_VALUE_MAX) { + errno = EINVAL; + return (NULL); + } + + sem = (sem_t)malloc(sizeof(struct sem)); + if (sem == NULL) { + errno = ENOSPC; + return (NULL); + } + + /* + * Initialize the semaphore. + */ + if (_pthread_mutex_init(&sem->lock, NULL) != 0) { + free(sem); + errno = ENOSPC; + return (NULL); + } + + if (_pthread_cond_init(&sem->gtzero, NULL) != 0) { + _pthread_mutex_destroy(&sem->lock); + free(sem); + errno = ENOSPC; + return (NULL); + } + + sem->count = (u_int32_t)value; + sem->nwaiters = 0; + sem->magic = SEM_MAGIC; + sem->semid = semid; + sem->syssem = system_sem; + return (sem); +} + +int +_sem_init(sem_t *sem, int pshared, unsigned int value) +{ + semid_t semid; + + semid = (semid_t)SEM_USER; + if ((pshared != 0) && (ksem_init(&semid, value) != 0)) + return (-1); + + (*sem) = sem_alloc(value, semid, pshared); + if ((*sem) == NULL) { + if (pshared != 0) + ksem_destroy(semid); + return (-1); + } + return (0); +} + +int +_sem_wait(sem_t *sem) +{ + struct pthread *curthread; + int retval; + + if (sem_check_validity(sem) != 0) + return (-1); + + curthread = _get_curthread(); + if ((*sem)->syssem != 0) { + _thr_cancel_enter(curthread); + retval = ksem_wait((*sem)->semid); + _thr_cancel_leave(curthread, retval != 0); + } + else { + _pthread_testcancel(); + _pthread_mutex_lock(&(*sem)->lock); + + while ((*sem)->count <= 0) { + (*sem)->nwaiters++; + THR_CLEANUP_PUSH(curthread, decrease_nwaiters, sem); + _pthread_cond_wait(&(*sem)->gtzero, &(*sem)->lock); + THR_CLEANUP_POP(curthread, 0); + (*sem)->nwaiters--; + } + (*sem)->count--; + + _pthread_mutex_unlock(&(*sem)->lock); + + retval = 0; + } + return (retval); +} + +int +_sem_timedwait(sem_t * __restrict sem, + const struct timespec * __restrict abs_timeout) +{ + struct pthread *curthread; + int retval; + int timeout_invalid; + + if (sem_check_validity(sem) != 0) + return (-1); + + if ((*sem)->syssem != 0) { + curthread = _get_curthread(); + _thr_cancel_enter(curthread); + retval = ksem_timedwait((*sem)->semid, abs_timeout); + _thr_cancel_leave(curthread, retval != 0); + } + else { + /* + * The timeout argument is only supposed to + * be checked if the thread would have blocked. + * This is checked outside of the lock so a + * segfault on an invalid address doesn't end + * up leaving the mutex locked. + */ + _pthread_testcancel(); + timeout_invalid = (abs_timeout->tv_nsec >= 1000000000) || + (abs_timeout->tv_nsec < 0); + _pthread_mutex_lock(&(*sem)->lock); + + if ((*sem)->count <= 0) { + if (timeout_invalid) { + _pthread_mutex_unlock(&(*sem)->lock); + errno = EINVAL; + return (-1); + } + (*sem)->nwaiters++; + _pthread_cleanup_push(decrease_nwaiters, sem); + _pthread_cond_timedwait(&(*sem)->gtzero, + &(*sem)->lock, abs_timeout); + _pthread_cleanup_pop(0); + (*sem)->nwaiters--; + } + if ((*sem)->count == 0) { + errno = ETIMEDOUT; + retval = -1; + } + else { + (*sem)->count--; + retval = 0; + } + + _pthread_mutex_unlock(&(*sem)->lock); + } + + return (retval); +} + +int +_sem_post(sem_t *sem) +{ + struct pthread *curthread; + int retval; + + if (sem_check_validity(sem) != 0) + return (-1); + + if ((*sem)->syssem != 0) + retval = ksem_post((*sem)->semid); + else { + /* + * sem_post() is required to be safe to call from within + * signal handlers. Thus, we must enter a critical region. + */ + curthread = _get_curthread(); + _thr_critical_enter(curthread); + _pthread_mutex_lock(&(*sem)->lock); + + (*sem)->count++; + if ((*sem)->nwaiters > 0) + _pthread_cond_signal(&(*sem)->gtzero); + + _pthread_mutex_unlock(&(*sem)->lock); + _thr_critical_leave(curthread); + retval = 0; + } + + return (retval); +} diff --git a/lib/libpthread/thread/thr_seterrno.c b/lib/libpthread/thread/thr_seterrno.c new file mode 100644 index 0000000..245d43f --- /dev/null +++ b/lib/libpthread/thread/thr_seterrno.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <pthread.h> +#include "thr_private.h" + +/* + * This function needs to reference the global error variable which is + * normally hidden from the user. + */ +#ifdef errno +#undef errno; +#endif +extern int errno; + +void +_thread_seterrno(pthread_t thread, int error) +{ + /* Check for the initial thread: */ + if (thread == _thr_initial) + /* The initial thread always uses the global error variable: */ + errno = error; + else + /* + * Threads other than the initial thread always use the error + * field in the thread structureL + */ + thread->error = error; +} diff --git a/lib/libpthread/thread/thr_setprio.c b/lib/libpthread/thread/thr_setprio.c new file mode 100644 index 0000000..3b7796a --- /dev/null +++ b/lib/libpthread/thread/thr_setprio.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_setprio); +LT10_COMPAT_DEFAULT(pthread_setprio); + +__weak_reference(_pthread_setprio, pthread_setprio); + +int +_pthread_setprio(pthread_t pthread, int prio) +{ + int ret, policy; + struct sched_param param; + + if ((ret = pthread_getschedparam(pthread, &policy, ¶m)) == 0) { + param.sched_priority = prio; + ret = pthread_setschedparam(pthread, policy, ¶m); + } + + /* Return the error status: */ + return (ret); +} diff --git a/lib/libpthread/thread/thr_setschedparam.c b/lib/libpthread/thread/thr_setschedparam.c new file mode 100644 index 0000000..8f5154c --- /dev/null +++ b/lib/libpthread/thread/thr_setschedparam.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>. + * 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 Daniel Eischen. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <errno.h> +#include <sys/param.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_setschedparam); +LT10_COMPAT_DEFAULT(pthread_setschedparam); + +__weak_reference(_pthread_setschedparam, pthread_setschedparam); + +int +_pthread_setschedparam(pthread_t pthread, int policy, + const struct sched_param *param) +{ + struct pthread *curthread = _get_curthread(); + int in_syncq; + int in_readyq = 0; + int old_prio; + int ret = 0; + + if ((param == NULL) || (policy < SCHED_FIFO) || (policy > SCHED_RR)) { + /* Return an invalid argument error: */ + ret = EINVAL; + } else if ((param->sched_priority < THR_MIN_PRIORITY) || + (param->sched_priority > THR_MAX_PRIORITY)) { + /* Return an unsupported value error. */ + ret = ENOTSUP; + + /* Find the thread in the list of active threads: */ + } else if ((ret = _thr_ref_add(curthread, pthread, /*include dead*/0)) + == 0) { + /* + * Lock the threads scheduling queue while we change + * its priority: + */ + THR_SCHED_LOCK(curthread, pthread); + if ((pthread->state == PS_DEAD) || + (pthread->state == PS_DEADLOCK) || + ((pthread->flags & THR_FLAGS_EXITING) != 0)) { + THR_SCHED_UNLOCK(curthread, pthread); + _thr_ref_delete(curthread, pthread); + return (ESRCH); + } + in_syncq = pthread->sflags & THR_FLAGS_IN_SYNCQ; + + /* Set the scheduling policy: */ + pthread->attr.sched_policy = policy; + + if (param->sched_priority == + THR_BASE_PRIORITY(pthread->base_priority)) + /* + * There is nothing to do; unlock the threads + * scheduling queue. + */ + THR_SCHED_UNLOCK(curthread, pthread); + else { + /* + * Remove the thread from its current priority + * queue before any adjustments are made to its + * active priority: + */ + old_prio = pthread->active_priority; + if ((pthread->flags & THR_FLAGS_IN_RUNQ) != 0) { + in_readyq = 1; + THR_RUNQ_REMOVE(pthread); + } + + /* Set the thread base priority: */ + pthread->base_priority &= + (THR_SIGNAL_PRIORITY | THR_RT_PRIORITY); + pthread->base_priority = param->sched_priority; + + /* Recalculate the active priority: */ + pthread->active_priority = MAX(pthread->base_priority, + pthread->inherited_priority); + + if (in_readyq) { + if ((pthread->priority_mutex_count > 0) && + (old_prio > pthread->active_priority)) { + /* + * POSIX states that if the priority is + * being lowered, the thread must be + * inserted at the head of the queue for + * its priority if it owns any priority + * protection or inheritence mutexes. + */ + THR_RUNQ_INSERT_HEAD(pthread); + } + else + THR_RUNQ_INSERT_TAIL(pthread); + } + + /* Unlock the threads scheduling queue: */ + THR_SCHED_UNLOCK(curthread, pthread); + + /* + * Check for any mutex priority adjustments. This + * includes checking for a priority mutex on which + * this thread is waiting. + */ + _mutex_notify_priochange(curthread, pthread, in_syncq); + } + _thr_ref_delete(curthread, pthread); + } + return (ret); +} diff --git a/lib/libpthread/thread/thr_sig.c b/lib/libpthread/thread/thr_sig.c new file mode 100644 index 0000000..f53b87f --- /dev/null +++ b/lib/libpthread/thread/thr_sig.c @@ -0,0 +1,1259 @@ +/* + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <sys/param.h> +#include <sys/types.h> +#include <sys/signalvar.h> +#include <signal.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <pthread.h> +#include "thr_private.h" + +/* Prototypes: */ +static inline void build_siginfo(siginfo_t *info, int signo); +#ifndef SYSTEM_SCOPE_ONLY +static struct pthread *thr_sig_find(struct kse *curkse, int sig, + siginfo_t *info); +#endif +static inline void thr_sigframe_restore(struct pthread *thread, + struct pthread_sigframe *psf); +static inline void thr_sigframe_save(struct pthread *thread, + struct pthread_sigframe *psf); + +#define SA_KILL 0x01 /* terminates process by default */ +#define SA_STOP 0x02 +#define SA_CONT 0x04 + +static int sigproptbl[NSIG] = { + SA_KILL, /* SIGHUP */ + SA_KILL, /* SIGINT */ + SA_KILL, /* SIGQUIT */ + SA_KILL, /* SIGILL */ + SA_KILL, /* SIGTRAP */ + SA_KILL, /* SIGABRT */ + SA_KILL, /* SIGEMT */ + SA_KILL, /* SIGFPE */ + SA_KILL, /* SIGKILL */ + SA_KILL, /* SIGBUS */ + SA_KILL, /* SIGSEGV */ + SA_KILL, /* SIGSYS */ + SA_KILL, /* SIGPIPE */ + SA_KILL, /* SIGALRM */ + SA_KILL, /* SIGTERM */ + 0, /* SIGURG */ + SA_STOP, /* SIGSTOP */ + SA_STOP, /* SIGTSTP */ + SA_CONT, /* SIGCONT */ + 0, /* SIGCHLD */ + SA_STOP, /* SIGTTIN */ + SA_STOP, /* SIGTTOU */ + 0, /* SIGIO */ + SA_KILL, /* SIGXCPU */ + SA_KILL, /* SIGXFSZ */ + SA_KILL, /* SIGVTALRM */ + SA_KILL, /* SIGPROF */ + 0, /* SIGWINCH */ + 0, /* SIGINFO */ + SA_KILL, /* SIGUSR1 */ + SA_KILL /* SIGUSR2 */ +}; + +/* #define DEBUG_SIGNAL */ +#ifdef DEBUG_SIGNAL +#define DBG_MSG stdout_debug +#else +#define DBG_MSG(x...) +#endif + +static __inline int +_thr_dump_enabled(void) +{ + return ((_thr_debug_flags & DBG_INFO_DUMP) != 0); +} + +/* + * Signal setup and delivery. + * + * 1) Delivering signals to threads in the same KSE. + * These signals are sent by upcall events and are set in the + * km_sigscaught field of the KSE mailbox. Since these signals + * are received while operating on the KSE stack, they can be + * delivered either by using signalcontext() to add a stack frame + * to the target thread's stack, or by adding them in the thread's + * pending set and having the thread run them down after it + * 2) Delivering signals to threads in other KSEs/KSEGs. + * 3) Delivering signals to threads in critical regions. + * 4) Delivering signals to threads after they change their signal masks. + * + * Methods of delivering signals. + * + * 1) Add a signal frame to the thread's saved context. + * 2) Add the signal to the thread structure, mark the thread as + * having signals to handle, and let the thread run them down + * after it resumes from the KSE scheduler. + * + * Problem with 1). You can't do this to a running thread or a + * thread in a critical region. + * + * Problem with 2). You can't do this to a thread that doesn't + * yield in some way (explicitly enters the scheduler). A thread + * blocked in the kernel or a CPU hungry thread will not see the + * signal without entering the scheduler. + * + * The solution is to use both 1) and 2) to deliver signals: + * + * o Thread in critical region - use 2). When the thread + * leaves the critical region it will check to see if it + * has pending signals and run them down. + * + * o Thread enters scheduler explicitly - use 2). The thread + * can check for pending signals after it returns from the + * the scheduler. + * + * o Thread is running and not current thread - use 2). When the + * thread hits a condition specified by one of the other bullets, + * the signal will be delivered. + * + * o Thread is running and is current thread (e.g., the thread + * has just changed its signal mask and now sees that it has + * pending signals) - just run down the pending signals. + * + * o Thread is swapped out due to quantum expiration - use 1) + * + * o Thread is blocked in kernel - kse_thr_wakeup() and then + * use 1) + */ + +/* + * Rules for selecting threads for signals received: + * + * 1) If the signal is a sychronous signal, it is delivered to + * the generating (current thread). If the thread has the + * signal masked, it is added to the threads pending signal + * set until the thread unmasks it. + * + * 2) A thread in sigwait() where the signal is in the thread's + * waitset. + * + * 3) A thread in sigsuspend() where the signal is not in the + * thread's suspended signal mask. + * + * 4) Any thread (first found/easiest to deliver) that has the + * signal unmasked. + */ + +#ifndef SYSTEM_SCOPE_ONLY + +static void * +sig_daemon(void *arg /* Unused */) +{ + int i; + kse_critical_t crit; + struct timespec ts; + sigset_t set; + struct kse *curkse; + struct pthread *curthread = _get_curthread(); + + DBG_MSG("signal daemon started(%p)\n", curthread); + + curthread->name = strdup("signal thread"); + crit = _kse_critical_enter(); + curkse = _get_curkse(); + + /* + * Daemon thread is a bound thread and we must be created with + * all signals masked + */ +#if 0 + SIGFILLSET(set); + __sys_sigprocmask(SIG_SETMASK, &set, NULL); +#endif + __sys_sigpending(&set); + ts.tv_sec = 0; + ts.tv_nsec = 0; + while (1) { + KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock); + _thr_proc_sigpending = set; + KSE_LOCK_RELEASE(curkse, &_thread_signal_lock); + for (i = 1; i <= _SIG_MAXSIG; i++) { + if (SIGISMEMBER(set, i) != 0) + _thr_sig_dispatch(curkse, i, + NULL /* no siginfo */); + } + ts.tv_sec = 30; + ts.tv_nsec = 0; + curkse->k_kcb->kcb_kmbx.km_flags = + KMF_NOUPCALL | KMF_NOCOMPLETED | KMF_WAITSIGEVENT; + kse_release(&ts); + curkse->k_kcb->kcb_kmbx.km_flags = 0; + set = curkse->k_kcb->kcb_kmbx.km_sigscaught; + } + return (0); +} + + +/* Utility function to create signal daemon thread */ +int +_thr_start_sig_daemon(void) +{ + pthread_attr_t attr; + sigset_t sigset, oldset; + + SIGFILLSET(sigset); + pthread_sigmask(SIG_SETMASK, &sigset, &oldset); + pthread_attr_init(&attr); + pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); + attr->flags |= THR_SIGNAL_THREAD; + /* sigmask will be inherited */ + if (pthread_create(&_thr_sig_daemon, &attr, sig_daemon, NULL)) + PANIC("can not create signal daemon thread!\n"); + pthread_attr_destroy(&attr); + pthread_sigmask(SIG_SETMASK, &oldset, NULL); + return (0); +} + +/* + * This signal handler only delivers asynchronous signals. + * This must be called with upcalls disabled and without + * holding any locks. + */ +void +_thr_sig_dispatch(struct kse *curkse, int sig, siginfo_t *info) +{ + struct kse_mailbox *kmbx; + struct pthread *thread; + + DBG_MSG(">>> _thr_sig_dispatch(%d)\n", sig); + + /* Check if the signal requires a dump of thread information: */ + if (_thr_dump_enabled() && (sig == SIGINFO)) { + /* Dump thread information to file: */ + _thread_dump_info(); + } + + while ((thread = thr_sig_find(curkse, sig, info)) != NULL) { + /* + * Setup the target thread to receive the signal: + */ + DBG_MSG("Got signal %d, selecting thread %p\n", sig, thread); + KSE_SCHED_LOCK(curkse, thread->kseg); + if ((thread->state == PS_DEAD) || + (thread->state == PS_DEADLOCK) || + THR_IS_EXITING(thread) || THR_IS_SUSPENDED(thread)) { + KSE_SCHED_UNLOCK(curkse, thread->kseg); + _thr_ref_delete(NULL, thread); + } else if (SIGISMEMBER(thread->sigmask, sig)) { + KSE_SCHED_UNLOCK(curkse, thread->kseg); + _thr_ref_delete(NULL, thread); + } else { + kmbx = _thr_sig_add(thread, sig, info); + KSE_SCHED_UNLOCK(curkse, thread->kseg); + _thr_ref_delete(NULL, thread); + if (kmbx != NULL) + kse_wakeup(kmbx); + break; + } + } + DBG_MSG("<<< _thr_sig_dispatch\n"); +} + +#endif /* ! SYSTEM_SCOPE_ONLY */ + +static __inline int +sigprop(int sig) +{ + + if (sig > 0 && sig < NSIG) + return (sigproptbl[_SIG_IDX(sig)]); + return (0); +} + +typedef void (*ohandler)(int sig, int code, + struct sigcontext *scp, char *addr, __sighandler_t *catcher); + +void +_thr_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp) +{ + struct pthread_sigframe psf; + __siginfohandler_t *sigfunc; + struct pthread *curthread; + struct kse *curkse; + struct sigaction act; + int sa_flags, err_save; + + err_save = errno; + + DBG_MSG(">>> _thr_sig_handler(%d)\n", sig); + + curthread = _get_curthread(); + if (curthread == NULL) + PANIC("No current thread.\n"); + if (!(curthread->attr.flags & PTHREAD_SCOPE_SYSTEM)) + PANIC("Thread is not system scope.\n"); + if (curthread->flags & THR_FLAGS_EXITING) { + errno = err_save; + return; + } + + curkse = _get_curkse(); + /* + * If thread is in critical region or if thread is on + * the way of state transition, then latch signal into buffer. + */ + if (_kse_in_critical() || THR_IN_CRITICAL(curthread) || + curthread->state != PS_RUNNING) { + DBG_MSG(">>> _thr_sig_handler(%d) in critical\n", sig); + curthread->siginfo[sig-1] = *info; + curthread->check_pending = 1; + curkse->k_sigseqno++; + SIGADDSET(curthread->sigpend, sig); + /* + * If the kse is on the way to idle itself, but + * we have signal ready, we should prevent it + * to sleep, kernel will latch the wakeup request, + * so kse_release will return from kernel immediately. + */ + if (KSE_IS_IDLE(curkse)) + kse_wakeup(&curkse->k_kcb->kcb_kmbx); + errno = err_save; + return; + } + + /* Check if the signal requires a dump of thread information: */ + if (_thr_dump_enabled() && (sig == SIGINFO)) { + /* Dump thread information to file: */ + _thread_dump_info(); + } + + /* Check the threads previous state: */ + curthread->critical_count++; + if (curthread->sigbackout != NULL) + curthread->sigbackout((void *)curthread); + curthread->critical_count--; + thr_sigframe_save(curthread, &psf); + THR_ASSERT(!(curthread->sigbackout), "sigbackout was not cleared."); + + _kse_critical_enter(); + /* Get a fresh copy of signal mask */ + __sys_sigprocmask(SIG_BLOCK, NULL, &curthread->sigmask); + KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock); + sigfunc = _thread_sigact[sig - 1].sa_sigaction; + sa_flags = _thread_sigact[sig - 1].sa_flags; + if (sa_flags & SA_RESETHAND) { + act.sa_handler = SIG_DFL; + act.sa_flags = SA_RESTART; + SIGEMPTYSET(act.sa_mask); + __sys_sigaction(sig, &act, NULL); + __sys_sigaction(sig, NULL, &_thread_sigact[sig - 1]); + } + KSE_LOCK_RELEASE(curkse, &_thread_signal_lock); + _kse_critical_leave(&curthread->tcb->tcb_tmbx); + + /* Now invoke real handler */ + if (((__sighandler_t *)sigfunc != SIG_DFL) && + ((__sighandler_t *)sigfunc != SIG_IGN) && + (sigfunc != (__siginfohandler_t *)_thr_sig_handler)) { + if ((sa_flags & SA_SIGINFO) != 0 || info == NULL) + (*(sigfunc))(sig, info, ucp); + else { + ((ohandler)(*sigfunc))( + sig, info->si_code, (struct sigcontext *)ucp, + info->si_addr, (__sighandler_t *)sigfunc); + } + } else { + if ((__sighandler_t *)sigfunc == SIG_DFL) { + if (sigprop(sig) & SA_KILL) { + if (_kse_isthreaded()) + kse_thr_interrupt(NULL, + KSE_INTR_SIGEXIT, sig); + else + kill(getpid(), sig); + } +#ifdef NOTYET + else if (sigprop(sig) & SA_STOP) + kse_thr_interrupt(NULL, KSE_INTR_JOBSTOP, sig); +#endif + } + } + _kse_critical_enter(); + curthread->sigmask = ucp->uc_sigmask; + SIG_CANTMASK(curthread->sigmask); + _kse_critical_leave(&curthread->tcb->tcb_tmbx); + + thr_sigframe_restore(curthread, &psf); + + DBG_MSG("<<< _thr_sig_handler(%d)\n", sig); + + errno = err_save; +} + +struct sighandle_info { + __siginfohandler_t *sigfunc; + int sa_flags; + int sig; + siginfo_t *info; + ucontext_t *ucp; +}; + +static void handle_signal(struct pthread *curthread, + struct sighandle_info *shi); +static void handle_signal_altstack(struct pthread *curthread, + struct sighandle_info *shi); + +/* Must be called with signal lock and schedule lock held in order */ +static void +thr_sig_invoke_handler(struct pthread *curthread, int sig, siginfo_t *info, + ucontext_t *ucp) +{ + __siginfohandler_t *sigfunc; + sigset_t sigmask; + int sa_flags; + int onstack; + struct sigaction act; + struct kse *curkse; + struct sighandle_info shi; + + /* + * Invoke the signal handler without going through the scheduler: + */ + DBG_MSG("Got signal %d, calling handler for current thread %p\n", + sig, curthread); + + if (!_kse_in_critical()) + PANIC("thr_sig_invoke_handler without in critical\n"); + curkse = curthread->kse; + /* + * Check that a custom handler is installed and if + * the signal is not blocked: + */ + sigfunc = _thread_sigact[sig - 1].sa_sigaction; + sa_flags = _thread_sigact[sig - 1].sa_flags; + sigmask = curthread->sigmask; + SIGSETOR(curthread->sigmask, _thread_sigact[sig - 1].sa_mask); + if (!(sa_flags & (SA_NODEFER | SA_RESETHAND))) + SIGADDSET(curthread->sigmask, sig); + if ((sig != SIGILL) && (sa_flags & SA_RESETHAND)) { + act.sa_handler = SIG_DFL; + act.sa_flags = SA_RESTART; + SIGEMPTYSET(act.sa_mask); + __sys_sigaction(sig, &act, NULL); + __sys_sigaction(sig, NULL, &_thread_sigact[sig - 1]); + } + KSE_LOCK_RELEASE(curkse, &_thread_signal_lock); + KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); + /* + * We are processing buffered signals, synchronize working + * signal mask into kernel. + */ + if (curthread->attr.flags & PTHREAD_SCOPE_SYSTEM) + __sys_sigprocmask(SIG_SETMASK, &curthread->sigmask, NULL); + onstack = _thr_sigonstack(&sigfunc); + ucp->uc_stack = curthread->sigstk; + ucp->uc_stack.ss_flags = (curthread->sigstk.ss_flags & SS_DISABLE) + ? SS_DISABLE : ((onstack) ? SS_ONSTACK : 0); + if (curthread->oldsigmask) { + ucp->uc_sigmask = *(curthread->oldsigmask); + curthread->oldsigmask = NULL; + } else + ucp->uc_sigmask = sigmask; + shi.sigfunc = sigfunc; + shi.sig = sig; + shi.sa_flags = sa_flags; + shi.info = info; + shi.ucp = ucp; + if ((curthread->sigstk.ss_flags & SS_DISABLE) == 0) { + /* Deliver signal on alternative stack */ + if (sa_flags & SA_ONSTACK && !onstack) + handle_signal_altstack(curthread, &shi); + else + handle_signal(curthread, &shi); + } else { + handle_signal(curthread, &shi); + } + + _kse_critical_enter(); + /* Don't trust after critical leave/enter */ + curkse = curthread->kse; + + /* + * Restore the thread's signal mask. + */ + curthread->sigmask = ucp->uc_sigmask; + SIG_CANTMASK(curthread->sigmask); + if (curthread->attr.flags & PTHREAD_SCOPE_SYSTEM) + __sys_sigprocmask(SIG_SETMASK, &ucp->uc_sigmask, NULL); + KSE_SCHED_LOCK(curkse, curkse->k_kseg); + KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock); + + DBG_MSG("Got signal %d, handler returned %p\n", sig, curthread); +} + +static void +handle_signal(struct pthread *curthread, struct sighandle_info *shi) +{ + _kse_critical_leave(&curthread->tcb->tcb_tmbx); + + /* Check if the signal requires a dump of thread information: */ + if (_thr_dump_enabled() && (shi->sig == SIGINFO)) { + /* Dump thread information to file: */ + _thread_dump_info(); + } + + if (((__sighandler_t *)shi->sigfunc != SIG_DFL) && + ((__sighandler_t *)shi->sigfunc != SIG_IGN)) { + if ((shi->sa_flags & SA_SIGINFO) != 0 || shi->info == NULL) + (*(shi->sigfunc))(shi->sig, shi->info, shi->ucp); + else { + ((ohandler)(*shi->sigfunc))( + shi->sig, shi->info->si_code, + (struct sigcontext *)shi->ucp, + shi->info->si_addr, + (__sighandler_t *)shi->sigfunc); + } + } else { + if ((__sighandler_t *)shi->sigfunc == SIG_DFL) { + if (sigprop(shi->sig) & SA_KILL) { + if (_kse_isthreaded()) + kse_thr_interrupt(NULL, + KSE_INTR_SIGEXIT, shi->sig); + else + kill(getpid(), shi->sig); + } +#ifdef NOTYET + else if (sigprop(shi->sig) & SA_STOP) + kse_thr_interrupt(NULL, KSE_INTR_JOBSTOP, + shi->sig); +#endif + } + } +} + +static void +handle_signal_wrapper(struct pthread *curthread, ucontext_t *ret_uc, + struct sighandle_info *shi) +{ + shi->ucp->uc_stack.ss_flags = SS_ONSTACK; + handle_signal(curthread, shi); + if (curthread->attr.flags & PTHREAD_SCOPE_SYSTEM) + setcontext(ret_uc); + else { + /* Work around for ia64, THR_SETCONTEXT does not work */ + _kse_critical_enter(); + curthread->tcb->tcb_tmbx.tm_context = *ret_uc; + _thread_switch(curthread->kse->k_kcb, curthread->tcb, 1); + /* THR_SETCONTEXT */ + } +} + +/* + * Jump to stack set by sigaltstack before invoking signal handler + */ +static void +handle_signal_altstack(struct pthread *curthread, struct sighandle_info *shi) +{ + volatile int once; + ucontext_t uc1, *uc2; + + THR_ASSERT(_kse_in_critical(), "Not in critical"); + + once = 0; + THR_GETCONTEXT(&uc1); + if (once == 0) { + once = 1; + /* XXX + * We are still in critical region, it is safe to operate thread + * context + */ + uc2 = &curthread->tcb->tcb_tmbx.tm_context; + uc2->uc_stack = curthread->sigstk; + makecontext(uc2, (void (*)(void))handle_signal_wrapper, + 3, curthread, &uc1, shi); + if (curthread->attr.flags & PTHREAD_SCOPE_SYSTEM) + setcontext(uc2); + else { + _thread_switch(curthread->kse->k_kcb, curthread->tcb, 1); + /* THR_SETCONTEXT(uc2); */ + } + } +} + +int +_thr_getprocsig(int sig, siginfo_t *siginfo) +{ + kse_critical_t crit; + struct kse *curkse; + int ret; + + DBG_MSG(">>> _thr_getprocsig\n"); + + crit = _kse_critical_enter(); + curkse = _get_curkse(); + KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock); + ret = _thr_getprocsig_unlocked(sig, siginfo); + KSE_LOCK_RELEASE(curkse, &_thread_signal_lock); + _kse_critical_leave(crit); + + DBG_MSG("<<< _thr_getprocsig\n"); + return (ret); +} + +int +_thr_getprocsig_unlocked(int sig, siginfo_t *siginfo) +{ + sigset_t sigset; + struct timespec ts; + + /* try to retrieve signal from kernel */ + SIGEMPTYSET(sigset); + SIGADDSET(sigset, sig); + ts.tv_sec = 0; + ts.tv_nsec = 0; + SIGDELSET(_thr_proc_sigpending, sig); + if (__sys_sigtimedwait(&sigset, siginfo, &ts) > 0) + return (sig); + return (0); +} + +#ifndef SYSTEM_SCOPE_ONLY +/* + * Find a thread that can handle the signal. This must be called + * with upcalls disabled. + */ +struct pthread * +thr_sig_find(struct kse *curkse, int sig, siginfo_t *info) +{ + struct kse_mailbox *kmbx = NULL; + struct pthread *pthread; + struct pthread *suspended_thread, *signaled_thread; + __siginfohandler_t *sigfunc; + siginfo_t si; + + DBG_MSG("Looking for thread to handle signal %d\n", sig); + + /* + * Enter a loop to look for threads that have the signal + * unmasked. POSIX specifies that a thread in a sigwait + * will get the signal over any other threads. Second + * preference will be threads in in a sigsuspend. Third + * preference will be the current thread. If none of the + * above, then the signal is delivered to the first thread + * that is found. Note that if a custom handler is not + * installed, the signal only affects threads in sigwait. + */ + suspended_thread = NULL; + signaled_thread = NULL; + + KSE_LOCK_ACQUIRE(curkse, &_thread_list_lock); + TAILQ_FOREACH(pthread, &_thread_list, tle) { + if (pthread == _thr_sig_daemon) + continue; + /* Signal delivering to bound thread is done by kernel */ + if (pthread->attr.flags & PTHREAD_SCOPE_SYSTEM) + continue; + /* Take the scheduling lock. */ + KSE_SCHED_LOCK(curkse, pthread->kseg); + if ((pthread->state == PS_DEAD) || + (pthread->state == PS_DEADLOCK) || + THR_IS_EXITING(pthread) || + THR_IS_SUSPENDED(pthread)) { + ; /* Skip this thread. */ + } else if (pthread->state == PS_SIGWAIT && + SIGISMEMBER(*(pthread->data.sigwait->waitset), sig)) { + /* + * retrieve signal from kernel, if it is job control + * signal, and sigaction is SIG_DFL, then we will + * be stopped in kernel, we hold lock here, but that + * does not matter, because that's job control, and + * whole process should be stopped. + */ + if (_thr_getprocsig(sig, &si)) { + DBG_MSG("Waking thread %p in sigwait" + " with signal %d\n", pthread, sig); + /* where to put siginfo ? */ + *(pthread->data.sigwait->siginfo) = si; + kmbx = _thr_setrunnable_unlocked(pthread); + } + KSE_SCHED_UNLOCK(curkse, pthread->kseg); + /* + * POSIX doesn't doesn't specify which thread + * will get the signal if there are multiple + * waiters, so we give it to the first thread + * we find. + * + * Do not attempt to deliver this signal + * to other threads and do not add the signal + * to the process pending set. + */ + KSE_LOCK_RELEASE(curkse, &_thread_list_lock); + if (kmbx != NULL) + kse_wakeup(kmbx); + if (suspended_thread != NULL) + _thr_ref_delete(NULL, suspended_thread); + if (signaled_thread != NULL) + _thr_ref_delete(NULL, signaled_thread); + return (NULL); + } else if (!SIGISMEMBER(pthread->sigmask, sig)) { + /* + * If debugger is running, we don't quick exit, + * and give it a chance to check the signal. + */ + if (_libkse_debug == 0) { + sigfunc = _thread_sigact[sig - 1].sa_sigaction; + if ((__sighandler_t *)sigfunc == SIG_DFL) { + if (sigprop(sig) & SA_KILL) { + kse_thr_interrupt(NULL, + KSE_INTR_SIGEXIT, sig); + /* Never reach */ + } + } + } + if (pthread->state == PS_SIGSUSPEND) { + if (suspended_thread == NULL) { + suspended_thread = pthread; + suspended_thread->refcount++; + } + } else if (signaled_thread == NULL) { + signaled_thread = pthread; + signaled_thread->refcount++; + } + } + KSE_SCHED_UNLOCK(curkse, pthread->kseg); + } + KSE_LOCK_RELEASE(curkse, &_thread_list_lock); + + if (suspended_thread != NULL) { + pthread = suspended_thread; + if (signaled_thread) + _thr_ref_delete(NULL, signaled_thread); + } else if (signaled_thread) { + pthread = signaled_thread; + } else { + pthread = NULL; + } + return (pthread); +} +#endif /* ! SYSTEM_SCOPE_ONLY */ + +static inline void +build_siginfo(siginfo_t *info, int signo) +{ + bzero(info, sizeof(*info)); + info->si_signo = signo; + info->si_pid = _thr_pid; +} + +/* + * This is called by a thread when it has pending signals to deliver. + * It should only be called from the context of the thread. + */ +void +_thr_sig_rundown(struct pthread *curthread, ucontext_t *ucp) +{ + struct pthread_sigframe psf; + siginfo_t siginfo; + int i, err_save; + kse_critical_t crit; + struct kse *curkse; + sigset_t sigmask; + + err_save = errno; + + DBG_MSG(">>> thr_sig_rundown (%p)\n", curthread); + + /* Check the threads previous state: */ + curthread->critical_count++; + if (curthread->sigbackout != NULL) + curthread->sigbackout((void *)curthread); + curthread->critical_count--; + + THR_ASSERT(!(curthread->sigbackout), "sigbackout was not cleared."); + THR_ASSERT((curthread->state == PS_RUNNING), "state is not PS_RUNNING"); + + thr_sigframe_save(curthread, &psf); + /* + * Lower the priority before calling the handler in case + * it never returns (longjmps back): + */ + crit = _kse_critical_enter(); + curkse = curthread->kse; + KSE_SCHED_LOCK(curkse, curkse->k_kseg); + KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock); + curthread->active_priority &= ~THR_SIGNAL_PRIORITY; + SIGFILLSET(sigmask); + while (1) { + /* + * For bound thread, we mask all signals and get a fresh + * copy of signal mask from kernel + */ + if (curthread->attr.flags & PTHREAD_SCOPE_SYSTEM) { + __sys_sigprocmask(SIG_SETMASK, &sigmask, + &curthread->sigmask); + } + for (i = 1; i <= _SIG_MAXSIG; i++) { + if (SIGISMEMBER(curthread->sigmask, i)) + continue; + if (SIGISMEMBER(curthread->sigpend, i)) { + SIGDELSET(curthread->sigpend, i); + siginfo = curthread->siginfo[i-1]; + break; + } + if (!(curthread->attr.flags & PTHREAD_SCOPE_SYSTEM) + && SIGISMEMBER(_thr_proc_sigpending, i)) { + if (_thr_getprocsig_unlocked(i, &siginfo)) + break; + } + } + if (i <= _SIG_MAXSIG) + thr_sig_invoke_handler(curthread, i, &siginfo, ucp); + else { + if (curthread->attr.flags & PTHREAD_SCOPE_SYSTEM) { + __sys_sigprocmask(SIG_SETMASK, + &curthread->sigmask, NULL); + } + break; + } + } + + /* Don't trust after signal handling */ + curkse = curthread->kse; + KSE_LOCK_RELEASE(curkse, &_thread_signal_lock); + KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); + _kse_critical_leave(&curthread->tcb->tcb_tmbx); + /* repost masked signal to kernel, it hardly happens in real world */ + if ((curthread->attr.flags & PTHREAD_SCOPE_SYSTEM) && + !SIGISEMPTY(curthread->sigpend)) { /* dirty read */ + __sys_sigprocmask(SIG_SETMASK, &sigmask, &curthread->sigmask); + for (i = 1; i <= _SIG_MAXSIG; ++i) { + if (SIGISMEMBER(curthread->sigpend, i)) { + SIGDELSET(curthread->sigpend, i); + if (!_kse_isthreaded()) + kill(getpid(), i); + else + kse_thr_interrupt( + &curthread->tcb->tcb_tmbx, + KSE_INTR_SENDSIG, + i); + } + } + __sys_sigprocmask(SIG_SETMASK, &curthread->sigmask, NULL); + } + DBG_MSG("<<< thr_sig_rundown (%p)\n", curthread); + + thr_sigframe_restore(curthread, &psf); + errno = err_save; +} + +/* + * This checks pending signals for the current thread. It should be + * called whenever a thread changes its signal mask. Note that this + * is called from a thread (using its stack). + * + * XXX - We might want to just check to see if there are pending + * signals for the thread here, but enter the UTS scheduler + * to actually install the signal handler(s). + */ +void +_thr_sig_check_pending(struct pthread *curthread) +{ + ucontext_t uc; + volatile int once; + int errsave; + + /* + * If the thread is in critical region, delay processing signals. + * If the thread state is not PS_RUNNING, it might be switching + * into UTS and but a THR_LOCK_RELEASE saw check_pending, and it + * goes here, in the case we delay processing signals, lets UTS + * process complicated things, normally UTS will call _thr_sig_add + * to resume the thread, so we needn't repeat doing it here. + */ + if (THR_IN_CRITICAL(curthread) || curthread->state != PS_RUNNING) + return; + + errsave = errno; + once = 0; + THR_GETCONTEXT(&uc); + if (once == 0) { + once = 1; + curthread->check_pending = 0; + _thr_sig_rundown(curthread, &uc); + } + errno = errsave; +} + +/* + * Perform thread specific actions in response to a signal. + * This function is only called if there is a handler installed + * for the signal, and if the target thread has the signal + * unmasked. + * + * This must be called with the thread's scheduling lock held. + */ +struct kse_mailbox * +_thr_sig_add(struct pthread *pthread, int sig, siginfo_t *info) +{ + siginfo_t siginfo; + struct kse *curkse; + struct kse_mailbox *kmbx = NULL; + struct pthread *curthread = _get_curthread(); + int restart; + int suppress_handler = 0; + int fromproc = 0; + __sighandler_t *sigfunc; + + DBG_MSG(">>> _thr_sig_add %p (%d)\n", pthread, sig); + + curkse = _get_curkse(); + restart = _thread_sigact[sig - 1].sa_flags & SA_RESTART; + sigfunc = _thread_sigact[sig - 1].sa_handler; + fromproc = (curthread == _thr_sig_daemon); + + if (pthread->state == PS_DEAD || pthread->state == PS_DEADLOCK || + pthread->state == PS_STATE_MAX) + return (NULL); /* return false */ + + if ((pthread->attr.flags & PTHREAD_SCOPE_SYSTEM) && + (curthread != pthread)) { + PANIC("Please use _thr_send_sig for bound thread"); + return (NULL); + } + + if (pthread->state != PS_SIGWAIT && + SIGISMEMBER(pthread->sigmask, sig)) { + /* signal is masked, just add signal to thread. */ + if (!fromproc) { + SIGADDSET(pthread->sigpend, sig); + if (info == NULL) + build_siginfo(&pthread->siginfo[sig-1], sig); + else if (info != &pthread->siginfo[sig-1]) + memcpy(&pthread->siginfo[sig-1], info, + sizeof(*info)); + } else { + if (!_thr_getprocsig(sig, &pthread->siginfo[sig-1])) + return (NULL); + SIGADDSET(pthread->sigpend, sig); + } + } + else { + /* if process signal not exists, just return */ + if (fromproc) { + if (!_thr_getprocsig(sig, &siginfo)) + return (NULL); + info = &siginfo; + } + + if (pthread->state != PS_SIGWAIT && sigfunc == SIG_DFL && + (sigprop(sig) & SA_KILL)) { + kse_thr_interrupt(NULL, KSE_INTR_SIGEXIT, sig); + /* Never reach */ + } + + /* + * Process according to thread state: + */ + switch (pthread->state) { + case PS_DEAD: + case PS_DEADLOCK: + case PS_STATE_MAX: + return (NULL); /* XXX return false */ + case PS_LOCKWAIT: + case PS_SUSPENDED: + /* + * You can't call a signal handler for threads in these + * states. + */ + suppress_handler = 1; + break; + case PS_RUNNING: + if ((pthread->flags & THR_FLAGS_IN_RUNQ)) { + THR_RUNQ_REMOVE(pthread); + pthread->active_priority |= THR_SIGNAL_PRIORITY; + THR_RUNQ_INSERT_TAIL(pthread); + } else { + /* Possible not in RUNQ and has curframe ? */ + pthread->active_priority |= THR_SIGNAL_PRIORITY; + } + break; + /* + * States which cannot be interrupted but still require the + * signal handler to run: + */ + case PS_COND_WAIT: + case PS_MUTEX_WAIT: + break; + + case PS_SLEEP_WAIT: + /* + * Unmasked signals always cause sleep to terminate + * early regardless of SA_RESTART: + */ + pthread->interrupted = 1; + break; + + case PS_JOIN: + break; + + case PS_SIGSUSPEND: + pthread->interrupted = 1; + break; + + case PS_SIGWAIT: + if (info == NULL) + build_siginfo(&pthread->siginfo[sig-1], sig); + else if (info != &pthread->siginfo[sig-1]) + memcpy(&pthread->siginfo[sig-1], info, + sizeof(*info)); + /* + * The signal handler is not called for threads in + * SIGWAIT. + */ + suppress_handler = 1; + /* Wake up the thread if the signal is not blocked. */ + if (SIGISMEMBER(*(pthread->data.sigwait->waitset), sig)) { + /* Return the signal number: */ + *(pthread->data.sigwait->siginfo) = pthread->siginfo[sig-1]; + /* Make the thread runnable: */ + kmbx = _thr_setrunnable_unlocked(pthread); + } else { + /* Increment the pending signal count. */ + SIGADDSET(pthread->sigpend, sig); + if (!SIGISMEMBER(pthread->sigmask, sig)) { + if (sigfunc == SIG_DFL && + sigprop(sig) & SA_KILL) { + kse_thr_interrupt(NULL, + KSE_INTR_SIGEXIT, + sig); + /* Never reach */ + } + pthread->check_pending = 1; + pthread->interrupted = 1; + kmbx = _thr_setrunnable_unlocked(pthread); + } + } + return (kmbx); + } + + SIGADDSET(pthread->sigpend, sig); + if (info == NULL) + build_siginfo(&pthread->siginfo[sig-1], sig); + else if (info != &pthread->siginfo[sig-1]) + memcpy(&pthread->siginfo[sig-1], info, sizeof(*info)); + pthread->check_pending = 1; + if (!(pthread->attr.flags & PTHREAD_SCOPE_SYSTEM) && + (pthread->blocked != 0) && !THR_IN_CRITICAL(pthread)) + kse_thr_interrupt(&pthread->tcb->tcb_tmbx, + restart ? KSE_INTR_RESTART : KSE_INTR_INTERRUPT, 0); + if (suppress_handler == 0) { + /* + * Setup a signal frame and save the current threads + * state: + */ + if (pthread->state != PS_RUNNING) { + if (pthread->flags & THR_FLAGS_IN_RUNQ) + THR_RUNQ_REMOVE(pthread); + pthread->active_priority |= THR_SIGNAL_PRIORITY; + kmbx = _thr_setrunnable_unlocked(pthread); + } + } + } + return (kmbx); +} + +/* + * Send a signal to a specific thread (ala pthread_kill): + */ +void +_thr_sig_send(struct pthread *pthread, int sig) +{ + struct pthread *curthread = _get_curthread(); + struct kse_mailbox *kmbx; + + if (pthread->attr.flags & PTHREAD_SCOPE_SYSTEM) { + kse_thr_interrupt(&pthread->tcb->tcb_tmbx, KSE_INTR_SENDSIG, sig); + return; + } + + /* Lock the scheduling queue of the target thread. */ + THR_SCHED_LOCK(curthread, pthread); + if (_thread_sigact[sig - 1].sa_handler != SIG_IGN) { + kmbx = _thr_sig_add(pthread, sig, NULL); + /* Add a preemption point. */ + if (kmbx == NULL && (curthread->kseg == pthread->kseg) && + (pthread->active_priority > curthread->active_priority)) + curthread->critical_yield = 1; + THR_SCHED_UNLOCK(curthread, pthread); + if (kmbx != NULL) + kse_wakeup(kmbx); + /* XXX + * If thread sent signal to itself, check signals now. + * It is not really needed, _kse_critical_leave should + * have already checked signals. + */ + if (pthread == curthread && curthread->check_pending) + _thr_sig_check_pending(curthread); + + } else { + THR_SCHED_UNLOCK(curthread, pthread); + } +} + +static inline void +thr_sigframe_restore(struct pthread *curthread, struct pthread_sigframe *psf) +{ + kse_critical_t crit; + struct kse *curkse; + + THR_THREAD_LOCK(curthread, curthread); + curthread->cancelflags = psf->psf_cancelflags; + crit = _kse_critical_enter(); + curkse = curthread->kse; + KSE_SCHED_LOCK(curkse, curthread->kseg); + curthread->flags = psf->psf_flags; + curthread->interrupted = psf->psf_interrupted; + curthread->timeout = psf->psf_timeout; + curthread->data = psf->psf_wait_data; + curthread->wakeup_time = psf->psf_wakeup_time; + curthread->continuation = psf->psf_continuation; + KSE_SCHED_UNLOCK(curkse, curthread->kseg); + _kse_critical_leave(crit); + THR_THREAD_UNLOCK(curthread, curthread); +} + +static inline void +thr_sigframe_save(struct pthread *curthread, struct pthread_sigframe *psf) +{ + kse_critical_t crit; + struct kse *curkse; + + THR_THREAD_LOCK(curthread, curthread); + psf->psf_cancelflags = curthread->cancelflags; + crit = _kse_critical_enter(); + curkse = curthread->kse; + KSE_SCHED_LOCK(curkse, curthread->kseg); + /* This has to initialize all members of the sigframe. */ + psf->psf_flags = (curthread->flags & (THR_FLAGS_PRIVATE | THR_FLAGS_EXITING)); + psf->psf_interrupted = curthread->interrupted; + psf->psf_timeout = curthread->timeout; + psf->psf_wait_data = curthread->data; + psf->psf_wakeup_time = curthread->wakeup_time; + psf->psf_continuation = curthread->continuation; + KSE_SCHED_UNLOCK(curkse, curthread->kseg); + _kse_critical_leave(crit); + THR_THREAD_UNLOCK(curthread, curthread); +} + +void +_thr_signal_init(void) +{ + struct sigaction act; + __siginfohandler_t *sigfunc; + int i; + sigset_t sigset; + + SIGFILLSET(sigset); + __sys_sigprocmask(SIG_SETMASK, &sigset, &_thr_initial->sigmask); + /* Enter a loop to get the existing signal status: */ + for (i = 1; i <= _SIG_MAXSIG; i++) { + /* Get the signal handler details: */ + if (__sys_sigaction(i, NULL, &_thread_sigact[i - 1]) != 0) { + /* + * Abort this process if signal + * initialisation fails: + */ + PANIC("Cannot read signal handler info"); + } + /* Intall wrapper if handler was set */ + sigfunc = _thread_sigact[i - 1].sa_sigaction; + if (((__sighandler_t *)sigfunc) != SIG_DFL && + ((__sighandler_t *)sigfunc) != SIG_IGN) { + act = _thread_sigact[i - 1]; + act.sa_flags |= SA_SIGINFO; + act.sa_sigaction = + (__siginfohandler_t *)_thr_sig_handler; + __sys_sigaction(i, &act, NULL); + } + } + if (_thr_dump_enabled()) { + /* + * Install the signal handler for SIGINFO. It isn't + * really needed, but it is nice to have for debugging + * purposes. + */ + _thread_sigact[SIGINFO - 1].sa_flags = SA_SIGINFO | SA_RESTART; + SIGEMPTYSET(act.sa_mask); + act.sa_flags = SA_SIGINFO | SA_RESTART; + act.sa_sigaction = (__siginfohandler_t *)&_thr_sig_handler; + if (__sys_sigaction(SIGINFO, &act, NULL) != 0) { + __sys_sigprocmask(SIG_SETMASK, &_thr_initial->sigmask, + NULL); + /* + * Abort this process if signal initialisation fails: + */ + PANIC("Cannot initialize signal handler"); + } + } + __sys_sigprocmask(SIG_SETMASK, &_thr_initial->sigmask, NULL); + __sys_sigaltstack(NULL, &_thr_initial->sigstk); +} + +void +_thr_signal_deinit(void) +{ + int i; + struct pthread *curthread = _get_curthread(); + + /* Clear process pending signals. */ + sigemptyset(&_thr_proc_sigpending); + + /* Enter a loop to get the existing signal status: */ + for (i = 1; i <= _SIG_MAXSIG; i++) { + /* Check for signals which cannot be trapped: */ + if (i == SIGKILL || i == SIGSTOP) { + } + + /* Set the signal handler details: */ + else if (__sys_sigaction(i, &_thread_sigact[i - 1], + NULL) != 0) { + /* + * Abort this process if signal + * initialisation fails: + */ + PANIC("Cannot set signal handler info"); + } + } + __sys_sigaltstack(&curthread->sigstk, NULL); +} + diff --git a/lib/libpthread/thread/thr_sigaction.c b/lib/libpthread/thread/thr_sigaction.c new file mode 100644 index 0000000..7ee0ce6 --- /dev/null +++ b/lib/libpthread/thread/thr_sigaction.c @@ -0,0 +1,120 @@ +/* + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <signal.h> +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_sigaction); +LT10_COMPAT_DEFAULT(sigaction); + +__weak_reference(_sigaction, sigaction); + +int +_sigaction(int sig, const struct sigaction * act, struct sigaction * oact) +{ + int ret = 0; + int err = 0; + struct sigaction newact, oldact; + struct pthread *curthread; + kse_critical_t crit; + + /* Check if the signal number is out of range: */ + if (sig < 1 || sig > _SIG_MAXSIG) { + /* Return an invalid argument: */ + errno = EINVAL; + ret = -1; + } else { + if (act) + newact = *act; + + crit = _kse_critical_enter(); + curthread = _get_curthread(); + KSE_LOCK_ACQUIRE(curthread->kse, &_thread_signal_lock); + + oldact = _thread_sigact[sig - 1]; + + /* Check if a signal action was supplied: */ + if (act != NULL) { + /* Set the new signal handler: */ + _thread_sigact[sig - 1] = newact; + } + + /* + * Check if the kernel needs to be advised of a change + * in signal action: + */ + if (act != NULL && sig != SIGINFO) { + + newact.sa_flags |= SA_SIGINFO; + + /* + * Check if the signal handler is being set to + * the default or ignore handlers: + */ + if (newact.sa_handler != SIG_DFL && + newact.sa_handler != SIG_IGN) { + /* + * Specify the thread kernel signal + * handler: + */ + newact.sa_handler = (void (*) ())_thr_sig_handler; + } + /* Change the signal action in the kernel: */ + if (__sys_sigaction(sig, &newact, NULL) != 0) { + _thread_sigact[sig - 1] = oldact; + /* errno is in kse, will copy it to thread */ + err = errno; + ret = -1; + } + } + KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock); + _kse_critical_leave(crit); + /* + * Check if the existing signal action structure contents are + * to be returned: + */ + if (oact != NULL) { + /* Return the existing signal action contents: */ + *oact = oldact; + } + if (ret != 0) { + /* Return errno to thread */ + errno = err; + } + } + + /* Return the completion status: */ + return (ret); +} diff --git a/lib/libpthread/thread/thr_sigaltstack.c b/lib/libpthread/thread/thr_sigaltstack.c new file mode 100644 index 0000000..8ebbdee --- /dev/null +++ b/lib/libpthread/thread/thr_sigaltstack.c @@ -0,0 +1,110 @@ +/*- + * Copyright (c) 2003 David Xu <davidxu@freebsd.org> + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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> +__FBSDID("$FreeBSD$"); + +#include <errno.h> +#include <signal.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_sigaltstack); +LT10_COMPAT_DEFAULT(sigaltstack); + +__weak_reference(_sigaltstack, sigaltstack); + +int +_sigaltstack(stack_t *_ss, stack_t *_oss) +{ + struct pthread *curthread = _get_curthread(); + stack_t ss, oss; + int oonstack, errsave, ret; + kse_critical_t crit; + + if (curthread->attr.flags & PTHREAD_SCOPE_SYSTEM) { + crit = _kse_critical_enter(); + ret = __sys_sigaltstack(_ss, _oss); + errsave = errno; + /* Get a copy */ + if (ret == 0 && _ss != NULL) + curthread->sigstk = *_ss; + _kse_critical_leave(crit); + errno = errsave; + return (ret); + } + + if (_ss) + ss = *_ss; + if (_oss) + oss = *_oss; + + /* Should get and set stack in atomic way */ + crit = _kse_critical_enter(); + oonstack = _thr_sigonstack(&ss); + if (_oss != NULL) { + oss = curthread->sigstk; + oss.ss_flags = (curthread->sigstk.ss_flags & SS_DISABLE) + ? SS_DISABLE : ((oonstack) ? SS_ONSTACK : 0); + } + + if (_ss != NULL) { + if (oonstack) { + _kse_critical_leave(crit); + errno = EPERM; + return (-1); + } + if ((ss.ss_flags & ~SS_DISABLE) != 0) { + _kse_critical_leave(crit); + errno = EINVAL; + return (-1); + } + if (!(ss.ss_flags & SS_DISABLE)) { + if (ss.ss_size < MINSIGSTKSZ) { + _kse_critical_leave(crit); + errno = ENOMEM; + return (-1); + } + curthread->sigstk = ss; + } else { + curthread->sigstk.ss_flags |= SS_DISABLE; + } + } + _kse_critical_leave(crit); + if (_oss != NULL) + *_oss = oss; + return (0); +} + +int +_thr_sigonstack(void *sp) +{ + struct pthread *curthread = _get_curthread(); + + return ((curthread->sigstk.ss_flags & SS_DISABLE) == 0 ? + (((size_t)sp - (size_t)curthread->sigstk.ss_sp) < curthread->sigstk.ss_size) + : 0); +} + diff --git a/lib/libpthread/thread/thr_sigmask.c b/lib/libpthread/thread/thr_sigmask.c new file mode 100644 index 0000000..05f5ae1 --- /dev/null +++ b/lib/libpthread/thread/thr_sigmask.c @@ -0,0 +1,116 @@ +/* + * Copyright (c) 1997 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <sys/param.h> +#include <sys/types.h> +#include <sys/signalvar.h> +#include <errno.h> +#include <signal.h> +#include <string.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_sigmask); +LT10_COMPAT_DEFAULT(pthread_sigmask); + +__weak_reference(_pthread_sigmask, pthread_sigmask); + +int +_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) +{ + struct pthread *curthread = _get_curthread(); + sigset_t oldset, newset; + int ret; + + if (curthread->attr.flags & PTHREAD_SCOPE_SYSTEM) { + ret = __sys_sigprocmask(how, set, oset); + if (ret != 0) + ret = errno; + /* Get a fresh copy */ + __sys_sigprocmask(SIG_SETMASK, NULL, &curthread->sigmask); + return (ret); + } + + if (set) + newset = *set; + + THR_SCHED_LOCK(curthread, curthread); + + ret = 0; + if (oset != NULL) + /* Return the current mask: */ + oldset = curthread->sigmask; + + /* Check if a new signal set was provided by the caller: */ + if (set != NULL) { + /* Process according to what to do: */ + switch (how) { + /* Block signals: */ + case SIG_BLOCK: + /* Add signals to the existing mask: */ + SIGSETOR(curthread->sigmask, newset); + break; + + /* Unblock signals: */ + case SIG_UNBLOCK: + /* Clear signals from the existing mask: */ + SIGSETNAND(curthread->sigmask, newset); + break; + + /* Set the signal process mask: */ + case SIG_SETMASK: + /* Set the new mask: */ + curthread->sigmask = newset; + break; + + /* Trap invalid actions: */ + default: + /* Return an invalid argument: */ + ret = EINVAL; + break; + } + SIG_CANTMASK(curthread->sigmask); + THR_SCHED_UNLOCK(curthread, curthread); + + /* + * Run down any pending signals: + */ + if (ret == 0) + _thr_sig_check_pending(curthread); + } else + THR_SCHED_UNLOCK(curthread, curthread); + + if (ret == 0 && oset != NULL) + *oset = oldset; + return (ret); +} diff --git a/lib/libpthread/thread/thr_sigpending.c b/lib/libpthread/thread/thr_sigpending.c new file mode 100644 index 0000000..5c666bf --- /dev/null +++ b/lib/libpthread/thread/thr_sigpending.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 1999 Daniel Eischen <eischen@vigrid.com>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <sys/param.h> +#include <sys/types.h> +#include <sys/signalvar.h> +#include <signal.h> +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_sigpending); +LT10_COMPAT_DEFAULT(sigpending); + +__weak_reference(_sigpending, sigpending); + +int +_sigpending(sigset_t *set) +{ + struct pthread *curthread = _get_curthread(); + kse_critical_t crit; + sigset_t sigset; + int ret = 0; + + /* Check for a null signal set pointer: */ + if (set == NULL) { + /* Return an invalid argument: */ + ret = EINVAL; + } + else { + if (curthread->attr.flags & PTHREAD_SCOPE_SYSTEM) + return (__sys_sigpending(set)); + + crit = _kse_critical_enter(); + KSE_SCHED_LOCK(curthread->kse, curthread->kseg); + sigset = curthread->sigpend; + KSE_SCHED_UNLOCK(curthread->kse, curthread->kseg); + KSE_LOCK_ACQUIRE(curthread->kse, &_thread_signal_lock); + SIGSETOR(sigset, _thr_proc_sigpending); + KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock); + _kse_critical_leave(crit); + *set = sigset; + } + /* Return the completion status: */ + return (ret); +} diff --git a/lib/libpthread/thread/thr_sigprocmask.c b/lib/libpthread/thread/thr_sigprocmask.c new file mode 100644 index 0000000..d2a20dd --- /dev/null +++ b/lib/libpthread/thread/thr_sigprocmask.c @@ -0,0 +1,58 @@ +/* + * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <sys/param.h> +#include <sys/types.h> +#include <sys/signalvar.h> +#include <signal.h> +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_sigprocmask); +LT10_COMPAT_DEFAULT(sigprocmask); + +__weak_reference(_sigprocmask, sigprocmask); + +int +_sigprocmask(int how, const sigset_t *set, sigset_t *oset) +{ + int ret; + + ret = pthread_sigmask(how, set, oset); + if (ret) { + errno = ret; + ret = -1; + } + return (ret); +} diff --git a/lib/libpthread/thread/thr_sigsuspend.c b/lib/libpthread/thread/thr_sigsuspend.c new file mode 100644 index 0000000..2f3ed5d --- /dev/null +++ b/lib/libpthread/thread/thr_sigsuspend.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <sys/signalvar.h> +#include <errno.h> +#include <pthread.h> +#include <signal.h> +#include <string.h> + +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(__sigsuspend); +LT10_COMPAT_PRIVATE(_sigsuspend); +LT10_COMPAT_DEFAULT(sigsuspend); + +__weak_reference(__sigsuspend, sigsuspend); + +int +_sigsuspend(const sigset_t *set) +{ + struct pthread *curthread = _get_curthread(); + sigset_t oldmask, newmask, tempset; + int ret = -1; + + if (curthread->attr.flags & PTHREAD_SCOPE_SYSTEM) + return (__sys_sigsuspend(set)); + + /* Check if a new signal set was provided by the caller: */ + if (set != NULL) { + newmask = *set; + SIG_CANTMASK(newmask); + THR_LOCK_SWITCH(curthread); + + /* Save current sigmask: */ + oldmask = curthread->sigmask; + curthread->oldsigmask = &oldmask; + + /* Change the caller's mask: */ + curthread->sigmask = newmask; + tempset = curthread->sigpend; + SIGSETNAND(tempset, newmask); + if (SIGISEMPTY(tempset)) { + THR_SET_STATE(curthread, PS_SIGSUSPEND); + /* Wait for a signal: */ + _thr_sched_switch_unlocked(curthread); + } else { + curthread->check_pending = 1; + THR_UNLOCK_SWITCH(curthread); + /* check pending signal I can handle: */ + _thr_sig_check_pending(curthread); + } + if ((curthread->cancelflags & THR_CANCELLING) != 0) + curthread->oldsigmask = NULL; + else { + THR_ASSERT(curthread->oldsigmask == NULL, + "oldsigmask is not cleared"); + } + + /* Always return an interrupted error: */ + errno = EINTR; + } else { + /* Return an invalid argument error: */ + errno = EINVAL; + } + + /* Return the completion status: */ + return (ret); +} + +int +__sigsuspend(const sigset_t * set) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_cancel_enter(curthread); + ret = _sigsuspend(set); + _thr_cancel_leave(curthread, 1); + + return (ret); +} diff --git a/lib/libpthread/thread/thr_sigwait.c b/lib/libpthread/thread/thr_sigwait.c new file mode 100644 index 0000000..cd7ac22 --- /dev/null +++ b/lib/libpthread/thread/thr_sigwait.c @@ -0,0 +1,212 @@ +//depot/projects/kse/lib/libpthread/thread/thr_sigwait.c#1 - branch change 15154 (text+ko) +/* + * Copyright (c) 1997 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <signal.h> +#include <sys/param.h> +#include <sys/signalvar.h> +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(__sigwait); +LT10_COMPAT_PRIVATE(_sigwait); +LT10_COMPAT_DEFAULT(sigwait); +LT10_COMPAT_PRIVATE(__sigtimedwait); +LT10_COMPAT_PRIVATE(_sigtimedwait); +LT10_COMPAT_DEFAULT(sigtimedwait); +LT10_COMPAT_PRIVATE(__sigwaitinfo); +LT10_COMPAT_PRIVATE(_sigwaitinfo); +LT10_COMPAT_DEFAULT(sigwaitinfo); + +__weak_reference(__sigwait, sigwait); +__weak_reference(__sigtimedwait, sigtimedwait); +__weak_reference(__sigwaitinfo, sigwaitinfo); + +static int +lib_sigtimedwait(const sigset_t *set, siginfo_t *info, + const struct timespec * timeout) +{ + struct pthread *curthread = _get_curthread(); + int ret = 0; + int i; + struct sigwait_data waitdata; + sigset_t waitset; + kse_critical_t crit; + siginfo_t siginfo; + + if (curthread->attr.flags & PTHREAD_SCOPE_SYSTEM) { + if (info == NULL) + info = &siginfo; + return (__sys_sigtimedwait((sigset_t *)set, info, + (struct timespec *)timeout)); + } + + /* + * Initialize the set of signals that will be waited on: + */ + waitset = *set; + + /* These signals can't be waited on. */ + SIGDELSET(waitset, SIGKILL); + SIGDELSET(waitset, SIGSTOP); + + /* + * POSIX says that the _application_ must explicitly install + * a dummy handler for signals that are SIG_IGN in order + * to sigwait on them. Note that SIG_IGN signals are left in + * the mask because a subsequent sigaction could enable an + * ignored signal. + */ + + crit = _kse_critical_enter(); + KSE_SCHED_LOCK(curthread->kse, curthread->kseg); + for (i = 1; i <= _SIG_MAXSIG; ++i) { + if (SIGISMEMBER(waitset, i) && + SIGISMEMBER(curthread->sigpend, i)) { + SIGDELSET(curthread->sigpend, i); + siginfo = curthread->siginfo[i - 1]; + KSE_SCHED_UNLOCK(curthread->kse, + curthread->kseg); + _kse_critical_leave(crit); + ret = i; + goto OUT; + } + } + curthread->timeout = 0; + curthread->interrupted = 0; + _thr_set_timeout(timeout); + /* Wait for a signal: */ + siginfo.si_signo = 0; + waitdata.waitset = &waitset; + waitdata.siginfo = &siginfo; + curthread->data.sigwait = &waitdata; + THR_SET_STATE(curthread, PS_SIGWAIT); + _thr_sched_switch_unlocked(curthread); + /* + * Return the signal number to the caller: + */ + if (siginfo.si_signo > 0) { + ret = siginfo.si_signo; + } else { + if (curthread->interrupted) + errno = EINTR; + else if (curthread->timeout) + errno = EAGAIN; + ret = -1; + } + curthread->timeout = 0; + curthread->interrupted = 0; + /* + * Probably unnecessary, but since it's in a union struct + * we don't know how it could be used in the future. + */ + curthread->data.sigwait = NULL; + +OUT: + if (ret > 0 && info != NULL) + *info = siginfo; + + return (ret); +} + +int +__sigtimedwait(const sigset_t *set, siginfo_t *info, + const struct timespec * timeout) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_cancel_enter(curthread); + ret = lib_sigtimedwait(set, info, timeout); + _thr_cancel_leave(curthread, 1); + return (ret); +} + +int _sigtimedwait(const sigset_t *set, siginfo_t *info, + const struct timespec * timeout) +{ + return lib_sigtimedwait(set, info, timeout); +} + +int +__sigwaitinfo(const sigset_t *set, siginfo_t *info) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_cancel_enter(curthread); + ret = lib_sigtimedwait(set, info, NULL); + _thr_cancel_leave(curthread, 1); + return (ret); +} + +int +_sigwaitinfo(const sigset_t *set, siginfo_t *info) +{ + return lib_sigtimedwait(set, info, NULL); +} + +int +__sigwait(const sigset_t *set, int *sig) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_cancel_enter(curthread); + ret = lib_sigtimedwait(set, NULL, NULL); + if (ret > 0) { + *sig = ret; + ret = 0; + } else { + ret = errno; + } + _thr_cancel_leave(curthread, 1); + return (ret); +} + +int +_sigwait(const sigset_t *set, int *sig) +{ + int ret; + + ret = lib_sigtimedwait(set, NULL, NULL); + if (ret > 0) { + *sig = ret; + ret = 0; + } else { + ret = errno; + } + return (ret); +} + diff --git a/lib/libpthread/thread/thr_single_np.c b/lib/libpthread/thread/thr_single_np.c new file mode 100644 index 0000000..882f6aa --- /dev/null +++ b/lib/libpthread/thread/thr_single_np.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1996 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <pthread.h> +#include <pthread_np.h> + +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_single_np); +LT10_COMPAT_DEFAULT(pthread_single_np); + +__weak_reference(_pthread_single_np, pthread_single_np); + +int _pthread_single_np() +{ + + /* Enter single-threaded (non-POSIX) scheduling mode: */ + pthread_suspend_all_np(); + /* + * XXX - Do we want to do this? + * __is_threaded = 0; + */ + return (0); +} diff --git a/lib/libpthread/thread/thr_sleep.c b/lib/libpthread/thread/thr_sleep.c new file mode 100644 index 0000000..0a11876 --- /dev/null +++ b/lib/libpthread/thread/thr_sleep.c @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>. + * 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(s), this list of conditions and the following disclaimer as + * the first lines of this file unmodified other than the possible + * addition of one or more copyright notices. + * 2. Redistributions in binary form must reproduce the above copyright + * notice(s), this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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. + * + * $FreeBSD$ + */ + +#include <unistd.h> +#include <pthread.h> +#include "thr_private.h" + +extern unsigned int __sleep(unsigned int); +extern int __usleep(useconds_t); + +LT10_COMPAT_PRIVATE(_sleep); +LT10_COMPAT_DEFAULT(sleep); +LT10_COMPAT_PRIVATE(_usleep); +LT10_COMPAT_DEFAULT(usleep); + +__weak_reference(_sleep, sleep); +__weak_reference(_usleep, usleep); + +unsigned int +_sleep(unsigned int seconds) +{ + struct pthread *curthread = _get_curthread(); + unsigned int ret; + + _thr_cancel_enter(curthread); + ret = __sleep(seconds); + _thr_cancel_leave(curthread, 1); + + return (ret); +} + +int +_usleep(useconds_t useconds) +{ + struct pthread *curthread = _get_curthread(); + unsigned int ret; + + _thr_cancel_enter(curthread); + ret = __usleep(useconds); + _thr_cancel_leave(curthread, 1); + + return (ret); +} diff --git a/lib/libpthread/thread/thr_spec.c b/lib/libpthread/thread/thr_spec.c new file mode 100644 index 0000000..f6e8861 --- /dev/null +++ b/lib/libpthread/thread/thr_spec.c @@ -0,0 +1,243 @@ +/* + * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <pthread.h> + +#include "thr_private.h" + + +struct pthread_key _thread_keytable[PTHREAD_KEYS_MAX]; + +/* + * XXX - This breaks the linker if LT10_COMPAT_DEFAULT doesn't + * also include a weak reference to the default symbol. + */ +LT10_COMPAT_PRIVATE(_thread_keytable); + +LT10_COMPAT_PRIVATE(_pthread_key_create); +LT10_COMPAT_DEFAULT(pthread_key_create); +LT10_COMPAT_PRIVATE(_pthread_key_delete); +LT10_COMPAT_DEFAULT(pthread_key_delete); +LT10_COMPAT_PRIVATE(_pthread_getspecific); +LT10_COMPAT_DEFAULT(pthread_getspecific); +LT10_COMPAT_PRIVATE(_pthread_setspecific); +LT10_COMPAT_DEFAULT(pthread_setspecific); + +__weak_reference(_pthread_key_create, pthread_key_create); +__weak_reference(_pthread_key_delete, pthread_key_delete); +__weak_reference(_pthread_getspecific, pthread_getspecific); +__weak_reference(_pthread_setspecific, pthread_setspecific); + + +int +_pthread_key_create(pthread_key_t *key, void (*destructor) (void *)) +{ + struct pthread *curthread = _get_curthread(); + int i; + + /* Lock the key table: */ + THR_LOCK_ACQUIRE(curthread, &_keytable_lock); + for (i = 0; i < PTHREAD_KEYS_MAX; i++) { + + if (_thread_keytable[i].allocated == 0) { + _thread_keytable[i].allocated = 1; + _thread_keytable[i].destructor = destructor; + _thread_keytable[i].seqno++; + + /* Unlock the key table: */ + THR_LOCK_RELEASE(curthread, &_keytable_lock); + *key = i; + return (0); + } + + } + /* Unlock the key table: */ + THR_LOCK_RELEASE(curthread, &_keytable_lock); + return (EAGAIN); +} + +int +_pthread_key_delete(pthread_key_t key) +{ + struct pthread *curthread = _get_curthread(); + int ret = 0; + + if ((unsigned int)key < PTHREAD_KEYS_MAX) { + /* Lock the key table: */ + THR_LOCK_ACQUIRE(curthread, &_keytable_lock); + + if (_thread_keytable[key].allocated) + _thread_keytable[key].allocated = 0; + else + ret = EINVAL; + + /* Unlock the key table: */ + THR_LOCK_RELEASE(curthread, &_keytable_lock); + } else + ret = EINVAL; + return (ret); +} + +void +_thread_cleanupspecific(void) +{ + struct pthread *curthread = _get_curthread(); + void (*destructor)( void *); + void *data = NULL; + int key; + int i; + + if (curthread->specific == NULL) + return; + + /* Lock the key table: */ + THR_LOCK_ACQUIRE(curthread, &_keytable_lock); + for (i = 0; (i < PTHREAD_DESTRUCTOR_ITERATIONS) && + (curthread->specific_data_count > 0); i++) { + for (key = 0; (key < PTHREAD_KEYS_MAX) && + (curthread->specific_data_count > 0); key++) { + destructor = NULL; + + if (_thread_keytable[key].allocated && + (curthread->specific[key].data != NULL)) { + if (curthread->specific[key].seqno == + _thread_keytable[key].seqno) { + data = (void *) + curthread->specific[key].data; + destructor = _thread_keytable[key].destructor; + } + curthread->specific[key].data = NULL; + curthread->specific_data_count--; + } + + /* + * If there is a destructore, call it + * with the key table entry unlocked: + */ + if (destructor != NULL) { + /* + * Don't hold the lock while calling the + * destructor: + */ + THR_LOCK_RELEASE(curthread, &_keytable_lock); + destructor(data); + THR_LOCK_ACQUIRE(curthread, &_keytable_lock); + } + } + } + THR_LOCK_RELEASE(curthread, &_keytable_lock); + free(curthread->specific); + curthread->specific = NULL; + if (curthread->specific_data_count > 0) + stderr_debug("Thread %p has exited with leftover " + "thread-specific data after %d destructor iterations\n", + curthread, PTHREAD_DESTRUCTOR_ITERATIONS); +} + +static inline struct pthread_specific_elem * +pthread_key_allocate_data(void) +{ + struct pthread_specific_elem *new_data; + + new_data = (struct pthread_specific_elem *) + malloc(sizeof(struct pthread_specific_elem) * PTHREAD_KEYS_MAX); + if (new_data != NULL) { + memset((void *) new_data, 0, + sizeof(struct pthread_specific_elem) * PTHREAD_KEYS_MAX); + } + return (new_data); +} + +int +_pthread_setspecific(pthread_key_t key, const void *value) +{ + struct pthread *pthread; + int ret = 0; + + /* Point to the running thread: */ + pthread = _get_curthread(); + + if ((pthread->specific) || + (pthread->specific = pthread_key_allocate_data())) { + if ((unsigned int)key < PTHREAD_KEYS_MAX) { + if (_thread_keytable[key].allocated) { + if (pthread->specific[key].data == NULL) { + if (value != NULL) + pthread->specific_data_count++; + } else if (value == NULL) + pthread->specific_data_count--; + pthread->specific[key].data = value; + pthread->specific[key].seqno = + _thread_keytable[key].seqno; + ret = 0; + } else + ret = EINVAL; + } else + ret = EINVAL; + } else + ret = ENOMEM; + return (ret); +} + +void * +_pthread_getspecific(pthread_key_t key) +{ + struct pthread *pthread; + void *data; + + /* Point to the running thread: */ + pthread = _get_curthread(); + + /* Check if there is specific data: */ + if (pthread->specific != NULL && (unsigned int)key < PTHREAD_KEYS_MAX) { + /* Check if this key has been used before: */ + if (_thread_keytable[key].allocated && + (pthread->specific[key].seqno == _thread_keytable[key].seqno)) { + /* Return the value: */ + data = (void *) pthread->specific[key].data; + } else { + /* + * This key has not been used before, so return NULL + * instead: + */ + data = NULL; + } + } else + /* No specific data has been created, so just return NULL: */ + data = NULL; + return (data); +} diff --git a/lib/libpthread/thread/thr_spinlock.c b/lib/libpthread/thread/thr_spinlock.c new file mode 100644 index 0000000..d187439 --- /dev/null +++ b/lib/libpthread/thread/thr_spinlock.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) 1997 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + * + */ + +#include <sys/types.h> +#include <machine/atomic.h> + +#include <libc_private.h> +#include "spinlock.h" +#include "thr_private.h" + +#define MAX_SPINLOCKS 72 + +struct spinlock_extra { + spinlock_t *owner; + pthread_mutex_t lock; +}; + +static void init_spinlock(spinlock_t *lck); + +static struct pthread_mutex_attr static_mutex_attr = + PTHREAD_MUTEXATTR_STATIC_INITIALIZER; +static pthread_mutexattr_t static_mattr = &static_mutex_attr; + +static pthread_mutex_t spinlock_static_lock; +static struct spinlock_extra extra[MAX_SPINLOCKS]; +static int spinlock_count = 0; +static int initialized = 0; + +LT10_COMPAT_PRIVATE(_spinlock); +LT10_COMPAT_PRIVATE(_spinlock_debug); +LT10_COMPAT_PRIVATE(_spinunlock); + +/* + * These are for compatability only. Spinlocks of this type + * are deprecated. + */ + +void +_spinunlock(spinlock_t *lck) +{ + struct spinlock_extra *extra; + + extra = (struct spinlock_extra *)lck->fname; + _pthread_mutex_unlock(&extra->lock); +} + +/* + * Lock a location for the running thread. Yield to allow other + * threads to run if this thread is blocked because the lock is + * not available. Note that this function does not sleep. It + * assumes that the lock will be available very soon. + */ +void +_spinlock(spinlock_t *lck) +{ + struct spinlock_extra *extra; + + if (!__isthreaded) + PANIC("Spinlock called when not threaded."); + if (!initialized) + PANIC("Spinlocks not initialized."); + /* + * Try to grab the lock and loop if another thread grabs + * it before we do. + */ + if (lck->fname == NULL) + init_spinlock(lck); + extra = (struct spinlock_extra *)lck->fname; + _pthread_mutex_lock(&extra->lock); +} + +/* + * Lock a location for the running thread. Yield to allow other + * threads to run if this thread is blocked because the lock is + * not available. Note that this function does not sleep. It + * assumes that the lock will be available very soon. + * + * This function checks if the running thread has already locked the + * location, warns if this occurs and creates a thread dump before + * returning. + */ +void +_spinlock_debug(spinlock_t *lck, char *fname, int lineno) +{ + _spinlock(lck); +} + +static void +init_spinlock(spinlock_t *lck) +{ + _pthread_mutex_lock(&spinlock_static_lock); + if ((lck->fname == NULL) && (spinlock_count < MAX_SPINLOCKS)) { + lck->fname = (char *)&extra[spinlock_count]; + extra[spinlock_count].owner = lck; + spinlock_count++; + } + _pthread_mutex_unlock(&spinlock_static_lock); + if (lck->fname == NULL) + PANIC("Exceeded max spinlocks"); +} + +void +_thr_spinlock_init(void) +{ + int i; + + if (initialized != 0) { + _thr_mutex_reinit(&spinlock_static_lock); + for (i = 0; i < spinlock_count; i++) + _thr_mutex_reinit(&extra[i].lock); + } else { + if (_pthread_mutex_init(&spinlock_static_lock, &static_mattr)) + PANIC("Cannot initialize spinlock_static_lock"); + for (i = 0; i < MAX_SPINLOCKS; i++) { + if (_pthread_mutex_init(&extra[i].lock, &static_mattr)) + PANIC("Cannot initialize spinlock extra"); + } + initialized = 1; + } +} diff --git a/lib/libpthread/thread/thr_stack.c b/lib/libpthread/thread/thr_stack.c new file mode 100644 index 0000000..f634055 --- /dev/null +++ b/lib/libpthread/thread/thr_stack.c @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2001 Daniel Eischen <deischen@freebsd.org> + * Copyright (c) 2000-2001 Jason Evans <jasone@freebsd.org> + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 AUTHORS 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. + * + * $FreeBSD$ + */ +#include <sys/types.h> +#include <sys/mman.h> +#include <sys/queue.h> +#include <stdlib.h> +#include <pthread.h> +#include "thr_private.h" + +/* Spare thread stack. */ +struct stack { + LIST_ENTRY(stack) qe; /* Stack queue linkage. */ + size_t stacksize; /* Stack size (rounded up). */ + size_t guardsize; /* Guard size. */ + void *stackaddr; /* Stack address. */ +}; + +/* + * Default sized (stack and guard) spare stack queue. Stacks are cached + * to avoid additional complexity managing mmap()ed stack regions. Spare + * stacks are used in LIFO order to increase cache locality. + */ +static LIST_HEAD(, stack) dstackq = LIST_HEAD_INITIALIZER(dstackq); + +/* + * Miscellaneous sized (non-default stack and/or guard) spare stack queue. + * Stacks are cached to avoid additional complexity managing mmap()ed + * stack regions. This list is unordered, since ordering on both stack + * size and guard size would be more trouble than it's worth. Stacks are + * allocated from this cache on a first size match basis. + */ +static LIST_HEAD(, stack) mstackq = LIST_HEAD_INITIALIZER(mstackq); + +/** + * Base address of the last stack allocated (including its red zone, if + * there is one). Stacks are allocated contiguously, starting beyond the + * top of the main stack. When a new stack is created, a red zone is + * typically created (actually, the red zone is mapped with PROT_NONE) above + * the top of the stack, such that the stack will not be able to grow all + * the way to the bottom of the next stack. This isn't fool-proof. It is + * possible for a stack to grow by a large amount, such that it grows into + * the next stack, and as long as the memory within the red zone is never + * accessed, nothing will prevent one thread stack from trouncing all over + * the next. + * + * low memory + * . . . . . . . . . . . . . . . . . . + * | | + * | stack 3 | start of 3rd thread stack + * +-----------------------------------+ + * | | + * | Red Zone (guard page) | red zone for 2nd thread + * | | + * +-----------------------------------+ + * | stack 2 - PTHREAD_STACK_DEFAULT | top of 2nd thread stack + * | | + * | | + * | | + * | | + * | stack 2 | + * +-----------------------------------+ <-- start of 2nd thread stack + * | | + * | Red Zone | red zone for 1st thread + * | | + * +-----------------------------------+ + * | stack 1 - PTHREAD_STACK_DEFAULT | top of 1st thread stack + * | | + * | | + * | | + * | | + * | stack 1 | + * +-----------------------------------+ <-- start of 1st thread stack + * | | (initial value of last_stack) + * | Red Zone | + * | | red zone for main thread + * +-----------------------------------+ + * | USRSTACK - PTHREAD_STACK_INITIAL | top of main thread stack + * | | ^ + * | | | + * | | | + * | | | stack growth + * | | + * +-----------------------------------+ <-- start of main thread stack + * (USRSTACK) + * high memory + * + */ +static void *last_stack = NULL; + +/* + * Round size up to the nearest multiple of + * _thr_page_size. + */ +static inline size_t +round_up(size_t size) +{ + if (size % _thr_page_size != 0) + size = ((size / _thr_page_size) + 1) * + _thr_page_size; + return size; +} + +int +_thr_stack_alloc(struct pthread_attr *attr) +{ + struct stack *spare_stack; + struct kse *curkse; + kse_critical_t crit; + size_t stacksize; + size_t guardsize; + char *stackaddr; + + /* + * Round up stack size to nearest multiple of _thr_page_size so + * that mmap() * will work. If the stack size is not an even + * multiple, we end up initializing things such that there is + * unused space above the beginning of the stack, so the stack + * sits snugly against its guard. + */ + stacksize = round_up(attr->stacksize_attr); + guardsize = round_up(attr->guardsize_attr); + + attr->stackaddr_attr = NULL; + attr->flags &= ~THR_STACK_USER; + + /* + * Use the garbage collector lock for synchronization of the + * spare stack lists and allocations from usrstack. + */ + crit = _kse_critical_enter(); + curkse = _get_curkse(); + KSE_LOCK_ACQUIRE(curkse, &_thread_list_lock); + /* + * If the stack and guard sizes are default, try to allocate a stack + * from the default-size stack cache: + */ + if ((stacksize == _thr_stack_default) && + (guardsize == _thr_guard_default)) { + if ((spare_stack = LIST_FIRST(&dstackq)) != NULL) { + /* Use the spare stack. */ + LIST_REMOVE(spare_stack, qe); + attr->stackaddr_attr = spare_stack->stackaddr; + } + } + /* + * The user specified a non-default stack and/or guard size, so try to + * allocate a stack from the non-default size stack cache, using the + * rounded up stack size (stack_size) in the search: + */ + else { + LIST_FOREACH(spare_stack, &mstackq, qe) { + if (spare_stack->stacksize == stacksize && + spare_stack->guardsize == guardsize) { + LIST_REMOVE(spare_stack, qe); + attr->stackaddr_attr = spare_stack->stackaddr; + break; + } + } + } + if (attr->stackaddr_attr != NULL) { + /* A cached stack was found. Release the lock. */ + KSE_LOCK_RELEASE(curkse, &_thread_list_lock); + _kse_critical_leave(crit); + } + else { + /* Allocate a stack from usrstack. */ + if (last_stack == NULL) + last_stack = _usrstack - _thr_stack_initial - + _thr_guard_default; + + /* Allocate a new stack. */ + stackaddr = last_stack - stacksize - guardsize; + + /* + * Even if stack allocation fails, we don't want to try to + * use this location again, so unconditionally decrement + * last_stack. Under normal operating conditions, the most + * likely reason for an mmap() error is a stack overflow of + * the adjacent thread stack. + */ + last_stack -= (stacksize + guardsize); + + /* Release the lock before mmap'ing it. */ + KSE_LOCK_RELEASE(curkse, &_thread_list_lock); + _kse_critical_leave(crit); + + /* Map the stack and guard page together, and split guard + page from allocated space: */ + if ((stackaddr = mmap(stackaddr, stacksize+guardsize, + PROT_READ | PROT_WRITE, MAP_STACK, + -1, 0)) != MAP_FAILED && + (guardsize == 0 || + mprotect(stackaddr, guardsize, PROT_NONE) == 0)) { + stackaddr += guardsize; + } else { + if (stackaddr != MAP_FAILED) + munmap(stackaddr, stacksize + guardsize); + stackaddr = NULL; + } + attr->stackaddr_attr = stackaddr; + } + if (attr->stackaddr_attr != NULL) + return (0); + else + return (-1); +} + +/* This function must be called with _thread_list_lock held. */ +void +_thr_stack_free(struct pthread_attr *attr) +{ + struct stack *spare_stack; + + if ((attr != NULL) && ((attr->flags & THR_STACK_USER) == 0) + && (attr->stackaddr_attr != NULL)) { + spare_stack = (attr->stackaddr_attr + attr->stacksize_attr + - sizeof(struct stack)); + spare_stack->stacksize = round_up(attr->stacksize_attr); + spare_stack->guardsize = round_up(attr->guardsize_attr); + spare_stack->stackaddr = attr->stackaddr_attr; + + if (spare_stack->stacksize == _thr_stack_default && + spare_stack->guardsize == _thr_guard_default) { + /* Default stack/guard size. */ + LIST_INSERT_HEAD(&dstackq, spare_stack, qe); + } else { + /* Non-default stack/guard size. */ + LIST_INSERT_HEAD(&mstackq, spare_stack, qe); + } + attr->stackaddr_attr = NULL; + } +} diff --git a/lib/libpthread/thread/thr_suspend_np.c b/lib/libpthread/thread/thr_suspend_np.c new file mode 100644 index 0000000..16c129c --- /dev/null +++ b/lib/libpthread/thread/thr_suspend_np.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <errno.h> +#include <pthread.h> +#include "thr_private.h" + +static void suspend_common(struct pthread *thread); + +LT10_COMPAT_PRIVATE(_pthread_suspend_np); +LT10_COMPAT_DEFAULT(pthread_suspend_np); +LT10_COMPAT_PRIVATE(_pthread_suspend_all_np); +LT10_COMPAT_DEFAULT(pthread_suspend_all_np); + +__weak_reference(_pthread_suspend_np, pthread_suspend_np); +__weak_reference(_pthread_suspend_all_np, pthread_suspend_all_np); + +/* Suspend a thread: */ +int +_pthread_suspend_np(pthread_t thread) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + /* Suspending the current thread doesn't make sense. */ + if (thread == _get_curthread()) + ret = EDEADLK; + + /* Add a reference to the thread: */ + else if ((ret = _thr_ref_add(curthread, thread, /*include dead*/0)) + == 0) { + /* Lock the threads scheduling queue: */ + THR_SCHED_LOCK(curthread, thread); + suspend_common(thread); + /* Unlock the threads scheduling queue: */ + THR_SCHED_UNLOCK(curthread, thread); + + /* Don't forget to remove the reference: */ + _thr_ref_delete(curthread, thread); + } + return (ret); +} + +void +_pthread_suspend_all_np(void) +{ + struct pthread *curthread = _get_curthread(); + struct pthread *thread; + kse_critical_t crit; + + /* Take the thread list lock: */ + crit = _kse_critical_enter(); + KSE_LOCK_ACQUIRE(curthread->kse, &_thread_list_lock); + + TAILQ_FOREACH(thread, &_thread_list, tle) { + if (thread != curthread) { + THR_SCHED_LOCK(curthread, thread); + suspend_common(thread); + THR_SCHED_UNLOCK(curthread, thread); + } + } + + /* Release the thread list lock: */ + KSE_LOCK_RELEASE(curthread->kse, &_thread_list_lock); + _kse_critical_leave(crit); +} + +void +suspend_common(struct pthread *thread) +{ + if ((thread->state != PS_DEAD) && + (thread->state != PS_DEADLOCK) && + ((thread->flags & THR_FLAGS_EXITING) == 0)) { + thread->flags |= THR_FLAGS_SUSPENDED; + if ((thread->flags & THR_FLAGS_IN_RUNQ) != 0) { + THR_RUNQ_REMOVE(thread); + THR_SET_STATE(thread, PS_SUSPENDED); + } +#ifdef NOT_YET + if ((thread->attr.flags & PTHREAD_SCOPE_SYSTEM) != 0) + /* ??? */ +#endif + } +} diff --git a/lib/libpthread/thread/thr_switch_np.c b/lib/libpthread/thread/thr_switch_np.c new file mode 100644 index 0000000..247b879 --- /dev/null +++ b/lib/libpthread/thread/thr_switch_np.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>. + * 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 Daniel Eischen. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <errno.h> +#include <pthread.h> +#include <pthread_np.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_pthread_switch_add_np); +LT10_COMPAT_DEFAULT(pthread_switch_add_np); +LT10_COMPAT_PRIVATE(_pthread_switch_delete_np); +LT10_COMPAT_DEFAULT(pthread_switch_delete_np); + +__weak_reference(_pthread_switch_add_np, pthread_switch_add_np); +__weak_reference(_pthread_switch_delete_np, pthread_switch_delete_np); + +int +_pthread_switch_add_np(pthread_switch_routine_t routine) +{ + return (ENOTSUP); +} + +int +_pthread_switch_delete_np(pthread_switch_routine_t routine) +{ + return (ENOTSUP); +} diff --git a/lib/libpthread/thread/thr_symbols.c b/lib/libpthread/thread/thr_symbols.c new file mode 100644 index 0000000..2b9639b --- /dev/null +++ b/lib/libpthread/thread/thr_symbols.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2004 David Xu <davidxu@freebsd.org> + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <stddef.h> +#include <pthread.h> +#include <rtld.h> + +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_thread_off_tcb); +LT10_COMPAT_PRIVATE(_thread_off_tmbx); +LT10_COMPAT_PRIVATE(_thread_off_next); +LT10_COMPAT_PRIVATE(_thread_off_attr_flags); +LT10_COMPAT_PRIVATE(_thread_off_kse); +LT10_COMPAT_PRIVATE(_thread_off_kse_locklevel); +LT10_COMPAT_PRIVATE(_thread_off_thr_locklevel); +LT10_COMPAT_PRIVATE(_thread_off_linkmap); +LT10_COMPAT_PRIVATE(_thread_off_tlsindex); +LT10_COMPAT_PRIVATE(_thread_size_key); +LT10_COMPAT_PRIVATE(_thread_off_key_allocated); +LT10_COMPAT_PRIVATE(_thread_off_key_destructor); +LT10_COMPAT_PRIVATE(_thread_max_keys); +LT10_COMPAT_PRIVATE(_thread_off_dtv); +LT10_COMPAT_PRIVATE(_thread_off_state); +LT10_COMPAT_PRIVATE(_thread_state_running); +LT10_COMPAT_PRIVATE(_thread_state_zoombie); + +/* A collection of symbols needed by debugger */ + +/* int _libkse_debug */ +int _thread_off_tcb = offsetof(struct pthread, tcb); +int _thread_off_tmbx = offsetof(struct tcb, tcb_tmbx); +int _thread_off_next = offsetof(struct pthread, tle.tqe_next); +int _thread_off_attr_flags = offsetof(struct pthread, attr.flags); +int _thread_off_kse = offsetof(struct pthread, kse); +int _thread_off_kse_locklevel = offsetof(struct kse, k_locklevel); +int _thread_off_thr_locklevel = offsetof(struct pthread, locklevel); +int _thread_off_linkmap = offsetof(Obj_Entry, linkmap); +int _thread_off_tlsindex = offsetof(Obj_Entry, tlsindex); +int _thread_size_key = sizeof(struct pthread_key); +int _thread_off_key_allocated = offsetof(struct pthread_key, allocated); +int _thread_off_key_destructor = offsetof(struct pthread_key, destructor); +int _thread_max_keys = PTHREAD_KEYS_MAX; +int _thread_off_dtv = DTV_OFFSET; +int _thread_off_state = offsetof(struct pthread, state); +int _thread_state_running = PS_RUNNING; +int _thread_state_zoombie = PS_DEAD; +int _thread_off_sigmask = offsetof(struct pthread, sigmask); +int _thread_off_sigpend = offsetof(struct pthread, sigpend); diff --git a/lib/libpthread/thread/thr_system.c b/lib/libpthread/thread/thr_system.c new file mode 100644 index 0000000..5e4fe1c --- /dev/null +++ b/lib/libpthread/thread/thr_system.c @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>. + * 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(s), this list of conditions and the following disclaimer as + * the first lines of this file unmodified other than the possible + * addition of one or more copyright notices. + * 2. Redistributions in binary form must reproduce the above copyright + * notice(s), this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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. + * + * $FreeBSD$ + */ + +#include <stdlib.h> +#include <pthread.h> +#include "thr_private.h" + +extern int __system(const char *); + +LT10_COMPAT_PRIVATE(_system); +LT10_COMPAT_DEFAULT(system); + +__weak_reference(_system, system); + +int +_system(const char *string) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_cancel_enter(curthread); + ret = __system(string); + _thr_cancel_leave(curthread, 1); + + return ret; +} diff --git a/lib/libpthread/thread/thr_tcdrain.c b/lib/libpthread/thread/thr_tcdrain.c new file mode 100644 index 0000000..e231d52 --- /dev/null +++ b/lib/libpthread/thread/thr_tcdrain.c @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>. + * 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(s), this list of conditions and the following disclaimer as + * the first lines of this file unmodified other than the possible + * addition of one or more copyright notices. + * 2. Redistributions in binary form must reproduce the above copyright + * notice(s), this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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. + * + * $FreeBSD$ + */ + +#include <termios.h> +#include <pthread.h> +#include "thr_private.h" + +extern int __tcdrain(int); + +LT10_COMPAT_PRIVATE(_tcdrain); +LT10_COMPAT_DEFAULT(tcdrain); + +__weak_reference(_tcdrain, tcdrain); + +int +_tcdrain(int fd) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_cancel_enter(curthread); + ret = __tcdrain(fd); + _thr_cancel_leave(curthread, 1); + + return (ret); +} diff --git a/lib/libpthread/thread/thr_vfork.c b/lib/libpthread/thread/thr_vfork.c new file mode 100644 index 0000000..428c129 --- /dev/null +++ b/lib/libpthread/thread/thr_vfork.c @@ -0,0 +1,17 @@ +/* + * $FreeBSD$ + */ +#include <unistd.h> + +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_vfork); +LT10_COMPAT_DEFAULT(vfork); + +__weak_reference(_vfork, vfork); + +int +_vfork(void) +{ + return (fork()); +} diff --git a/lib/libpthread/thread/thr_wait.c b/lib/libpthread/thread/thr_wait.c new file mode 100644 index 0000000..8f61a0c --- /dev/null +++ b/lib/libpthread/thread/thr_wait.c @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>. + * 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(s), this list of conditions and the following disclaimer as + * the first lines of this file unmodified other than the possible + * addition of one or more copyright notices. + * 2. Redistributions in binary form must reproduce the above copyright + * notice(s), this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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. + * + * $FreeBSD$ + */ + +#include <pthread.h> +#include "thr_private.h" + +extern int __wait(int *); + +LT10_COMPAT_PRIVATE(_wait); +LT10_COMPAT_DEFAULT(wait); + +__weak_reference(_wait, wait); + +pid_t +_wait(int *istat) +{ + struct pthread *curthread = _get_curthread(); + pid_t ret; + + _thr_cancel_enter(curthread); + ret = __wait(istat); + _thr_cancel_leave(curthread, 1); + + return ret; +} diff --git a/lib/libpthread/thread/thr_wait4.c b/lib/libpthread/thread/thr_wait4.c new file mode 100644 index 0000000..fae0fed --- /dev/null +++ b/lib/libpthread/thread/thr_wait4.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <sys/types.h> + +#include "namespace.h" +#include <errno.h> +#include <sys/wait.h> +#include <pthread.h> +#include "un-namespace.h" + +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(__wait4); +LT10_COMPAT_DEFAULT(wait4); + +__weak_reference(__wait4, wait4); + +pid_t +__wait4(pid_t pid, int *istat, int options, struct rusage *rusage) +{ + struct pthread *curthread = _get_curthread(); + pid_t ret; + + _thr_cancel_enter(curthread); + ret = _wait4(pid, istat, options, rusage); + _thr_cancel_leave(curthread, 1); + + return ret; +} diff --git a/lib/libpthread/thread/thr_waitpid.c b/lib/libpthread/thread/thr_waitpid.c new file mode 100644 index 0000000..1244c12 --- /dev/null +++ b/lib/libpthread/thread/thr_waitpid.c @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>. + * 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(s), this list of conditions and the following disclaimer as + * the first lines of this file unmodified other than the possible + * addition of one or more copyright notices. + * 2. Redistributions in binary form must reproduce the above copyright + * notice(s), this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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. + * + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <sys/wait.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_waitpid); +LT10_COMPAT_DEFAULT(waitpid); + +extern int __waitpid(pid_t, int *, int); + +__weak_reference(_waitpid, waitpid); + +pid_t +_waitpid(pid_t wpid, int *status, int options) +{ + struct pthread *curthread = _get_curthread(); + pid_t ret; + + _thr_cancel_enter(curthread); + ret = __waitpid(wpid, status, options); + _thr_cancel_leave(curthread, 1); + + return ret; +} diff --git a/lib/libpthread/thread/thr_write.c b/lib/libpthread/thread/thr_write.c new file mode 100644 index 0000000..00ad990 --- /dev/null +++ b/lib/libpthread/thread/thr_write.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + * + */ +#include <sys/types.h> +#include <sys/fcntl.h> +#include <sys/uio.h> +#include <errno.h> +#include <unistd.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(__write); +LT10_COMPAT_DEFAULT(write); + +__weak_reference(__write, write); + +ssize_t +__write(int fd, const void *buf, size_t nbytes) +{ + struct pthread *curthread = _get_curthread(); + ssize_t ret; + + _thr_cancel_enter(curthread); + ret = __sys_write(fd, buf, nbytes); + _thr_cancel_leave(curthread, 1); + + return ret; +} diff --git a/lib/libpthread/thread/thr_writev.c b/lib/libpthread/thread/thr_writev.c new file mode 100644 index 0000000..a05159f --- /dev/null +++ b/lib/libpthread/thread/thr_writev.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + * + */ +#include <sys/types.h> +#include <sys/fcntl.h> +#include <sys/uio.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(__writev); +LT10_COMPAT_DEFAULT(writev); + +__weak_reference(__writev, writev); + +ssize_t +__writev(int fd, const struct iovec *iov, int iovcnt) +{ + struct pthread *curthread = _get_curthread(); + ssize_t ret; + + _thr_cancel_enter(curthread); + ret = __sys_writev(fd, iov, iovcnt); + _thr_cancel_leave(curthread, 1); + + return ret; +} diff --git a/lib/libpthread/thread/thr_yield.c b/lib/libpthread/thread/thr_yield.c new file mode 100644 index 0000000..6ba77f8 --- /dev/null +++ b/lib/libpthread/thread/thr_yield.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * 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 John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. + * + * $FreeBSD$ + */ +#include <pthread.h> +#include "thr_private.h" + +LT10_COMPAT_PRIVATE(_sched_yield); +LT10_COMPAT_DEFAULT(sched_yield); +LT10_COMPAT_PRIVATE(_pthread_yield); +LT10_COMPAT_DEFAULT(pthread_yield); + +__weak_reference(_sched_yield, sched_yield); +__weak_reference(_pthread_yield, pthread_yield); + +int +_sched_yield(void) +{ + struct pthread *curthread = _get_curthread(); + + if (curthread->attr.flags & PTHREAD_SCOPE_SYSTEM) + return (__sys_sched_yield()); + + /* Reset the accumulated time slice value for the current thread: */ + curthread->slice_usec = -1; + + /* Schedule the next thread: */ + _thr_sched_switch(curthread); + /* Always return no error. */ + return(0); +} + +/* Draft 4 yield */ +void +_pthread_yield(void) +{ + struct pthread *curthread = _get_curthread(); + + if (curthread->attr.flags & PTHREAD_SCOPE_SYSTEM) { + __sys_sched_yield(); + return; + } + + /* Reset the accumulated time slice value for the current thread: */ + curthread->slice_usec = -1; + + /* Schedule the next thread: */ + _thr_sched_switch(curthread); +} |