diff options
Diffstat (limited to 'lib/libthr')
87 files changed, 13287 insertions, 0 deletions
diff --git a/lib/libthr/Makefile b/lib/libthr/Makefile new file mode 100644 index 0000000..c4de29a --- /dev/null +++ b/lib/libthr/Makefile @@ -0,0 +1,67 @@ +# $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. + +SHLIBDIR?= /lib + +.include <bsd.own.mk> +MK_SSP= no + +LIB=thr +SHLIB_MAJOR= 3 +WARNS?= 3 +CFLAGS+=-DPTHREAD_KERNEL +CFLAGS+=-I${.CURDIR}/../libc/include -I${.CURDIR}/thread \ + -I${.CURDIR}/../../include +CFLAGS+=-I${.CURDIR}/arch/${MACHINE_CPUARCH}/include +CFLAGS+=-I${.CURDIR}/sys +CFLAGS+=-I${.CURDIR}/../../libexec/rtld-elf +CFLAGS+=-I${.CURDIR}/../../libexec/rtld-elf/${MACHINE_CPUARCH} +CFLAGS+=-I${.CURDIR}/../libthread_db +CFLAGS+=-Winline + +.ifndef NO_THREAD_UNWIND_STACK +CFLAGS+=-fexceptions +CFLAGS+=-D_PTHREAD_FORCED_UNWIND +.endif + +LDFLAGS+=-Wl,-znodelete + +VERSION_DEF=${.CURDIR}/../libc/Versions.def +SYMBOL_MAPS=${.CURDIR}/pthread.map + +MAN= libthr.3 + +# enable extra internal consistancy checks +CFLAGS+=-D_PTHREADS_INVARIANTS +#CFLAGS+=-g + +PRECIOUSLIB= + +.PATH: ${.CURDIR}/arch/${MACHINE_CPUARCH}/${MACHINE_CPUARCH} + +.include "${.CURDIR}/arch/${MACHINE_CPUARCH}/Makefile.inc" +.include "${.CURDIR}/sys/Makefile.inc" +.include "${.CURDIR}/thread/Makefile.inc" + +.if ${MK_INSTALLLIB} != "no" +SYMLINKS+=lib${LIB}.a ${LIBDIR}/libpthread.a +.endif +.if !defined(NO_PIC) +SYMLINKS+=lib${LIB}.so ${LIBDIR}/libpthread.so +.endif +.if ${MK_PROFILE} != "no" +SYMLINKS+=lib${LIB}_p.a ${LIBDIR}/libpthread_p.a +.endif + +.if !defined(WITHOUT_SYSCALL_COMPAT) +CFLAGS+=-DSYSCALL_COMPAT +.endif + +.include <bsd.lib.mk> diff --git a/lib/libthr/arch/amd64/Makefile.inc b/lib/libthr/arch/amd64/Makefile.inc new file mode 100644 index 0000000..e6d99ec --- /dev/null +++ b/lib/libthr/arch/amd64/Makefile.inc @@ -0,0 +1,3 @@ +#$FreeBSD$ + +SRCS+= pthread_md.c _umtx_op_err.S diff --git a/lib/libthr/arch/amd64/amd64/_umtx_op_err.S b/lib/libthr/arch/amd64/amd64/_umtx_op_err.S new file mode 100644 index 0000000..b54fe64 --- /dev/null +++ b/lib/libthr/arch/amd64/amd64/_umtx_op_err.S @@ -0,0 +1,39 @@ +/*- + * Copyright (C) 2008 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. 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$ + */ + +#include <sys/syscall.h> +#include <machine/asm.h> + +#define RSYSCALL_ERR(x) ENTRY(__CONCAT(x, _err)); \ + mov __CONCAT($SYS_,x),%rax; KERNCALL; ret; + +#define KERNCALL movq %rcx, %r10; syscall + +RSYSCALL_ERR(_umtx_op) + + .section .note.GNU-stack,"",%progbits diff --git a/lib/libthr/arch/amd64/amd64/pthread_md.c b/lib/libthr/arch/amd64/amd64/pthread_md.c new file mode 100644 index 0000000..b661657 --- /dev/null +++ b/lib/libthr/arch/amd64/amd64/pthread_md.c @@ -0,0 +1,55 @@ +/* + * 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 <sys/types.h> +#include <rtld_tls.h> + +#include "pthread_md.h" + +/* + * The constructors. + */ +struct tcb * +_tcb_ctor(struct pthread *thread, int initial) +{ + struct tcb *tcb; + + if (initial) + __asm __volatile("movq %%fs:0, %0" : "=r" (tcb)); + else + tcb = _rtld_allocate_tls(NULL, sizeof(struct tcb), 16); + if (tcb) + tcb->tcb_thread = thread; + return (tcb); +} + +void +_tcb_dtor(struct tcb *tcb) +{ + _rtld_free_tls(tcb, sizeof(struct tcb), 16); +} diff --git a/lib/libthr/arch/amd64/include/pthread_md.h b/lib/libthr/arch/amd64/include/pthread_md.h new file mode 100644 index 0000000..0ebea2e --- /dev/null +++ b/lib/libthr/arch/amd64/include/pthread_md.h @@ -0,0 +1,103 @@ +/*- + * 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. + */ +#ifndef _PTHREAD_MD_H_ +#define _PTHREAD_MD_H_ + +#include <stddef.h> +#include <sys/types.h> +#include <machine/sysarch.h> + +#define CPU_SPINWAIT __asm __volatile("pause") + +#define DTV_OFFSET offsetof(struct tcb, tcb_dtv) + +/* + * Variant II tcb, first two members are required by rtld, + * %fs points to the structure. + */ +struct tcb { + struct tcb *tcb_self; /* required by rtld */ + void *tcb_dtv; /* required by rtld */ + struct pthread *tcb_thread; + void *tcb_spare[1]; +}; + +/* + * Evaluates to the byte offset of the per-tcb variable name. + */ +#define __tcb_offset(name) __offsetof(struct tcb, name) + +/* + * Evaluates to the type of the per-tcb variable name. + */ +#define __tcb_type(name) __typeof(((struct tcb *)0)->name) + +/* + * Evaluates to the value of the per-tcb variable name. + */ +#define TCB_GET64(name) ({ \ + __tcb_type(name) __result; \ + \ + u_long __i; \ + __asm __volatile("movq %%fs:%1, %0" \ + : "=r" (__i) \ + : "m" (*(volatile u_long *)(__tcb_offset(name)))); \ + __result = (__tcb_type(name))__i; \ + \ + __result; \ +}) + +struct tcb *_tcb_ctor(struct pthread *, int); +void _tcb_dtor(struct tcb *tcb); + +static __inline void +_tcb_set(struct tcb *tcb) +{ + amd64_set_fsbase(tcb); +} + +static __inline struct tcb * +_tcb_get(void) +{ + return (TCB_GET64(tcb_self)); +} + +static __inline struct pthread * +_get_curthread(void) +{ + return (TCB_GET64(tcb_thread)); +} + +#define HAS__UMTX_OP_ERR 1 + +#endif diff --git a/lib/libthr/arch/arm/Makefile.inc b/lib/libthr/arch/arm/Makefile.inc new file mode 100644 index 0000000..2ee2247 --- /dev/null +++ b/lib/libthr/arch/arm/Makefile.inc @@ -0,0 +1,3 @@ +# $FreeBSD$ + +SRCS+= pthread_md.c diff --git a/lib/libthr/arch/arm/arm/pthread_md.c b/lib/libthr/arch/arm/arm/pthread_md.c new file mode 100644 index 0000000..028f06c --- /dev/null +++ b/lib/libthr/arch/arm/arm/pthread_md.c @@ -0,0 +1,53 @@ +/*- + * 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, 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$ + */ + +#include <stdlib.h> +#include <sys/types.h> +#include <rtld_tls.h> + +#include "pthread_md.h" + +struct tcb * +_tcb_ctor(struct pthread *thread, int initial) +{ + struct tcb *tcb; + + tcb = _rtld_allocate_tls((initial) ? _tcb_get() : NULL, + sizeof(struct tcb), 16); + if (tcb) + tcb->tcb_thread = thread; + + return (tcb); +} + +void +_tcb_dtor(struct tcb *tcb) +{ + + _rtld_free_tls(tcb, sizeof(struct tcb), 16); +} diff --git a/lib/libthr/arch/arm/include/pthread_md.h b/lib/libthr/arch/arm/include/pthread_md.h new file mode 100644 index 0000000..83ddf7f --- /dev/null +++ b/lib/libthr/arch/arm/include/pthread_md.h @@ -0,0 +1,82 @@ +/*- + * 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, 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. + */ +#ifndef _PTHREAD_MD_H_ +#define _PTHREAD_MD_H_ + +#include <sys/types.h> +#include <machine/sysarch.h> +#include <stddef.h> + +#define CPU_SPINWAIT +#define DTV_OFFSET offsetof(struct tcb, tcb_dtv) + +/* + * Variant II tcb, first two members are required by rtld. + */ +struct tcb { + void *tcb_dtv; /* required by rtld */ + struct pthread *tcb_thread; /* our hook */ +}; + +/* + * The tcb constructors. + */ +struct tcb *_tcb_ctor(struct pthread *, int); +void _tcb_dtor(struct tcb *); + +/* Called from the thread to set its private data. */ +static __inline void +_tcb_set(struct tcb *tcb) +{ + *((struct tcb **)ARM_TP_ADDRESS) = tcb; +} + +/* + * Get the current tcb. + */ +static __inline struct tcb * +_tcb_get(void) +{ + return (*((struct tcb **)ARM_TP_ADDRESS)); +} + +extern struct pthread *_thr_initial; + +static __inline struct pthread * +_get_curthread(void) +{ + if (_thr_initial) + return (_tcb_get()->tcb_thread); + return (NULL); +} + +#endif /* _PTHREAD_MD_H_ */ diff --git a/lib/libthr/arch/i386/Makefile.inc b/lib/libthr/arch/i386/Makefile.inc new file mode 100644 index 0000000..01290d5 --- /dev/null +++ b/lib/libthr/arch/i386/Makefile.inc @@ -0,0 +1,3 @@ +# $FreeBSD$ + +SRCS+= pthread_md.c _umtx_op_err.S diff --git a/lib/libthr/arch/i386/i386/_umtx_op_err.S b/lib/libthr/arch/i386/i386/_umtx_op_err.S new file mode 100644 index 0000000..113412c --- /dev/null +++ b/lib/libthr/arch/i386/i386/_umtx_op_err.S @@ -0,0 +1,38 @@ +/*- + * Copyright (C) 2008 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. 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$ + */ + +#include <machine/asm.h> +#include <sys/syscall.h> + +#define SYSCALL_ERR(x) \ + ENTRY(__CONCAT(x, _err)); \ + mov __CONCAT($SYS_,x),%eax; int $0x80; ret + +SYSCALL_ERR(_umtx_op) + + .section .note.GNU-stack,"",%progbits diff --git a/lib/libthr/arch/i386/i386/pthread_md.c b/lib/libthr/arch/i386/i386/pthread_md.c new file mode 100644 index 0000000..a8b31d5 --- /dev/null +++ b/lib/libthr/arch/i386/i386/pthread_md.c @@ -0,0 +1,57 @@ +/*- + * 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. + * + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <machine/segments.h> +#include <machine/sysarch.h> +#include <string.h> +#include <rtld_tls.h> + +#include "pthread_md.h" + +struct tcb * +_tcb_ctor(struct pthread *thread, int initial) +{ + struct tcb *tcb; + + if (initial) + __asm __volatile("movl %%gs:0, %0" : "=r" (tcb)); + else + tcb = _rtld_allocate_tls(NULL, sizeof(struct tcb), 16); + if (tcb) + tcb->tcb_thread = thread; + return (tcb); +} + +void +_tcb_dtor(struct tcb *tcb) +{ + + _rtld_free_tls(tcb, sizeof(struct tcb), 16); +} diff --git a/lib/libthr/arch/i386/include/pthread_md.h b/lib/libthr/arch/i386/include/pthread_md.h new file mode 100644 index 0000000..c160aa4 --- /dev/null +++ b/lib/libthr/arch/i386/include/pthread_md.h @@ -0,0 +1,108 @@ +/*- + * Copyright (c) 2002 Daniel Eischen <deischen@freebsd.org>. + * 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, 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. + */ +#ifndef _PTHREAD_MD_H_ +#define _PTHREAD_MD_H_ + +#include <stddef.h> +#include <sys/types.h> +#include <machine/sysarch.h> + +#define CPU_SPINWAIT __asm __volatile("pause") + +#define DTV_OFFSET offsetof(struct tcb, tcb_dtv) + +/* + * Variant II tcb, first two members are required by rtld, + * %gs points to the structure. + */ +struct tcb { + struct tcb *tcb_self; /* required by rtld */ + void *tcb_dtv; /* required by rtld */ + struct pthread *tcb_thread; +}; + +/* + * Evaluates to the byte offset of the per-tcb variable name. + */ +#define __tcb_offset(name) __offsetof(struct tcb, name) + +/* + * Evaluates to the type of the per-tcb variable name. + */ +#define __tcb_type(name) __typeof(((struct tcb *)0)->name) + +/* + * Evaluates to the value of the per-tcb variable name. + */ +#define TCB_GET32(name) ({ \ + __tcb_type(name) __result; \ + \ + u_int __i; \ + __asm __volatile("movl %%gs:%1, %0" \ + : "=r" (__i) \ + : "m" (*(volatile u_int *)(__tcb_offset(name)))); \ + __result = (__tcb_type(name))__i; \ + \ + __result; \ +}) + +/* + * The constructors. + */ +struct tcb *_tcb_ctor(struct pthread *, int); +void _tcb_dtor(struct tcb *tcb); + +/* Called from the thread to set its private data. */ +static __inline void +_tcb_set(struct tcb *tcb) +{ + i386_set_gsbase(tcb); +} + +/* Get the current kcb. */ +static __inline struct tcb * +_tcb_get(void) +{ + return (TCB_GET32(tcb_self)); +} + +/* Get the current thread. */ +static __inline struct pthread * +_get_curthread(void) +{ + return (TCB_GET32(tcb_thread)); +} + +#define HAS__UMTX_OP_ERR 1 + +#endif diff --git a/lib/libthr/arch/ia64/Makefile.inc b/lib/libthr/arch/ia64/Makefile.inc new file mode 100644 index 0000000..795aa99 --- /dev/null +++ b/lib/libthr/arch/ia64/Makefile.inc @@ -0,0 +1,3 @@ +# $FreeBSD$ + +SRCS+= _umtx_op_err.S pthread_md.c diff --git a/lib/libthr/arch/ia64/ia64/_umtx_op_err.S b/lib/libthr/arch/ia64/ia64/_umtx_op_err.S new file mode 100644 index 0000000..a712210 --- /dev/null +++ b/lib/libthr/arch/ia64/ia64/_umtx_op_err.S @@ -0,0 +1,35 @@ +/*- + * Copyright (c) 2009 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 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 <sys/syscall.h> + +ENTRY(_umtx_op_err, 5) + CALLSYS_NOERROR(_umtx_op) + br.ret.sptk.few rp +END(_umtx_op_err) diff --git a/lib/libthr/arch/ia64/ia64/pthread_md.c b/lib/libthr/arch/ia64/ia64/pthread_md.c new file mode 100644 index 0000000..c2ad71a --- /dev/null +++ b/lib/libthr/arch/ia64/ia64/pthread_md.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2003 Daniel Eischen <deischen@freebsd.org> + * 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, 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 <sys/types.h> +#include <rtld_tls.h> + +#include "pthread_md.h" + +/* + * The constructors. + */ +struct tcb * +_tcb_ctor(struct pthread *thread, int initial) +{ + struct tcb *tcb; + + tcb = _rtld_allocate_tls((initial) ? _tcb_get() : NULL, + sizeof(struct tcb), 16); + if (tcb) + tcb->tcb_thread = thread; + return (tcb); +} + +void +_tcb_dtor(struct tcb *tcb) +{ + _rtld_free_tls(tcb, sizeof(struct tcb), 16); +} diff --git a/lib/libthr/arch/ia64/include/pthread_md.h b/lib/libthr/arch/ia64/include/pthread_md.h new file mode 100644 index 0000000..69b3eec --- /dev/null +++ b/lib/libthr/arch/ia64/include/pthread_md.h @@ -0,0 +1,82 @@ +/* + * 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 <stddef.h> + +#define CPU_SPINWAIT + +#define HAS__UMTX_OP_ERR 1 + +#define DTV_OFFSET offsetof(struct tcb, tcb_dtv) + +/* + * Variant I tcb. The structure layout is fixed, don't blindly + * change it! + */ +struct tcb { + void *tcb_dtv; + struct pthread *tcb_thread; +}; + +/* + * The tcb constructors. + */ +struct tcb *_tcb_ctor(struct pthread *, int); +void _tcb_dtor(struct tcb *); + +/* Called from the thread to set its private data. */ +static __inline void +_tcb_set(struct tcb *tcb) +{ + register struct tcb *tp __asm("%r13"); + + __asm __volatile("mov %0 = %1;;" : "=r"(tp) : "r"(tcb)); +} + +static __inline struct tcb * +_tcb_get(void) +{ + register struct tcb *tp __asm("%r13"); + + return (tp); +} + +extern struct pthread *_thr_initial; + +static __inline struct pthread * +_get_curthread(void) +{ + if (_thr_initial) + return (_tcb_get()->tcb_thread); + return (NULL); +} + +#endif /* _PTHREAD_MD_H_ */ diff --git a/lib/libthr/arch/mips/Makefile.inc b/lib/libthr/arch/mips/Makefile.inc new file mode 100644 index 0000000..2ee2247 --- /dev/null +++ b/lib/libthr/arch/mips/Makefile.inc @@ -0,0 +1,3 @@ +# $FreeBSD$ + +SRCS+= pthread_md.c diff --git a/lib/libthr/arch/mips/include/pthread_md.h b/lib/libthr/arch/mips/include/pthread_md.h new file mode 100644 index 0000000..19c9f38 --- /dev/null +++ b/lib/libthr/arch/mips/include/pthread_md.h @@ -0,0 +1,89 @@ +/*- + * 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, 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. + * + * from: src/lib/libthr/arch/arm/include/pthread_md.h,v 1.3 2005/10/29 13:40:31 davidxu + * $FreeBSD$ + */ + +/* + * Machine-dependent thread prototypes/definitions. + */ +#ifndef _PTHREAD_MD_H_ +#define _PTHREAD_MD_H_ + +#include <sys/types.h> +#include <machine/sysarch.h> +#include <machine/tls.h> +#include <stddef.h> + +#define CPU_SPINWAIT +#define DTV_OFFSET offsetof(struct tcb, tcb_dtv) + +/* + * Variant I tcb. The structure layout is fixed, don't blindly + * change it! + */ +struct tcb { + void *tcb_dtv; + struct pthread *tcb_thread; +}; + +/* + * The tcb constructors. + */ +struct tcb *_tcb_ctor(struct pthread *, int); +void _tcb_dtor(struct tcb *); + +/* Called from the thread to set its private data. */ +static __inline void +_tcb_set(struct tcb *tcb) +{ + + sysarch(MIPS_SET_TLS, tcb); +} + +/* + * Get the current tcb. + */ +static __inline struct tcb * +_tcb_get(void) +{ + struct tcb *tcb; + + sysarch(MIPS_GET_TLS, &tcb); + return tcb; +} + +extern struct pthread *_thr_initial; + +static __inline struct pthread * +_get_curthread(void) +{ + if (_thr_initial) + return (_tcb_get()->tcb_thread); + return (NULL); +} + +#endif /* _PTHREAD_MD_H_ */ diff --git a/lib/libthr/arch/mips/mips/pthread_md.c b/lib/libthr/arch/mips/mips/pthread_md.c new file mode 100644 index 0000000..e596edc --- /dev/null +++ b/lib/libthr/arch/mips/mips/pthread_md.c @@ -0,0 +1,59 @@ +/*- + * 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, 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. + * + * from: src/lib/libthr/arch/arm/arm/pthread_md.c,v 1.2 2005/10/29 13:40:31 davidxu + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdlib.h> +#include <sys/types.h> +#include <rtld_tls.h> +#include <strings.h> + +#include <machine/sysarch.h> + +#include "pthread_md.h" + +struct tcb * +_tcb_ctor(struct pthread *thread, int initial) +{ + struct tcb *tcb; + + tcb = _rtld_allocate_tls((initial) ? _tcb_get() : NULL, + sizeof(struct tcb), 16); + if (tcb) + tcb->tcb_thread = thread; + + return (tcb); +} + +void +_tcb_dtor(struct tcb *tcb) +{ + + _rtld_free_tls(tcb, sizeof(struct tcb), 16); +} diff --git a/lib/libthr/arch/powerpc/Makefile.inc b/lib/libthr/arch/powerpc/Makefile.inc new file mode 100644 index 0000000..2ee2247 --- /dev/null +++ b/lib/libthr/arch/powerpc/Makefile.inc @@ -0,0 +1,3 @@ +# $FreeBSD$ + +SRCS+= pthread_md.c diff --git a/lib/libthr/arch/powerpc/include/pthread_md.h b/lib/libthr/arch/powerpc/include/pthread_md.h new file mode 100644 index 0000000..91102b4 --- /dev/null +++ b/lib/libthr/arch/powerpc/include/pthread_md.h @@ -0,0 +1,96 @@ +/* + * 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. + */ +#ifndef _PTHREAD_MD_H_ +#define _PTHREAD_MD_H_ + +#include <stddef.h> +#include <sys/types.h> + +#define CPU_SPINWAIT + +#define DTV_OFFSET offsetof(struct tcb, tcb_dtv) +#ifdef __powerpc64__ +#define TP_OFFSET 0x7010 +#else +#define TP_OFFSET 0x7008 +#endif + +/* + * Variant I tcb. The structure layout is fixed, don't blindly + * change it. + * %r2 (32-bit) or %r13 (64-bit) points to end of the structure. + */ +struct tcb { + void *tcb_dtv; + struct pthread *tcb_thread; +}; + +struct tcb *_tcb_ctor(struct pthread *, int); +void _tcb_dtor(struct tcb *); + +static __inline void +_tcb_set(struct tcb *tcb) +{ +#ifdef __powerpc64__ + register uint8_t *_tp __asm__("%r13"); +#else + register uint8_t *_tp __asm__("%r2"); +#endif + + __asm __volatile("mr %0,%1" : "=r"(_tp) : + "r"((uint8_t *)tcb + TP_OFFSET)); +} + +static __inline struct tcb * +_tcb_get(void) +{ +#ifdef __powerpc64__ + register uint8_t *_tp __asm__("%r13"); +#else + register uint8_t *_tp __asm__("%r2"); +#endif + + return ((struct tcb *)(_tp - TP_OFFSET)); +} + +extern struct pthread *_thr_initial; + +static __inline struct pthread * +_get_curthread(void) +{ + if (_thr_initial) + return (_tcb_get()->tcb_thread); + return (NULL); +} + +#endif /* _PTHREAD_MD_H_ */ diff --git a/lib/libthr/arch/powerpc/powerpc/pthread_md.c b/lib/libthr/arch/powerpc/powerpc/pthread_md.c new file mode 100644 index 0000000..66f043e --- /dev/null +++ b/lib/libthr/arch/powerpc/powerpc/pthread_md.c @@ -0,0 +1,54 @@ +/* + * 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 <sys/types.h> +#include <rtld_tls.h> + +#include "pthread_md.h" + +/* + * The constructors. + */ +struct tcb * +_tcb_ctor(struct pthread *thread, int initial) +{ + struct tcb *tcb; + + tcb = _rtld_allocate_tls((initial) ? _tcb_get() : NULL, + sizeof(struct tcb), 1); + if (tcb) + tcb->tcb_thread = thread; + return (tcb); + +} + +void +_tcb_dtor(struct tcb *tcb) +{ + _rtld_free_tls(tcb, sizeof(struct tcb), 1); +} diff --git a/lib/libthr/arch/sparc64/Makefile.inc b/lib/libthr/arch/sparc64/Makefile.inc new file mode 100644 index 0000000..88586b4 --- /dev/null +++ b/lib/libthr/arch/sparc64/Makefile.inc @@ -0,0 +1,3 @@ +# $FreeBSD$ + +SRCS+= _umtx_op_err.S pthread_md.c diff --git a/lib/libthr/arch/sparc64/include/pthread_md.h b/lib/libthr/arch/sparc64/include/pthread_md.h new file mode 100644 index 0000000..7ee9654 --- /dev/null +++ b/lib/libthr/arch/sparc64/include/pthread_md.h @@ -0,0 +1,84 @@ +/*- + * 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. + */ +#ifndef _PTHREAD_MD_H_ +#define _PTHREAD_MD_H_ + +#include <stddef.h> + +#define CPU_SPINWAIT + +#define DTV_OFFSET offsetof(struct tcb, tcb_dtv) + +/* + * Variant II tcb, first two members are required by rtld. + * %g7 points to the structure. + */ +struct tcb { + struct tcb *tcb_self; /* required by rtld */ + void *tcb_dtv; /* required by rtld */ + struct pthread *tcb_thread; /* our hook */ + void *tcb_spare[1]; +}; + +/* + * The tcb constructors. + */ +struct tcb *_tcb_ctor(struct pthread *, int); +void _tcb_dtor(struct tcb *); + +/* Called from the thread to set its private data. */ +static __inline void +_tcb_set(struct tcb *tcb) +{ + + __asm __volatile("mov %0, %%g7" : : "r" (tcb)); +} + +static __inline struct tcb * +_tcb_get(void) +{ + register struct tcb *tp __asm("%g7"); + + return (tp); +} + +static __inline struct pthread * +_get_curthread(void) +{ + + return (_tcb_get()->tcb_thread); +} + +#define HAS__UMTX_OP_ERR 1 + +#endif /* _PTHREAD_MD_H_ */ diff --git a/lib/libthr/arch/sparc64/sparc64/_umtx_op_err.S b/lib/libthr/arch/sparc64/sparc64/_umtx_op_err.S new file mode 100644 index 0000000..220d279 --- /dev/null +++ b/lib/libthr/arch/sparc64/sparc64/_umtx_op_err.S @@ -0,0 +1,38 @@ +/*- + * Copyright (c) 2011 Marius Strobl <marius@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 <machine/asm.h> +__FBSDID("$FreeBSD$"); + +#include <sys/syscall.h> + +#include <machine/utrap.h> + +ENTRY(_umtx_op_err) + mov SYS__umtx_op, %g1 + retl + ta %xcc, ST_SYSCALL +END(_umtx_op_err) diff --git a/lib/libthr/arch/sparc64/sparc64/pthread_md.c b/lib/libthr/arch/sparc64/sparc64/pthread_md.c new file mode 100644 index 0000000..e1d439a --- /dev/null +++ b/lib/libthr/arch/sparc64/sparc64/pthread_md.c @@ -0,0 +1,56 @@ +/*- + * 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 <rtld_tls.h> + +#include "pthread_md.h" + +struct tcb * +_tcb_ctor(struct pthread *thread, int initial) +{ + struct tcb *tcb; + + if (initial) + tcb = _tcb_get(); + else + tcb = _rtld_allocate_tls(NULL, sizeof(struct tcb), 16); + if (tcb) + tcb->tcb_thread = thread; + return (tcb); +} + +void +_tcb_dtor(struct tcb *tcb) +{ + + _rtld_free_tls(tcb, sizeof(struct tcb), 16); +} diff --git a/lib/libthr/libthr.3 b/lib/libthr/libthr.3 new file mode 100644 index 0000000..696da17 --- /dev/null +++ b/lib/libthr/libthr.3 @@ -0,0 +1,62 @@ +.\" Copyright (c) 2005 Robert N. M. Watson +.\" 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$ +.\" +.Dd October 19, 2007 +.Dt LIBTHR 3 +.Os +.Sh NAME +.Nm libthr +.Nd "1:1 POSIX threads library" +.Sh LIBRARY +.Lb libthr +.Sh SYNOPSIS +.In pthread.h +.Sh DESCRIPTION +The +.Nm +library provides a 1:1 implementation of the +.Xr pthread 3 +library interfaces for application threading. +It +has been optimized for use by applications expecting system scope thread +semantics, and can provide significant performance improvements +compared to +.Lb libkse . +.Sh SEE ALSO +.Xr pthread 3 +.Sh AUTHORS +.An -nosplit +The +.Nm +library +was originally created by +.An "Jeff Roberson" Aq jeff@FreeBSD.org , +and enhanced by +.An "Jonathan Mini" Aq mini@FreeBSD.org +and +.An "Mike Makonnen" Aq mtm@FreeBSD.org . +It has been substantially rewritten and optimized by +.An "David Xu" Aq davidxu@FreeBSD.org . diff --git a/lib/libthr/pthread.map b/lib/libthr/pthread.map new file mode 100644 index 0000000..355edea --- /dev/null +++ b/lib/libthr/pthread.map @@ -0,0 +1,410 @@ +/* + * $FreeBSD$ + */ + +/* + * Use the same naming scheme as libc. + */ +FBSD_1.0 { + __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_getclock; + pthread_condattr_getpshared; + pthread_condattr_init; + pthread_condattr_setclock; + 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_getpshared; + pthread_mutexattr_getprotocol; + 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_set_name_np; + pthread_self; + 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_timedjoin_np; + pthread_yield; + raise; + read; + readv; + recvfrom; + recvmsg; + select; + sendmsg; + sendto; + sigaction; + sigprocmask; + sigsuspend; + sigwait; + sigwaitinfo; + sigtimedwait; + sleep; + system; + tcdrain; + usleep; + wait; + wait3; + wait4; + waitpid; + write; + writev; +}; + +/* + * List the private interfaces reserved for use in FreeBSD libraries. + * These are not part of our application ABI. + */ +FBSDprivate_1.0 { + ___creat; + ___pause; + ___pselect; + ___sigwait; + ___sleep; + ___system; + ___tcdrain; + ___usleep; + ___wait; + ___waitpid; + __accept; + __aio_suspend; + __close; + __connect; + __fcntl; + __fsync; + __msync; + __nanosleep; + __open; + __openat; + __poll; + __pthread_cond_timedwait; + __pthread_cond_wait; + __pthread_cxa_finalize; + __pthread_mutex_init; + __pthread_mutex_lock; + __pthread_mutex_timedlock; + __pthread_mutex_trylock; + __read; + __readv; + __recvfrom; + __recvmsg; + __select; + __sendmsg; + __sendto; + __sigsuspend; + __sigtimedwait; + __sigwaitinfo; + __wait3; + __wait4; + __write; + __writev; + _fork; + _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_getaffinity_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_setaffinity_np; + _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_cancel_enter; + _pthread_cancel_leave; + _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_getclock; + _pthread_condattr_getpshared; + _pthread_condattr_init; + _pthread_condattr_setclock; + _pthread_condattr_setpshared; + _pthread_create; + _pthread_detach; + _pthread_equal; + _pthread_exit; + _pthread_getaffinity_np; + _pthread_getconcurrency; + _pthread_getcpuclockid; + _pthread_getprio; + _pthread_getschedparam; + _pthread_getspecific; + _pthread_getthreadid_np; + _pthread_join; + _pthread_key_create; + _pthread_key_delete; + _pthread_kill; + _pthread_main_np; + _pthread_multi_np; + _pthread_mutex_destroy; + _pthread_mutex_getprioceiling; + _pthread_mutex_getspinloops_np; + _pthread_mutex_getyieldloops_np; + _pthread_mutex_init; + _pthread_mutex_init_calloc_cb; + _pthread_mutex_isowned_np; + _pthread_mutex_lock; + _pthread_mutex_setprioceiling; + _pthread_mutex_setspinloops_np; + _pthread_mutex_setyieldloops_np; + _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_setaffinity_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_timedjoin_np; + _pthread_yield; + _raise; + _setcontext; + _sigaction; + _sigprocmask; + _sigsuspend; + _sigtimedwait; + _sigwait; + _sigwaitinfo; + _spinlock; + _spinlock_debug; + _spinunlock; + _swapcontext; + + /* Debugger needs these. */ + _libthr_debug; + _thread_active_threads; + _thread_bp_create; + _thread_bp_death; + _thread_event_mask; + _thread_keytable; + _thread_last_event; + _thread_list; + _thread_max_keys; + _thread_off_attr_flags; + _thread_off_dtv; + _thread_off_event_buf; + _thread_off_event_mask; + _thread_off_key_allocated; + _thread_off_key_destructor; + _thread_off_linkmap; + _thread_off_next; + _thread_off_report_events; + _thread_off_state; + _thread_off_tcb; + _thread_off_tid; + _thread_off_tlsindex; + _thread_size_key; + _thread_state_running; + _thread_state_zoombie; + + __pthread_map_stacks_exec; +}; + +FBSD_1.1 { + __pthread_cleanup_pop_imp; + __pthread_cleanup_push_imp; + pthread_attr_getaffinity_np; + pthread_attr_setaffinity_np; + pthread_getaffinity_np; + pthread_getcpuclockid; + pthread_setaffinity_np; + pthread_mutex_getspinloops_np; + pthread_mutex_getyieldloops_np; + pthread_mutex_isowned_np; + pthread_mutex_setspinloops_np; + pthread_mutex_setyieldloops_np; +}; + +FBSD_1.2 { + openat; + pthread_getthreadid_np; + setcontext; + swapcontext; +}; diff --git a/lib/libthr/support/Makefile.inc b/lib/libthr/support/Makefile.inc new file mode 100644 index 0000000..35a15f8 --- /dev/null +++ b/lib/libthr/support/Makefile.inc @@ -0,0 +1,29 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/support ${.CURDIR}/../libc/gen ${.CURDIR}/../libc/string + +# libc must search machine_arch, then machine_cpuarch, but libthr has all its +# code implemented in machine_cpuarch. Cope. +.if exists(${.CURDIR}/../libc/${MACHINE_ARCH}/sys) +.PATH: ${.CURDIR}/../libc/${MACHINE_ARCH}/sys +CFLAGS+= -I${.CURDIR}/../libc/${MACHINE_ARCH} +.else +.PATH: ${.CURDIR}/../libc/${MACHINE_CPUARCH}/sys +CFLAGS+= -I${.CURDIR}/../libc/${MACHINE_CPUARCH} +.endif + +SYSCALLS= thr_new + +SYSCALL_SRC= ${SYSCALLS:S/$/.S/} +SYSCALL_OBJ= ${SYSCALLS:S/$/.So/} + +${SYSCALL_SRC}: + printf '#include "SYS.h"\nRSYSCALL(${.PREFIX})\n' > ${.TARGET} + +LIBC_OBJS= + +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/libthr/sys/Makefile.inc b/lib/libthr/sys/Makefile.inc new file mode 100644 index 0000000..70c6dda --- /dev/null +++ b/lib/libthr/sys/Makefile.inc @@ -0,0 +1,5 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/sys + +SRCS+= thr_error.c diff --git a/lib/libthr/sys/thr_error.c b/lib/libthr/sys/thr_error.c new file mode 100644 index 0000000..45295c5 --- /dev/null +++ b/lib/libthr/sys/thr_error.c @@ -0,0 +1,56 @@ +/* + * 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; + +int * +__error(void) +{ + struct pthread *curthread; + + if (_thr_initial != NULL) { + curthread = _get_curthread(); + if (curthread != NULL && curthread != _thr_initial) + return (&curthread->error); + } + return (&errno); +} diff --git a/lib/libthr/thread/Makefile.inc b/lib/libthr/thread/Makefile.inc new file mode 100644 index 0000000..ddde6e9 --- /dev/null +++ b/lib/libthr/thread/Makefile.inc @@ -0,0 +1,58 @@ +# $FreeBSD$ + +# thr sources +.PATH: ${.CURDIR}/thread + +SRCS+= \ + thr_affinity.c \ + thr_attr.c \ + thr_barrier.c \ + thr_barrierattr.c \ + thr_cancel.c \ + thr_clean.c \ + thr_concurrency.c \ + thr_cond.c \ + thr_condattr.c \ + thr_create.c \ + thr_detach.c \ + thr_equal.c \ + thr_event.c \ + thr_exit.c \ + thr_fork.c \ + thr_getprio.c \ + thr_getcpuclockid.c \ + thr_getschedparam.c \ + thr_getthreadid_np.c \ + thr_info.c \ + thr_init.c \ + thr_join.c \ + thr_list.c \ + thr_kern.c \ + thr_kill.c \ + thr_main_np.c \ + thr_multi_np.c \ + thr_mutex.c \ + thr_mutexattr.c \ + thr_once.c \ + thr_printf.c \ + thr_pspinlock.c \ + thr_resume_np.c \ + thr_rtld.c \ + thr_rwlock.c \ + thr_rwlockattr.c \ + thr_self.c \ + thr_sem.c \ + thr_setprio.c \ + thr_setschedparam.c \ + thr_sig.c \ + thr_single_np.c \ + thr_sleepq.c \ + thr_spec.c \ + thr_spinlock.c \ + thr_stack.c \ + thr_syscalls.c \ + thr_suspend_np.c \ + thr_switch_np.c \ + thr_symbols.c \ + thr_umtx.c \ + thr_yield.c diff --git a/lib/libthr/thread/thr_affinity.c b/lib/libthr/thread/thr_affinity.c new file mode 100644 index 0000000..7e9b0f9 --- /dev/null +++ b/lib/libthr/thread/thr_affinity.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2008, 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 "namespace.h" +#include <pthread_np.h> +#include <sys/param.h> +#include <sys/cpuset.h> +#include "un-namespace.h" + +#include "thr_private.h" + +__weak_reference(_pthread_getaffinity_np, pthread_getaffinity_np); +__weak_reference(_pthread_setaffinity_np, pthread_setaffinity_np); + +int +_pthread_setaffinity_np(pthread_t td, size_t cpusetsize, const cpuset_t *cpusetp) +{ + struct pthread *curthread = _get_curthread(); + lwpid_t tid; + int error; + + if (td == curthread) { + error = cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID, + -1, cpusetsize, cpusetp); + if (error == -1) + error = errno; + } else if ((error = _thr_find_thread(curthread, td, 0)) == 0) { + tid = TID(td); + error = cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID, tid, + cpusetsize, cpusetp); + if (error == -1) + error = errno; + THR_THREAD_UNLOCK(curthread, td); + } + return (error); +} + +int +_pthread_getaffinity_np(pthread_t td, size_t cpusetsize, cpuset_t *cpusetp) +{ + struct pthread *curthread = _get_curthread(); + lwpid_t tid; + int error; + + if (td == curthread) { + error = cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID, + -1, cpusetsize, cpusetp); + if (error == -1) + error = errno; + } else if ((error = _thr_find_thread(curthread, td, 0)) == 0) { + tid = TID(td); + error = cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID, tid, + cpusetsize, cpusetp); + if (error == -1) + error = errno; + THR_THREAD_UNLOCK(curthread, td); + } + return (error); +} diff --git a/lib/libthr/thread/thr_attr.c b/lib/libthr/thread/thr_attr.c new file mode 100644 index 0000000..0cac528 --- /dev/null +++ b/lib/libthr/thread/thr_attr.c @@ -0,0 +1,650 @@ +/* + * 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. + * + */ + +/* + * Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>. + * Copyright (C) 2001 Jason Evans <jasone@freebsd.org>. + * 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(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. + */ + +/* + * 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. 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 <stdlib.h> +#include <string.h> +#include <pthread_np.h> +#include <sys/sysctl.h> +#include "un-namespace.h" + +#include "thr_private.h" + +static size_t _get_kern_cpuset_size(void); + +__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 { + if ((*attr)->cpuset != NULL) + free((*attr)->cpuset); + /* 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); +} + +__weak_reference(_pthread_attr_get_np, pthread_attr_get_np); + +int +_pthread_attr_get_np(pthread_t pthread, pthread_attr_t *dstattr) +{ + struct pthread *curthread; + struct pthread_attr attr, *dst; + int ret; + size_t kern_size; + + if (pthread == NULL || dstattr == NULL || (dst = *dstattr) == NULL) + return (EINVAL); + kern_size = _get_kern_cpuset_size(); + if (dst->cpuset == NULL) { + dst->cpuset = calloc(1, kern_size); + dst->cpusetsize = kern_size; + } + curthread = _get_curthread(); + if ((ret = _thr_find_thread(curthread, pthread, /*include dead*/0)) != 0) + return (ret); + attr = pthread->attr; + if (pthread->flags & THR_FLAGS_DETACHED) + attr.flags |= PTHREAD_DETACHED; + ret = cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID, TID(pthread), + dst->cpusetsize, dst->cpuset); + if (ret == -1) + ret = errno; + THR_THREAD_UNLOCK(curthread, pthread); + if (ret == 0) { + memcpy(&dst->pthread_attr_start_copy, + &attr.pthread_attr_start_copy, + offsetof(struct pthread_attr, pthread_attr_end_copy) - + offsetof(struct pthread_attr, pthread_attr_start_copy)); + } + return (ret); +} + +__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); +} + +__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); +} + +__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); +} + +__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); +} + +__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); +} + +__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); +} + +__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); +} + +__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); +} + +__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); +} + +__weak_reference(_pthread_attr_init, pthread_attr_init); + +int +_pthread_attr_init(pthread_attr_t *attr) +{ + int ret; + pthread_attr_t pattr; + + _thr_check_init(); + + /* 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)); + + /* Return a pointer to the attribute object: */ + *attr = pattr; + ret = 0; + } + return(ret); +} + +__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); +} + +__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); +} + +__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); +} + +__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); +} + +__weak_reference(_pthread_attr_setschedparam, pthread_attr_setschedparam); + +int +_pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param) +{ + int policy; + + if ((attr == NULL) || (*attr == NULL)) + return (EINVAL); + + if (param == NULL) + return (ENOTSUP); + + policy = (*attr)->sched_policy; + + if (policy == SCHED_FIFO || policy == SCHED_RR) { + if (param->sched_priority < _thr_priorities[policy-1].pri_min || + param->sched_priority > _thr_priorities[policy-1].pri_max) + return (ENOTSUP); + } else { + /* + * Ignore it for SCHED_OTHER now, patches for glib ports + * are wrongly using M:N thread library's internal macro + * THR_MIN_PRIORITY and THR_MAX_PRIORITY. + */ + } + + (*attr)->prio = param->sched_priority; + + return (0); +} + +__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; + (*attr)->prio = _thr_priorities[policy-1].pri_default; + } + return(ret); +} + +__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); +} + +__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); +} + +__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); +} + +__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); +} + +static size_t +_get_kern_cpuset_size(void) +{ + static int kern_cpuset_size = 0; + + if (kern_cpuset_size == 0) { + size_t len; + + len = sizeof(kern_cpuset_size); + if (sysctlbyname("kern.sched.cpusetsize", &kern_cpuset_size, + &len, NULL, 0)) + PANIC("failed to get sysctl kern.sched.cpusetsize"); + } + + return (kern_cpuset_size); +} + +__weak_reference(_pthread_attr_setaffinity_np, pthread_attr_setaffinity_np); +int +_pthread_attr_setaffinity_np(pthread_attr_t *pattr, size_t cpusetsize, + const cpuset_t *cpusetp) +{ + pthread_attr_t attr; + int ret; + + if (pattr == NULL || (attr = (*pattr)) == NULL) + ret = EINVAL; + else { + if (cpusetsize == 0 || cpusetp == NULL) { + if (attr->cpuset != NULL) { + free(attr->cpuset); + attr->cpuset = NULL; + attr->cpusetsize = 0; + } + return (0); + } + size_t kern_size = _get_kern_cpuset_size(); + /* Kernel rejects small set, we check it here too. */ + if (cpusetsize < kern_size) + return (ERANGE); + if (cpusetsize > kern_size) { + /* Kernel checks invalid bits, we check it here too. */ + size_t i; + for (i = kern_size; i < cpusetsize; ++i) { + if (((char *)cpusetp)[i]) + return (EINVAL); + } + } + if (attr->cpuset == NULL) { + attr->cpuset = calloc(1, kern_size); + if (attr->cpuset == NULL) + return (errno); + attr->cpusetsize = kern_size; + } + memcpy(attr->cpuset, cpusetp, kern_size); + ret = 0; + } + return (ret); +} + +__weak_reference(_pthread_attr_getaffinity_np, pthread_attr_getaffinity_np); +int +_pthread_attr_getaffinity_np(const pthread_attr_t *pattr, size_t cpusetsize, + cpuset_t *cpusetp) +{ + pthread_attr_t attr; + int ret = 0; + + if (pattr == NULL || (attr = (*pattr)) == NULL) + ret = EINVAL; + else { + /* Kernel rejects small set, we check it here too. */ + size_t kern_size = _get_kern_cpuset_size(); + if (cpusetsize < kern_size) + return (ERANGE); + if (attr->cpuset != NULL) + memcpy(cpusetp, attr->cpuset, MIN(cpusetsize, + attr->cpusetsize)); + else + memset(cpusetp, -1, kern_size); + if (cpusetsize > kern_size) + memset(((char *)cpusetp) + kern_size, 0, + cpusetsize - kern_size); + } + return (ret); +} diff --git a/lib/libthr/thread/thr_autoinit.c b/lib/libthr/thread/thr_autoinit.c new file mode 100644 index 0000000..c425569 --- /dev/null +++ b/lib/libthr/thread/thr_autoinit.c @@ -0,0 +1,65 @@ +/* + * 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$ + */ + +#include <pthread.h> + +#include "thr_private.h" + +/* + * This module uses GCC extentions to initialize the + * threads package at program start-up time. + */ + +void _thread_init_hack(void) __attribute__ ((constructor)); + +void +_thread_init_hack(void) +{ + + _thread_init(); +} + +/* + * 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/libthr/thread/thr_barrier.c b/lib/libthr/thread/thr_barrier.c new file mode 100644 index 0000000..86f880e --- /dev/null +++ b/lib/libthr/thread/thr_barrier.c @@ -0,0 +1,138 @@ +/*- + * 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 "namespace.h" +#include <errno.h> +#include <stdlib.h> +#include <pthread.h> +#include "un-namespace.h" + +#include "thr_private.h" + +__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; + struct pthread *curthread; + + if (barrier == NULL || *barrier == NULL) + return (EINVAL); + + curthread = _get_curthread(); + bar = *barrier; + THR_UMUTEX_LOCK(curthread, &bar->b_lock); + if (bar->b_destroying) { + THR_UMUTEX_UNLOCK(curthread, &bar->b_lock); + return (EBUSY); + } + bar->b_destroying = 1; + do { + if (bar->b_waiters > 0) { + bar->b_destroying = 0; + THR_UMUTEX_UNLOCK(curthread, &bar->b_lock); + return (EBUSY); + } + if (bar->b_refcount != 0) { + _thr_ucond_wait(&bar->b_cv, &bar->b_lock, NULL, 0); + THR_UMUTEX_LOCK(curthread, &bar->b_lock); + } else + break; + } while (1); + bar->b_destroying = 0; + THR_UMUTEX_UNLOCK(curthread, &bar->b_lock); + + *barrier = NULL; + free(bar); + return (0); +} + +int +_pthread_barrier_init(pthread_barrier_t *barrier, + const pthread_barrierattr_t *attr, unsigned count) +{ + pthread_barrier_t bar; + + (void)attr; + + if (barrier == NULL || count <= 0) + return (EINVAL); + + bar = malloc(sizeof(struct pthread_barrier)); + if (bar == NULL) + return (ENOMEM); + + _thr_umutex_init(&bar->b_lock); + _thr_ucond_init(&bar->b_cv); + bar->b_cycle = 0; + bar->b_waiters = 0; + bar->b_count = count; + bar->b_refcount = 0; + *barrier = bar; + + return (0); +} + +int +_pthread_barrier_wait(pthread_barrier_t *barrier) +{ + struct pthread *curthread = _get_curthread(); + pthread_barrier_t bar; + int64_t cycle; + int ret; + + if (barrier == NULL || *barrier == NULL) + return (EINVAL); + + bar = *barrier; + THR_UMUTEX_LOCK(curthread, &bar->b_lock); + if (++bar->b_waiters == bar->b_count) { + /* Current thread is lastest thread */ + bar->b_waiters = 0; + bar->b_cycle++; + _thr_ucond_broadcast(&bar->b_cv); + THR_UMUTEX_UNLOCK(curthread, &bar->b_lock); + ret = PTHREAD_BARRIER_SERIAL_THREAD; + } else { + cycle = bar->b_cycle; + bar->b_refcount++; + do { + _thr_ucond_wait(&bar->b_cv, &bar->b_lock, NULL, 0); + THR_UMUTEX_LOCK(curthread, &bar->b_lock); + /* test cycle to avoid bogus wakeup */ + } while (cycle == bar->b_cycle); + if (--bar->b_refcount == 0 && bar->b_destroying) + _thr_ucond_broadcast(&bar->b_cv); + THR_UMUTEX_UNLOCK(curthread, &bar->b_lock); + ret = 0; + } + return (ret); +} diff --git a/lib/libthr/thread/thr_barrierattr.c b/lib/libthr/thread/thr_barrierattr.c new file mode 100644 index 0000000..32b9723 --- /dev/null +++ b/lib/libthr/thread/thr_barrierattr.c @@ -0,0 +1,96 @@ +/* + * 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 "namespace.h" +#include <errno.h> +#include <stdlib.h> +#include <pthread.h> +#include "un-namespace.h" + +#include "thr_private.h" + +__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/libthr/thread/thr_cancel.c b/lib/libthr/thread/thr_cancel.c new file mode 100644 index 0000000..89f0ee1 --- /dev/null +++ b/lib/libthr/thread/thr_cancel.c @@ -0,0 +1,176 @@ +/* + * 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 "namespace.h" +#include <pthread.h> +#include "un-namespace.h" + +#include "thr_private.h" + +__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 void +testcancel(struct pthread *curthread) +{ + if (__predict_false(SHOULD_CANCEL(curthread) && + !THR_IN_CRITICAL(curthread))) + _pthread_exit(PTHREAD_CANCELED); +} + +void +_thr_testcancel(struct pthread *curthread) +{ + testcancel(curthread); +} + +int +_pthread_cancel(pthread_t pthread) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + /* + * POSIX says _pthread_cancel should be async cancellation safe. + * _thr_find_thread and THR_THREAD_UNLOCK will enter and leave critical + * region automatically. + */ + if ((ret = _thr_find_thread(curthread, pthread, 0)) == 0) { + if (!pthread->cancel_pending) { + pthread->cancel_pending = 1; + if (pthread->state != PS_DEAD) + _thr_send_sig(pthread, SIGCANCEL); + } + THR_THREAD_UNLOCK(curthread, pthread); + } + return (ret); +} + +int +_pthread_setcancelstate(int state, int *oldstate) +{ + struct pthread *curthread = _get_curthread(); + int oldval; + + oldval = curthread->cancel_enable; + switch (state) { + case PTHREAD_CANCEL_DISABLE: + curthread->cancel_enable = 0; + break; + case PTHREAD_CANCEL_ENABLE: + curthread->cancel_enable = 1; + testcancel(curthread); + break; + default: + return (EINVAL); + } + + if (oldstate) { + *oldstate = oldval ? PTHREAD_CANCEL_ENABLE : + PTHREAD_CANCEL_DISABLE; + } + return (0); +} + +int +_pthread_setcanceltype(int type, int *oldtype) +{ + struct pthread *curthread = _get_curthread(); + int oldval; + + oldval = curthread->cancel_async; + switch (type) { + case PTHREAD_CANCEL_ASYNCHRONOUS: + curthread->cancel_async = 1; + testcancel(curthread); + break; + case PTHREAD_CANCEL_DEFERRED: + curthread->cancel_async = 0; + break; + default: + return (EINVAL); + } + + if (oldtype) { + *oldtype = oldval ? PTHREAD_CANCEL_ASYNCHRONOUS : + PTHREAD_CANCEL_DEFERRED; + } + return (0); +} + +void +_pthread_testcancel(void) +{ + struct pthread *curthread = _get_curthread(); + + testcancel(curthread); +} + +void +_thr_cancel_enter(struct pthread *curthread) +{ + curthread->cancel_point = 1; + testcancel(curthread); +} + +void +_thr_cancel_enter2(struct pthread *curthread, int maycancel) +{ + curthread->cancel_point = 1; + if (__predict_false(SHOULD_CANCEL(curthread) && + !THR_IN_CRITICAL(curthread))) { + if (!maycancel) + thr_wake(curthread->tid); + else + _pthread_exit(PTHREAD_CANCELED); + } +} + +void +_thr_cancel_leave(struct pthread *curthread, int maycancel) +{ + curthread->cancel_point = 0; + if (__predict_false(SHOULD_CANCEL(curthread) && + !THR_IN_CRITICAL(curthread) && maycancel)) + _pthread_exit(PTHREAD_CANCELED); +} + +void +_pthread_cancel_enter(int maycancel) +{ + _thr_cancel_enter2(_get_curthread(), maycancel); +} + +void +_pthread_cancel_leave(int maycancel) +{ + _thr_cancel_leave(_get_curthread(), maycancel); +} diff --git a/lib/libthr/thread/thr_clean.c b/lib/libthr/thread/thr_clean.c new file mode 100644 index 0000000..dc5b0c7 --- /dev/null +++ b/lib/libthr/thread/thr_clean.c @@ -0,0 +1,100 @@ +/* + * 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. 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 <signal.h> +#include <errno.h> +#include <stdlib.h> +#include <pthread.h> +#include "un-namespace.h" + +#include "thr_private.h" + +#undef pthread_cleanup_push +#undef pthread_cleanup_pop + +/* old binary compatible interfaces */ +__weak_reference(_pthread_cleanup_push, pthread_cleanup_push); +__weak_reference(_pthread_cleanup_pop, pthread_cleanup_pop); + +void +__pthread_cleanup_push_imp(void (*routine)(void *), void *arg, + struct _pthread_cleanup_info *info) +{ + struct pthread *curthread = _get_curthread(); + struct pthread_cleanup *newbuf; + + newbuf = (void *)info; + newbuf->routine = routine; + newbuf->routine_arg = arg; + newbuf->onheap = 0; + newbuf->prev = curthread->cleanup; + curthread->cleanup = newbuf; +} + +void +__pthread_cleanup_pop_imp(int execute) +{ + struct pthread *curthread = _get_curthread(); + struct pthread_cleanup *old; + + if ((old = curthread->cleanup) != NULL) { + curthread->cleanup = old->prev; + if (execute) + old->routine(old->routine_arg); + if (old->onheap) + free(old); + } +} + +void +_pthread_cleanup_push(void (*routine) (void *), void *arg) +{ + struct pthread *curthread = _get_curthread(); + struct pthread_cleanup *newbuf; +#ifdef _PTHREAD_FORCED_UNWIND + curthread->unwind_disabled = 1; +#endif + if ((newbuf = (struct pthread_cleanup *) + malloc(sizeof(struct _pthread_cleanup_info))) != NULL) { + newbuf->routine = routine; + newbuf->routine_arg = arg; + newbuf->onheap = 1; + newbuf->prev = curthread->cleanup; + curthread->cleanup = newbuf; + } +} + +void +_pthread_cleanup_pop(int execute) +{ + __pthread_cleanup_pop_imp(execute); +} diff --git a/lib/libthr/thread/thr_concurrency.c b/lib/libthr/thread/thr_concurrency.c new file mode 100644 index 0000000..61f0c4a --- /dev/null +++ b/lib/libthr/thread/thr_concurrency.c @@ -0,0 +1,65 @@ +/* + * 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. 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 Sergey Osokin. + * 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 SERGEY OSOKIN 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" + +static int current_concurrency = 0; + +__weak_reference(_pthread_getconcurrency, pthread_getconcurrency); +__weak_reference(_pthread_setconcurrency, pthread_setconcurrency); + +int +_pthread_getconcurrency(void) +{ + return current_concurrency; +} + +int +_pthread_setconcurrency(int new_level) +{ + int ret; + + if (new_level < 0) { + ret = EINVAL; + } else { + current_concurrency = new_level; + ret = 0; + } + return (ret); +} diff --git a/lib/libthr/thread/thr_cond.c b/lib/libthr/thread/thr_cond.c new file mode 100644 index 0000000..a834711 --- /dev/null +++ b/lib/libthr/thread/thr_cond.c @@ -0,0 +1,476 @@ +/* + * 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 "namespace.h" +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <pthread.h> +#include <limits.h> +#include "un-namespace.h" + +#include "thr_private.h" + +/* + * Prototypes + */ +int __pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); +int __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, + const struct timespec * abstime); +static int cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr); +static int cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex, + const struct timespec *abstime, int cancel); +static int cond_signal_common(pthread_cond_t *cond); +static int cond_broadcast_common(pthread_cond_t *cond); + +/* + * 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); + +#define CV_PSHARED(cvp) (((cvp)->__flags & USYNC_PROCESS_SHARED) != 0) + +static int +cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr) +{ + struct pthread_cond *cvp; + int error = 0; + + if ((cvp = (pthread_cond_t) + calloc(1, sizeof(struct pthread_cond))) == NULL) { + error = ENOMEM; + } else { + /* + * Initialise the condition variable structure: + */ + if (cond_attr == NULL || *cond_attr == NULL) { + cvp->__clock_id = CLOCK_REALTIME; + } else { + if ((*cond_attr)->c_pshared) + cvp->__flags |= USYNC_PROCESS_SHARED; + cvp->__clock_id = (*cond_attr)->c_clockid; + } + *cond = cvp; + } + return (error); +} + +static int +init_static(struct pthread *thread, pthread_cond_t *cond) +{ + int ret; + + THR_LOCK_ACQUIRE(thread, &_cond_static_lock); + + if (*cond == NULL) + ret = cond_init(cond, NULL); + else + ret = 0; + + THR_LOCK_RELEASE(thread, &_cond_static_lock); + + return (ret); +} + +#define CHECK_AND_INIT_COND \ + if (__predict_false((cvp = (*cond)) <= THR_COND_DESTROYED)) { \ + if (cvp == THR_COND_INITIALIZER) { \ + int ret; \ + ret = init_static(_get_curthread(), cond); \ + if (ret) \ + return (ret); \ + } else if (cvp == THR_COND_DESTROYED) { \ + return (EINVAL); \ + } \ + cvp = *cond; \ + } + +int +_pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr) +{ + + *cond = NULL; + return (cond_init(cond, cond_attr)); +} + +int +_pthread_cond_destroy(pthread_cond_t *cond) +{ + struct pthread_cond *cvp; + int error = 0; + + if ((cvp = *cond) == THR_COND_INITIALIZER) + error = 0; + else if (cvp == THR_COND_DESTROYED) + error = EINVAL; + else { + cvp = *cond; + *cond = THR_COND_DESTROYED; + + /* + * Free the memory allocated for the condition + * variable structure: + */ + free(cvp); + } + return (error); +} + +/* + * Cancellation behaivor: + * Thread may be canceled at start, if thread is canceled, it means it + * did not get a wakeup from pthread_cond_signal(), otherwise, it is + * not canceled. + * Thread cancellation never cause wakeup from pthread_cond_signal() + * to be lost. + */ +static int +cond_wait_kernel(struct pthread_cond *cvp, struct pthread_mutex *mp, + const struct timespec *abstime, int cancel) +{ + struct pthread *curthread = _get_curthread(); + int recurse; + int error, error2 = 0; + + error = _mutex_cv_detach(mp, &recurse); + if (error != 0) + return (error); + + if (cancel) { + _thr_cancel_enter2(curthread, 0); + error = _thr_ucond_wait((struct ucond *)&cvp->__has_kern_waiters, + (struct umutex *)&mp->m_lock, abstime, + CVWAIT_ABSTIME|CVWAIT_CLOCKID); + _thr_cancel_leave(curthread, 0); + } else { + error = _thr_ucond_wait((struct ucond *)&cvp->__has_kern_waiters, + (struct umutex *)&mp->m_lock, abstime, + CVWAIT_ABSTIME|CVWAIT_CLOCKID); + } + + /* + * Note that PP mutex and ROBUST mutex may return + * interesting error codes. + */ + if (error == 0) { + error2 = _mutex_cv_lock(mp, recurse); + } else if (error == EINTR || error == ETIMEDOUT) { + error2 = _mutex_cv_lock(mp, recurse); + if (error2 == 0 && cancel) + _thr_testcancel(curthread); + if (error == EINTR) + error = 0; + } else { + /* We know that it didn't unlock the mutex. */ + error2 = _mutex_cv_attach(mp, recurse); + if (error2 == 0 && cancel) + _thr_testcancel(curthread); + } + return (error2 != 0 ? error2 : error); +} + +/* + * Thread waits in userland queue whenever possible, when thread + * is signaled or broadcasted, it is removed from the queue, and + * is saved in curthread's defer_waiters[] buffer, but won't be + * woken up until mutex is unlocked. + */ + +static int +cond_wait_user(struct pthread_cond *cvp, struct pthread_mutex *mp, + const struct timespec *abstime, int cancel) +{ + struct pthread *curthread = _get_curthread(); + struct sleepqueue *sq; + int recurse; + int error; + + if (curthread->wchan != NULL) + PANIC("thread was already on queue."); + + if (cancel) + _thr_testcancel(curthread); + + _sleepq_lock(cvp); + /* + * set __has_user_waiters before unlocking mutex, this allows + * us to check it without locking in pthread_cond_signal(). + */ + cvp->__has_user_waiters = 1; + curthread->will_sleep = 1; + (void)_mutex_cv_unlock(mp, &recurse); + curthread->mutex_obj = mp; + _sleepq_add(cvp, curthread); + for(;;) { + _thr_clear_wake(curthread); + _sleepq_unlock(cvp); + + if (cancel) { + _thr_cancel_enter2(curthread, 0); + error = _thr_sleep(curthread, cvp->__clock_id, abstime); + _thr_cancel_leave(curthread, 0); + } else { + error = _thr_sleep(curthread, cvp->__clock_id, abstime); + } + + _sleepq_lock(cvp); + if (curthread->wchan == NULL) { + error = 0; + break; + } else if (cancel && SHOULD_CANCEL(curthread)) { + sq = _sleepq_lookup(cvp); + cvp->__has_user_waiters = + _sleepq_remove(sq, curthread); + _sleepq_unlock(cvp); + curthread->mutex_obj = NULL; + _mutex_cv_lock(mp, recurse); + if (!THR_IN_CRITICAL(curthread)) + _pthread_exit(PTHREAD_CANCELED); + else /* this should not happen */ + return (0); + } else if (error == ETIMEDOUT) { + sq = _sleepq_lookup(cvp); + cvp->__has_user_waiters = + _sleepq_remove(sq, curthread); + break; + } + } + _sleepq_unlock(cvp); + curthread->mutex_obj = NULL; + _mutex_cv_lock(mp, recurse); + return (error); +} + +static int +cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex, + const struct timespec *abstime, int cancel) +{ + struct pthread *curthread = _get_curthread(); + struct pthread_cond *cvp; + struct pthread_mutex *mp; + int error; + + CHECK_AND_INIT_COND + + mp = *mutex; + + if ((error = _mutex_owned(curthread, mp)) != 0) + return (error); + + if (curthread->attr.sched_policy != SCHED_OTHER || + (mp->m_lock.m_flags & (UMUTEX_PRIO_PROTECT|UMUTEX_PRIO_INHERIT| + USYNC_PROCESS_SHARED)) != 0 || + (cvp->__flags & USYNC_PROCESS_SHARED) != 0) + return cond_wait_kernel(cvp, mp, abstime, cancel); + else + return cond_wait_user(cvp, mp, abstime, cancel); +} + +int +_pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) +{ + + return (cond_wait_common(cond, mutex, NULL, 0)); +} + +int +__pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) +{ + + return (cond_wait_common(cond, mutex, NULL, 1)); +} + +int +_pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, + const struct timespec * abstime) +{ + + if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || + abstime->tv_nsec >= 1000000000) + return (EINVAL); + + return (cond_wait_common(cond, mutex, abstime, 0)); +} + +int +__pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, + const struct timespec *abstime) +{ + + if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || + abstime->tv_nsec >= 1000000000) + return (EINVAL); + + return (cond_wait_common(cond, mutex, abstime, 1)); +} + +static int +cond_signal_common(pthread_cond_t *cond) +{ + struct pthread *curthread = _get_curthread(); + struct pthread *td; + struct pthread_cond *cvp; + struct pthread_mutex *mp; + struct sleepqueue *sq; + int *waddr; + int pshared; + + /* + * If the condition variable is statically initialized, perform dynamic + * initialization. + */ + CHECK_AND_INIT_COND + + pshared = CV_PSHARED(cvp); + + _thr_ucond_signal((struct ucond *)&cvp->__has_kern_waiters); + + if (pshared || cvp->__has_user_waiters == 0) + return (0); + + curthread = _get_curthread(); + waddr = NULL; + _sleepq_lock(cvp); + sq = _sleepq_lookup(cvp); + if (sq == NULL) { + _sleepq_unlock(cvp); + return (0); + } + + td = _sleepq_first(sq); + mp = td->mutex_obj; + cvp->__has_user_waiters = _sleepq_remove(sq, td); + if (mp->m_owner == curthread) { + if (curthread->nwaiter_defer >= MAX_DEFER_WAITERS) { + _thr_wake_all(curthread->defer_waiters, + curthread->nwaiter_defer); + curthread->nwaiter_defer = 0; + } + curthread->defer_waiters[curthread->nwaiter_defer++] = + &td->wake_addr->value; + mp->m_flags |= PMUTEX_FLAG_DEFERED; + } else { + waddr = &td->wake_addr->value; + } + _sleepq_unlock(cvp); + if (waddr != NULL) + _thr_set_wake(waddr); + return (0); +} + +struct broadcast_arg { + struct pthread *curthread; + unsigned int *waddrs[MAX_DEFER_WAITERS]; + int count; +}; + +static void +drop_cb(struct pthread *td, void *arg) +{ + struct broadcast_arg *ba = arg; + struct pthread_mutex *mp; + struct pthread *curthread = ba->curthread; + + mp = td->mutex_obj; + if (mp->m_owner == curthread) { + if (curthread->nwaiter_defer >= MAX_DEFER_WAITERS) { + _thr_wake_all(curthread->defer_waiters, + curthread->nwaiter_defer); + curthread->nwaiter_defer = 0; + } + curthread->defer_waiters[curthread->nwaiter_defer++] = + &td->wake_addr->value; + mp->m_flags |= PMUTEX_FLAG_DEFERED; + } else { + if (ba->count >= MAX_DEFER_WAITERS) { + _thr_wake_all(ba->waddrs, ba->count); + ba->count = 0; + } + ba->waddrs[ba->count++] = &td->wake_addr->value; + } +} + +static int +cond_broadcast_common(pthread_cond_t *cond) +{ + int pshared; + struct pthread_cond *cvp; + struct sleepqueue *sq; + struct broadcast_arg ba; + + /* + * If the condition variable is statically initialized, perform dynamic + * initialization. + */ + CHECK_AND_INIT_COND + + pshared = CV_PSHARED(cvp); + + _thr_ucond_broadcast((struct ucond *)&cvp->__has_kern_waiters); + + if (pshared || cvp->__has_user_waiters == 0) + return (0); + + ba.curthread = _get_curthread(); + ba.count = 0; + + _sleepq_lock(cvp); + sq = _sleepq_lookup(cvp); + if (sq == NULL) { + _sleepq_unlock(cvp); + return (0); + } + _sleepq_drop(sq, drop_cb, &ba); + cvp->__has_user_waiters = 0; + _sleepq_unlock(cvp); + if (ba.count > 0) + _thr_wake_all(ba.waddrs, ba.count); + return (0); +} + +int +_pthread_cond_signal(pthread_cond_t * cond) +{ + + return (cond_signal_common(cond)); +} + +int +_pthread_cond_broadcast(pthread_cond_t * cond) +{ + + return (cond_broadcast_common(cond)); +} diff --git a/lib/libthr/thread/thr_condattr.c b/lib/libthr/thread/thr_condattr.c new file mode 100644 index 0000000..52b9f51 --- /dev/null +++ b/lib/libthr/thread/thr_condattr.c @@ -0,0 +1,124 @@ +/* + * 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. 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 <stdlib.h> +#include <string.h> +#include <errno.h> +#include <pthread.h> +#include "un-namespace.h" + +#include "thr_private.h" + +__weak_reference(_pthread_condattr_init, pthread_condattr_init); +__weak_reference(_pthread_condattr_destroy, pthread_condattr_destroy); +__weak_reference(_pthread_condattr_getclock, pthread_condattr_getclock); +__weak_reference(_pthread_condattr_setclock, pthread_condattr_setclock); +__weak_reference(_pthread_condattr_getpshared, pthread_condattr_getpshared); +__weak_reference(_pthread_condattr_setpshared, pthread_condattr_setpshared); + +int +_pthread_condattr_init(pthread_condattr_t *attr) +{ + pthread_condattr_t pattr; + int ret; + + 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); +} + +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); +} + +int +_pthread_condattr_getclock(const pthread_condattr_t *attr, clockid_t *clock_id) +{ + if (attr == NULL || *attr == NULL) + return (EINVAL); + *clock_id = (*attr)->c_clockid; + return (0); +} + +int +_pthread_condattr_setclock(pthread_condattr_t *attr, clockid_t clock_id) +{ + if (attr == NULL || *attr == NULL) + return (EINVAL); + if (clock_id != CLOCK_REALTIME && + clock_id != CLOCK_VIRTUAL && + clock_id != CLOCK_PROF && + clock_id != CLOCK_MONOTONIC) { + return (EINVAL); + } + (*attr)->c_clockid = clock_id; + return (0); +} + +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/libthr/thread/thr_create.c b/lib/libthr/thread/thr_create.c new file mode 100644 index 0000000..a41b33f --- /dev/null +++ b/lib/libthr/thread/thr_create.c @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2003 Daniel M. Eischen <deischen@gdeb.com> + * 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 "namespace.h" +#include <sys/types.h> +#include <sys/rtprio.h> +#include <sys/signalvar.h> +#include <errno.h> +#include <link.h> +#include <stdlib.h> +#include <string.h> +#include <stddef.h> +#include <pthread.h> +#include <pthread_np.h> +#include "un-namespace.h" + +#include "thr_private.h" + +static int create_stack(struct pthread_attr *pattr); +static void thread_start(struct pthread *curthread); + +__weak_reference(_pthread_create, pthread_create); + +int +_pthread_create(pthread_t * thread, const pthread_attr_t * attr, + void *(*start_routine) (void *), void *arg) +{ + struct pthread *curthread, *new_thread; + struct thr_param param; + struct sched_param sched_param; + struct rtprio rtp; + int ret = 0, locked, create_suspended; + sigset_t set, oset; + cpuset_t *cpusetp = NULL; + int cpusetsize = 0; + int old_stack_prot; + + _thr_check_init(); + + /* + * Tell libc and others now they need lock to protect their data. + */ + if (_thr_isthreaded() == 0 && _thr_setthreaded(1)) + return (EAGAIN); + + curthread = _get_curthread(); + if ((new_thread = _thr_alloc(curthread)) == NULL) + return (EAGAIN); + + memset(¶m, 0, sizeof(param)); + + if (attr == NULL || *attr == NULL) + /* Use the default thread attributes: */ + new_thread->attr = _pthread_attr_default; + else { + new_thread->attr = *(*attr); + cpusetp = new_thread->attr.cpuset; + cpusetsize = new_thread->attr.cpusetsize; + new_thread->attr.cpuset = NULL; + new_thread->attr.cpusetsize = 0; + } + if (new_thread->attr.sched_inherit == PTHREAD_INHERIT_SCHED) { + /* inherit scheduling contention scope */ + if (curthread->attr.flags & PTHREAD_SCOPE_SYSTEM) + new_thread->attr.flags |= PTHREAD_SCOPE_SYSTEM; + else + new_thread->attr.flags &= ~PTHREAD_SCOPE_SYSTEM; + + new_thread->attr.prio = curthread->attr.prio; + new_thread->attr.sched_policy = curthread->attr.sched_policy; + } + + new_thread->tid = TID_TERMINATED; + + old_stack_prot = _rtld_get_stack_prot(); + if (create_stack(&new_thread->attr) != 0) { + /* Insufficient memory to create a stack: */ + _thr_free(curthread, new_thread); + return (EAGAIN); + } + /* + * Write a magic value to the thread structure + * to help identify valid ones: + */ + new_thread->magic = THR_MAGIC; + new_thread->start_routine = start_routine; + new_thread->arg = arg; + new_thread->cancel_enable = 1; + new_thread->cancel_async = 0; + /* Initialize the mutex queue: */ + TAILQ_INIT(&new_thread->mutexq); + TAILQ_INIT(&new_thread->pp_mutexq); + + /* Initialise hooks in the thread structure: */ + if (new_thread->attr.suspend == THR_CREATE_SUSPENDED) { + new_thread->flags = THR_FLAGS_NEED_SUSPEND; + create_suspended = 1; + } else { + create_suspended = 0; + } + + new_thread->state = PS_RUNNING; + + if (new_thread->attr.flags & PTHREAD_CREATE_DETACHED) + new_thread->flags |= THR_FLAGS_DETACHED; + + /* Add the new thread. */ + new_thread->refcount = 1; + _thr_link(curthread, new_thread); + + /* + * Handle the race between __pthread_map_stacks_exec and + * thread linkage. + */ + if (old_stack_prot != _rtld_get_stack_prot()) + _thr_stack_fix_protection(new_thread); + + /* Return thread pointer eariler so that new thread can use it. */ + (*thread) = new_thread; + if (SHOULD_REPORT_EVENT(curthread, TD_CREATE) || cpusetp != NULL) { + THR_THREAD_LOCK(curthread, new_thread); + locked = 1; + } else + locked = 0; + param.start_func = (void (*)(void *)) thread_start; + param.arg = new_thread; + param.stack_base = new_thread->attr.stackaddr_attr; + param.stack_size = new_thread->attr.stacksize_attr; + param.tls_base = (char *)new_thread->tcb; + param.tls_size = sizeof(struct tcb); + param.child_tid = &new_thread->tid; + param.parent_tid = &new_thread->tid; + param.flags = 0; + if (new_thread->attr.flags & PTHREAD_SCOPE_SYSTEM) + param.flags |= THR_SYSTEM_SCOPE; + if (new_thread->attr.sched_inherit == PTHREAD_INHERIT_SCHED) + param.rtp = NULL; + else { + sched_param.sched_priority = new_thread->attr.prio; + _schedparam_to_rtp(new_thread->attr.sched_policy, + &sched_param, &rtp); + param.rtp = &rtp; + } + + /* Schedule the new thread. */ + if (create_suspended) { + SIGFILLSET(set); + SIGDELSET(set, SIGTRAP); + __sys_sigprocmask(SIG_SETMASK, &set, &oset); + new_thread->sigmask = oset; + SIGDELSET(new_thread->sigmask, SIGCANCEL); + } + + ret = thr_new(¶m, sizeof(param)); + + if (ret != 0) { + ret = errno; + /* + * Translate EPROCLIM into well-known POSIX code EAGAIN. + */ + if (ret == EPROCLIM) + ret = EAGAIN; + } + + if (create_suspended) + __sys_sigprocmask(SIG_SETMASK, &oset, NULL); + + if (ret != 0) { + if (!locked) + THR_THREAD_LOCK(curthread, new_thread); + new_thread->state = PS_DEAD; + new_thread->tid = TID_TERMINATED; + new_thread->flags |= THR_FLAGS_DETACHED; + new_thread->refcount--; + if (new_thread->flags & THR_FLAGS_NEED_SUSPEND) { + new_thread->cycle++; + _thr_umtx_wake(&new_thread->cycle, INT_MAX, 0); + } + _thr_try_gc(curthread, new_thread); /* thread lock released */ + atomic_add_int(&_thread_active_threads, -1); + } else if (locked) { + if (cpusetp != NULL) { + if (cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID, + TID(new_thread), cpusetsize, cpusetp)) { + ret = errno; + /* kill the new thread */ + new_thread->force_exit = 1; + new_thread->flags |= THR_FLAGS_DETACHED; + _thr_try_gc(curthread, new_thread); + /* thread lock released */ + goto out; + } + } + + _thr_report_creation(curthread, new_thread); + THR_THREAD_UNLOCK(curthread, new_thread); + } +out: + if (ret) + (*thread) = 0; + return (ret); +} + +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 +thread_start(struct pthread *curthread) +{ + sigset_t set; + + if (curthread->attr.suspend == THR_CREATE_SUSPENDED) + set = curthread->sigmask; + + /* + * This is used as a serialization point to allow parent + * to report 'new thread' event to debugger or tweak new thread's + * attributes before the new thread does real-world work. + */ + THR_LOCK(curthread); + THR_UNLOCK(curthread); + + if (curthread->force_exit) + _pthread_exit(PTHREAD_CANCELED); + + if (curthread->attr.suspend == THR_CREATE_SUSPENDED) { +#if 0 + /* Done in THR_UNLOCK() */ + _thr_ast(curthread); +#endif + + /* + * Parent thread have stored signal mask for us, + * we should restore it now. + */ + __sys_sigprocmask(SIG_SETMASK, &set, NULL); + } + +#ifdef _PTHREAD_FORCED_UNWIND + curthread->unwind_stackend = (char *)curthread->attr.stackaddr_attr + + curthread->attr.stacksize_attr; +#endif + + /* Run the current thread's start routine with argument: */ + _pthread_exit(curthread->start_routine(curthread->arg)); + + /* This point should never be reached. */ + PANIC("Thread has resumed after exit"); +} diff --git a/lib/libthr/thread/thr_detach.c b/lib/libthr/thread/thr_detach.c new file mode 100644 index 0000000..c494f7c --- /dev/null +++ b/lib/libthr/thread/thr_detach.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2005 David Xu <davidxu@freebsd.org> + * Copyright (C) 2003 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 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 "namespace.h" +#include <sys/types.h> +#include <errno.h> +#include <pthread.h> +#include "un-namespace.h" + +#include "thr_private.h" + +__weak_reference(_pthread_detach, pthread_detach); + +int +_pthread_detach(pthread_t pthread) +{ + struct pthread *curthread = _get_curthread(); + int rval; + + if (pthread == NULL) + return (EINVAL); + + if ((rval = _thr_find_thread(curthread, pthread, + /*include dead*/1)) != 0) { + return (rval); + } + + /* Check if the thread is already detached or has a joiner. */ + if ((pthread->flags & THR_FLAGS_DETACHED) != 0 || + (pthread->joiner != NULL)) { + THR_THREAD_UNLOCK(curthread, pthread); + return (EINVAL); + } + + /* Flag the thread as detached. */ + pthread->flags |= THR_FLAGS_DETACHED; + _thr_try_gc(curthread, pthread); /* thread lock released */ + + return (0); +} diff --git a/lib/libthr/thread/thr_equal.c b/lib/libthr/thread/thr_equal.c new file mode 100644 index 0000000..2f602b5 --- /dev/null +++ b/lib/libthr/thread/thr_equal.c @@ -0,0 +1,43 @@ +/* + * 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. 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" + +__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/libthr/thread/thr_event.c b/lib/libthr/thread/thr_event.c new file mode 100644 index 0000000..716d21e --- /dev/null +++ b/lib/libthr/thread/thr_event.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2005 David Xu + * 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 "thr_private.h" + +void +_thread_bp_create(void) +{ +} + +void +_thread_bp_death(void) +{ +} + +void +_thr_report_creation(struct pthread *curthread, struct pthread *newthread) +{ + curthread->event_buf.event = TD_CREATE; + curthread->event_buf.th_p = (uintptr_t)newthread; + curthread->event_buf.data = 0; + THR_UMUTEX_LOCK(curthread, &_thr_event_lock); + _thread_last_event = curthread; + _thread_bp_create(); + _thread_last_event = NULL; + THR_UMUTEX_UNLOCK(curthread, &_thr_event_lock); +} + +void +_thr_report_death(struct pthread *curthread) +{ + curthread->event_buf.event = TD_DEATH; + curthread->event_buf.th_p = (uintptr_t)curthread; + curthread->event_buf.data = 0; + THR_UMUTEX_LOCK(curthread, &_thr_event_lock); + _thread_last_event = curthread; + _thread_bp_death(); + _thread_last_event = NULL; + THR_UMUTEX_UNLOCK(curthread, &_thr_event_lock); +} diff --git a/lib/libthr/thread/thr_exit.c b/lib/libthr/thread/thr_exit.c new file mode 100644 index 0000000..7001311 --- /dev/null +++ b/lib/libthr/thread/thr_exit.c @@ -0,0 +1,309 @@ +/* + * 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. 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> +#ifdef _PTHREAD_FORCED_UNWIND +#include <dlfcn.h> +#endif +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> +#include <sys/types.h> +#include <sys/signalvar.h> +#include "un-namespace.h" + +#include "libc_private.h" +#include "thr_private.h" + +void _pthread_exit(void *status); + +static void exit_thread(void) __dead2; + +__weak_reference(_pthread_exit, pthread_exit); + +#ifdef _PTHREAD_FORCED_UNWIND +static int message_printed; + +static void thread_unwind(void) __dead2; +#ifdef PIC +static void thread_uw_init(void); +static _Unwind_Reason_Code thread_unwind_stop(int version, + _Unwind_Action actions, + int64_t exc_class, + struct _Unwind_Exception *exc_obj, + struct _Unwind_Context *context, void *stop_parameter); +/* unwind library pointers */ +static _Unwind_Reason_Code (*uwl_forcedunwind)(struct _Unwind_Exception *, + _Unwind_Stop_Fn, void *); +static unsigned long (*uwl_getcfa)(struct _Unwind_Context *); + +static void +thread_uw_init(void) +{ + static int inited = 0; + Dl_info dlinfo; + void *handle; + void *forcedunwind, *getcfa; + + if (inited) + return; + handle = RTLD_DEFAULT; + if ((forcedunwind = dlsym(handle, "_Unwind_ForcedUnwind")) != NULL) { + if (dladdr(forcedunwind, &dlinfo)) { + /* + * Make sure the address is always valid by holding the library, + * also assume functions are in same library. + */ + if ((handle = dlopen(dlinfo.dli_fname, RTLD_LAZY)) != NULL) { + forcedunwind = dlsym(handle, "_Unwind_ForcedUnwind"); + getcfa = dlsym(handle, "_Unwind_GetCFA"); + if (forcedunwind != NULL && getcfa != NULL) { + uwl_getcfa = getcfa; + atomic_store_rel_ptr((volatile void *)&uwl_forcedunwind, + (uintptr_t)forcedunwind); + } else { + dlclose(handle); + } + } + } + } + inited = 1; +} + +_Unwind_Reason_Code +_Unwind_ForcedUnwind(struct _Unwind_Exception *ex, _Unwind_Stop_Fn stop_func, + void *stop_arg) +{ + return (*uwl_forcedunwind)(ex, stop_func, stop_arg); +} + +unsigned long +_Unwind_GetCFA(struct _Unwind_Context *context) +{ + return (*uwl_getcfa)(context); +} +#else +#pragma weak _Unwind_GetCFA +#pragma weak _Unwind_ForcedUnwind +#endif /* PIC */ + +static void +thread_unwind_cleanup(_Unwind_Reason_Code code, struct _Unwind_Exception *e) +{ + /* + * Specification said that _Unwind_Resume should not be used here, + * instead, user should rethrow the exception. For C++ user, they + * should put "throw" sentence in catch(...) block. + */ + PANIC("exception should be rethrown"); +} + +static _Unwind_Reason_Code +thread_unwind_stop(int version, _Unwind_Action actions, + int64_t exc_class, + struct _Unwind_Exception *exc_obj, + struct _Unwind_Context *context, void *stop_parameter) +{ + struct pthread *curthread = _get_curthread(); + struct pthread_cleanup *cur; + uintptr_t cfa; + int done = 0; + + /* XXX assume stack grows down to lower address */ + + cfa = _Unwind_GetCFA(context); + if (actions & _UA_END_OF_STACK || + cfa >= (uintptr_t)curthread->unwind_stackend) { + done = 1; + } + + while ((cur = curthread->cleanup) != NULL && + (done || (uintptr_t)cur <= cfa)) { + __pthread_cleanup_pop_imp(1); + } + + if (done) + exit_thread(); /* Never return! */ + + return (_URC_NO_REASON); +} + +static void +thread_unwind(void) +{ + struct pthread *curthread = _get_curthread(); + + curthread->ex.exception_class = 0; + curthread->ex.exception_cleanup = thread_unwind_cleanup; + _Unwind_ForcedUnwind(&curthread->ex, thread_unwind_stop, NULL); + PANIC("_Unwind_ForcedUnwind returned"); +} + +#endif + +void +_thread_exit(const char *fname, int lineno, const 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(); +} + +void +_pthread_exit(void *status) +{ + _pthread_exit_mask(status, NULL); +} + +void +_pthread_exit_mask(void *status, sigset_t *mask) +{ + struct pthread *curthread = _get_curthread(); + + /* Check if this thread is already in the process of exiting: */ + if (curthread->cancelling) { + 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. */ + curthread->cancelling = 1; + curthread->no_cancel = 1; + curthread->cancel_async = 0; + curthread->cancel_point = 0; + if (mask != NULL) + __sys_sigprocmask(SIG_SETMASK, mask, NULL); + if (curthread->unblock_sigcancel) { + sigset_t set; + + curthread->unblock_sigcancel = 0; + SIGEMPTYSET(set); + SIGADDSET(set, SIGCANCEL); + __sys_sigprocmask(SIG_UNBLOCK, mask, NULL); + } + + /* Save the return value: */ + curthread->ret = status; +#ifdef _PTHREAD_FORCED_UNWIND + +#ifdef PIC + thread_uw_init(); +#endif /* PIC */ + +#ifdef PIC + if (uwl_forcedunwind != NULL) { +#else + if (_Unwind_ForcedUnwind != NULL) { +#endif + if (curthread->unwind_disabled) { + if (message_printed == 0) { + message_printed = 1; + _thread_printf(2, "Warning: old _pthread_cleanup_push was called, " + "stack unwinding is disabled.\n"); + } + goto cleanup; + } + thread_unwind(); + + } else { +cleanup: + while (curthread->cleanup != NULL) { + __pthread_cleanup_pop_imp(1); + } + exit_thread(); + } + +#else + while (curthread->cleanup != NULL) { + __pthread_cleanup_pop_imp(1); + } + + exit_thread(); +#endif /* _PTHREAD_FORCED_UNWIND */ +} + +static void +exit_thread(void) +{ + struct pthread *curthread = _get_curthread(); + + /* Check if there is thread specific data: */ + if (curthread->specific != NULL) { + /* Run the thread-specific data destructors: */ + _thread_cleanupspecific(); + } + + if (!_thr_isthreaded()) + exit(0); + + if (atomic_fetchadd_int(&_thread_active_threads, -1) == 1) { + exit(0); + /* Never reach! */ + } + + /* Tell malloc that the thread is exiting. */ + _malloc_thread_cleanup(); + + THR_LOCK(curthread); + curthread->state = PS_DEAD; + if (curthread->flags & THR_FLAGS_NEED_SUSPEND) { + curthread->cycle++; + _thr_umtx_wake(&curthread->cycle, INT_MAX, 0); + } + if (!curthread->force_exit && SHOULD_REPORT_EVENT(curthread, TD_DEATH)) + _thr_report_death(curthread); + /* + * Thread was created with initial refcount 1, we drop the + * reference count to allow it to be garbage collected. + */ + curthread->refcount--; + _thr_try_gc(curthread, curthread); /* thread lock released */ + +#if defined(_PTHREADS_INVARIANTS) + if (THR_IN_CRITICAL(curthread)) + PANIC("thread exits with resources held!"); +#endif + /* + * Kernel will do wakeup at the address, so joiner thread + * will be resumed if it is sleeping at the address. + */ + thr_exit(&curthread->tid); + PANIC("thr_exit() returned"); + /* Never reach! */ +} diff --git a/lib/libthr/thread/thr_fork.c b/lib/libthr/thread/thr_fork.c new file mode 100644 index 0000000..a1399bf --- /dev/null +++ b/lib/libthr/thread/thr_fork.c @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2005 David Xu <davidxu@freebsd.org> + * 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$ + */ + +/* + * 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. 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 "namespace.h" +#include <errno.h> +#include <link.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <pthread.h> +#include <spinlock.h> +#include "un-namespace.h" + +#include "libc_private.h" +#include "rtld_lock.h" +#include "thr_private.h" + +__weak_reference(_pthread_atfork, pthread_atfork); + +int +_pthread_atfork(void (*prepare)(void), void (*parent)(void), + void (*child)(void)) +{ + struct pthread *curthread; + struct pthread_atfork *af; + + _thr_check_init(); + + if ((af = malloc(sizeof(struct pthread_atfork))) == NULL) + return (ENOMEM); + + curthread = _get_curthread(); + af->prepare = prepare; + af->parent = parent; + af->child = child; + THR_CRITICAL_ENTER(curthread); + _thr_rwl_wrlock(&_thr_atfork_lock); + TAILQ_INSERT_TAIL(&_thr_atfork_list, af, qe); + _thr_rwl_unlock(&_thr_atfork_lock); + THR_CRITICAL_LEAVE(curthread); + return (0); +} + +void +__pthread_cxa_finalize(struct dl_phdr_info *phdr_info) +{ + atfork_head temp_list = TAILQ_HEAD_INITIALIZER(temp_list); + struct pthread *curthread; + struct pthread_atfork *af, *af1; + + _thr_check_init(); + + curthread = _get_curthread(); + THR_CRITICAL_ENTER(curthread); + _thr_rwl_wrlock(&_thr_atfork_lock); + TAILQ_FOREACH_SAFE(af, &_thr_atfork_list, qe, af1) { + if (__elf_phdr_match_addr(phdr_info, af->prepare) || + __elf_phdr_match_addr(phdr_info, af->parent) || + __elf_phdr_match_addr(phdr_info, af->child)) { + TAILQ_REMOVE(&_thr_atfork_list, af, qe); + TAILQ_INSERT_TAIL(&temp_list, af, qe); + } + } + _thr_rwl_unlock(&_thr_atfork_lock); + THR_CRITICAL_LEAVE(curthread); + while ((af = TAILQ_FIRST(&temp_list)) != NULL) { + TAILQ_REMOVE(&temp_list, af, qe); + free(af); + } + _thr_tsd_unload(phdr_info); + _thr_sigact_unload(phdr_info); +} + +__weak_reference(_fork, fork); + +pid_t _fork(void); + +pid_t +_fork(void) +{ + struct pthread *curthread; + struct pthread_atfork *af; + pid_t ret; + int errsave, cancelsave; + int was_threaded; + int rtld_locks[MAX_RTLD_LOCKS]; + + if (!_thr_is_inited()) + return (__sys_fork()); + + curthread = _get_curthread(); + cancelsave = curthread->no_cancel; + curthread->no_cancel = 1; + _thr_rwl_rdlock(&_thr_atfork_lock); + + /* Run down atfork prepare handlers. */ + TAILQ_FOREACH_REVERSE(af, &_thr_atfork_list, atfork_head, qe) { + if (af->prepare != NULL) + af->prepare(); + } + + /* + * Block all signals until we reach a safe point. + */ + _thr_signal_block(curthread); + _thr_signal_prefork(); + + /* + * All bets are off as to what should happen soon if the parent + * process was not so kindly as to set up pthread fork hooks to + * relinquish all running threads. + */ + if (_thr_isthreaded() != 0) { + was_threaded = 1; + _malloc_prefork(); + _rtld_atfork_pre(rtld_locks); + } else { + was_threaded = 0; + } + + /* Fork a new process: */ + if ((ret = __sys_fork()) == 0) { + /* Child process */ + errsave = errno; + curthread->cancel_pending = 0; + curthread->flags &= ~(THR_FLAGS_NEED_SUSPEND|THR_FLAGS_DETACHED); + + /* + * Thread list will be reinitialized, and later we call + * _libpthread_init(), it will add us back to list. + */ + curthread->tlflags &= ~TLFLAGS_IN_TDLIST; + + /* child is a new kernel thread. */ + thr_self(&curthread->tid); + + /* clear other threads locked us. */ + _thr_umutex_init(&curthread->lock); + _mutex_fork(curthread); + + _thr_signal_postfork_child(); + + if (was_threaded) + _rtld_atfork_post(rtld_locks); + _thr_setthreaded(0); + + /* reinitialize libc spinlocks. */ + _thr_spinlock_init(); + + /* reinitalize library. */ + _libpthread_init(curthread); + + /* atfork is reinitializeded by _libpthread_init()! */ + _thr_rwl_rdlock(&_thr_atfork_lock); + + if (was_threaded) { + __isthreaded = 1; + _malloc_postfork(); + __isthreaded = 0; + } + + /* Ready to continue, unblock signals. */ + _thr_signal_unblock(curthread); + + /* Run down atfork child handlers. */ + TAILQ_FOREACH(af, &_thr_atfork_list, qe) { + if (af->child != NULL) + af->child(); + } + _thr_rwlock_unlock(&_thr_atfork_lock); + curthread->no_cancel = cancelsave; + } else { + /* Parent process */ + errsave = errno; + + _thr_signal_postfork(); + + if (was_threaded) { + _rtld_atfork_post(rtld_locks); + _malloc_postfork(); + } + + /* Ready to continue, unblock signals. */ + _thr_signal_unblock(curthread); + + /* Run down atfork parent handlers. */ + TAILQ_FOREACH(af, &_thr_atfork_list, qe) { + if (af->parent != NULL) + af->parent(); + } + + _thr_rwlock_unlock(&_thr_atfork_lock); + curthread->no_cancel = cancelsave; + /* test async cancel */ + if (curthread->cancel_async) + _thr_testcancel(curthread); + } + errno = errsave; + + /* Return the process ID: */ + return (ret); +} diff --git a/lib/libthr/thread/thr_getcpuclockid.c b/lib/libthr/thread/thr_getcpuclockid.c new file mode 100644 index 0000000..68f88d5 --- /dev/null +++ b/lib/libthr/thread/thr_getcpuclockid.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2008 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 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 <sys/time.h> +#include "un-namespace.h" + +#include "thr_private.h" + +__weak_reference(_pthread_getcpuclockid, pthread_getcpuclockid); + +int +_pthread_getcpuclockid(pthread_t pthread, clockid_t *clock_id) +{ + if (pthread == NULL) + return (EINVAL); + + *clock_id = CLOCK_THREAD_CPUTIME_ID; + return (0); +} diff --git a/lib/libthr/thread/thr_getprio.c b/lib/libthr/thread/thr_getprio.c new file mode 100644 index 0000000..f0d1a296 --- /dev/null +++ b/lib/libthr/thread/thr_getprio.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. 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" + +__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/libthr/thread/thr_getschedparam.c b/lib/libthr/thread/thr_getschedparam.c new file mode 100644 index 0000000..b36d724 --- /dev/null +++ b/lib/libthr/thread/thr_getschedparam.c @@ -0,0 +1,77 @@ +/* + * 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 "namespace.h" +#include <sys/types.h> +#include <sys/rtprio.h> +#include <errno.h> +#include <pthread.h> +#include "un-namespace.h" + +#include "thr_private.h" + +__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; + + if (policy == NULL || param == NULL) + return (EINVAL); + + if (pthread == curthread) { + /* + * Avoid searching the thread list when it is the current + * thread. + */ + THR_LOCK(curthread); + *policy = curthread->attr.sched_policy; + param->sched_priority = curthread->attr.prio; + THR_UNLOCK(curthread); + ret = 0; + } + /* Find the thread in the list of active threads. */ + else if ((ret = _thr_ref_add(curthread, pthread, /*include dead*/0)) + == 0) { + THR_THREAD_LOCK(curthread, pthread); + *policy = pthread->attr.sched_policy; + param->sched_priority = pthread->attr.prio; + THR_THREAD_UNLOCK(curthread, pthread); + _thr_ref_delete(curthread, pthread); + } + return (ret); +} diff --git a/lib/libthr/thread/thr_getthreadid_np.c b/lib/libthr/thread/thr_getthreadid_np.c new file mode 100644 index 0000000..f963a56 --- /dev/null +++ b/lib/libthr/thread/thr_getthreadid_np.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2011 Jung-uk Kim <jkim@FreeBSD.org> + * + * Redistribution and use in source and binary forms, 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 "namespace.h" +#include <pthread.h> +#include <pthread_np.h> +#include "un-namespace.h" + +#include "thr_private.h" + +__weak_reference(_pthread_getthreadid_np, pthread_getthreadid_np); + +/* + * Provide the equivelant to AIX pthread_getthreadid_np() function. + */ +int +_pthread_getthreadid_np(void) +{ + struct pthread *curthread; + + _thr_check_init(); + curthread = _get_curthread(); + return (TID(curthread)); +} diff --git a/lib/libthr/thread/thr_info.c b/lib/libthr/thread/thr_info.c new file mode 100644 index 0000000..2da6da2 --- /dev/null +++ b/lib/libthr/thread/thr_info.c @@ -0,0 +1,70 @@ +/* + * 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. 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 <stdlib.h> +#include <string.h> +#include <pthread.h> +#include <pthread_np.h> +#include "un-namespace.h" + +#include "thr_private.h" + +__weak_reference(_pthread_set_name_np, pthread_set_name_np); + +/* Set the thread name for debug. */ +void +_pthread_set_name_np(pthread_t thread, const char *name) +{ + struct pthread *curthread = _get_curthread(); + int ret = 0; + + if (curthread == thread) { + if (thr_set_name(thread->tid, name)) + ret = errno; + } else { + if (_thr_ref_add(curthread, thread, 0) == 0) { + THR_THREAD_LOCK(curthread, thread); + if (thread->state != PS_DEAD) { + if (thr_set_name(thread->tid, name)) + ret = errno; + } + THR_THREAD_UNLOCK(curthread, thread); + _thr_ref_delete(curthread, thread); + } else { + ret = ESRCH; + } + } +#if 0 + /* XXX should return error code. */ + return (ret); +#endif +} diff --git a/lib/libthr/thread/thr_init.c b/lib/libthr/thread/thr_init.c new file mode 100644 index 0000000..86abad8 --- /dev/null +++ b/lib/libthr/thread/thr_init.c @@ -0,0 +1,476 @@ +/* + * 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$ + */ + +#include "namespace.h" +#include <sys/types.h> +#include <sys/signalvar.h> +#include <sys/ioctl.h> +#include <sys/sysctl.h> +#include <sys/ttycom.h> +#include <sys/mman.h> +#include <sys/rtprio.h> +#include <errno.h> +#include <fcntl.h> +#include <paths.h> +#include <pthread.h> +#include <pthread_np.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include "un-namespace.h" + +#include "libc_private.h" +#include "thr_private.h" + +char *_usrstack; +struct pthread *_thr_initial; +int _libthr_debug; +int _thread_event_mask; +struct pthread *_thread_last_event; +pthreadlist _thread_list = TAILQ_HEAD_INITIALIZER(_thread_list); +pthreadlist _thread_gc_list = TAILQ_HEAD_INITIALIZER(_thread_gc_list); +int _thread_active_threads = 1; +atfork_head _thr_atfork_list = TAILQ_HEAD_INITIALIZER(_thr_atfork_list); +struct urwlock _thr_atfork_lock = DEFAULT_URWLOCK; + +struct pthread_prio _thr_priorities[3] = { + {RTP_PRIO_MIN, RTP_PRIO_MAX, 0}, /* FIFO */ + {0, 0, 63}, /* OTHER */ + {RTP_PRIO_MIN, RTP_PRIO_MAX, 0} /* RR */ +}; + +struct pthread_attr _pthread_attr_default = { + .sched_policy = SCHED_OTHER, + .sched_inherit = PTHREAD_INHERIT_SCHED, + .prio = 0, + .suspend = THR_CREATE_RUNNING, + .flags = PTHREAD_SCOPE_SYSTEM, + .stackaddr_attr = NULL, + .stacksize_attr = THR_STACK_DEFAULT, + .guardsize_attr = 0, + .cpusetsize = 0, + .cpuset = NULL +}; + +struct pthread_mutex_attr _pthread_mutexattr_default = { + .m_type = PTHREAD_MUTEX_DEFAULT, + .m_protocol = PTHREAD_PRIO_NONE, + .m_ceiling = 0 +}; + +struct pthread_mutex_attr _pthread_mutexattr_adaptive_default = { + .m_type = PTHREAD_MUTEX_ADAPTIVE_NP, + .m_protocol = PTHREAD_PRIO_NONE, + .m_ceiling = 0 +}; + +/* Default condition variable attributes: */ +struct pthread_cond_attr _pthread_condattr_default = { + .c_pshared = PTHREAD_PROCESS_PRIVATE, + .c_clockid = CLOCK_REALTIME +}; + +pid_t _thr_pid; +int _thr_is_smp = 0; +size_t _thr_guard_default; +size_t _thr_stack_default = THR_STACK_DEFAULT; +size_t _thr_stack_initial = THR_STACK_INITIAL; +int _thr_page_size; +int _thr_spinloops; +int _thr_yieldloops; +int _gc_count; +struct umutex _mutex_static_lock = DEFAULT_UMUTEX; +struct umutex _cond_static_lock = DEFAULT_UMUTEX; +struct umutex _rwlock_static_lock = DEFAULT_UMUTEX; +struct umutex _keytable_lock = DEFAULT_UMUTEX; +struct urwlock _thr_list_lock = DEFAULT_URWLOCK; +struct umutex _thr_event_lock = DEFAULT_UMUTEX; + +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) __attribute__ ((constructor)); + +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_LIB_REQUIRE(_fork); +STATIC_LIB_REQUIRE(_pthread_getspecific); +STATIC_LIB_REQUIRE(_pthread_key_create); +STATIC_LIB_REQUIRE(_pthread_key_delete); +STATIC_LIB_REQUIRE(_pthread_mutex_destroy); +STATIC_LIB_REQUIRE(_pthread_mutex_init); +STATIC_LIB_REQUIRE(_pthread_mutex_lock); +STATIC_LIB_REQUIRE(_pthread_mutex_trylock); +STATIC_LIB_REQUIRE(_pthread_mutex_unlock); +STATIC_LIB_REQUIRE(_pthread_mutexattr_init); +STATIC_LIB_REQUIRE(_pthread_mutexattr_destroy); +STATIC_LIB_REQUIRE(_pthread_mutexattr_settype); +STATIC_LIB_REQUIRE(_pthread_once); +STATIC_LIB_REQUIRE(_pthread_setspecific); +STATIC_LIB_REQUIRE(_raise); +STATIC_LIB_REQUIRE(_sem_destroy); +STATIC_LIB_REQUIRE(_sem_getvalue); +STATIC_LIB_REQUIRE(_sem_init); +STATIC_LIB_REQUIRE(_sem_post); +STATIC_LIB_REQUIRE(_sem_timedwait); +STATIC_LIB_REQUIRE(_sem_trywait); +STATIC_LIB_REQUIRE(_sem_wait); +STATIC_LIB_REQUIRE(_sigaction); +STATIC_LIB_REQUIRE(_sigprocmask); +STATIC_LIB_REQUIRE(_sigsuspend); +STATIC_LIB_REQUIRE(_sigtimedwait); +STATIC_LIB_REQUIRE(_sigwait); +STATIC_LIB_REQUIRE(_sigwaitinfo); +STATIC_LIB_REQUIRE(_spinlock); +STATIC_LIB_REQUIRE(_spinlock_debug); +STATIC_LIB_REQUIRE(_spinunlock); +STATIC_LIB_REQUIRE(_thread_init_hack); + +/* + * 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_LIB_REQUIRE(_pthread_once); +STATIC_LIB_REQUIRE(_pthread_key_create); +STATIC_LIB_REQUIRE(_pthread_key_delete); +STATIC_LIB_REQUIRE(_pthread_getspecific); +STATIC_LIB_REQUIRE(_pthread_setspecific); +STATIC_LIB_REQUIRE(_pthread_mutex_init); +STATIC_LIB_REQUIRE(_pthread_mutex_destroy); +STATIC_LIB_REQUIRE(_pthread_mutex_lock); +STATIC_LIB_REQUIRE(_pthread_mutex_trylock); +STATIC_LIB_REQUIRE(_pthread_mutex_unlock); +STATIC_LIB_REQUIRE(_pthread_create); + +/* Pull in all symbols required by libthread_db */ +STATIC_LIB_REQUIRE(_thread_state_running); + +#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 */ + {DUAL_ENTRY(__pthread_cleanup_pop_imp)},/* PJT_CLEANUP_POP_IMP */ + {DUAL_ENTRY(__pthread_cleanup_push_imp)},/* PJT_CLEANUP_PUSH_IMP */ + {DUAL_ENTRY(_pthread_cancel_enter)}, /* PJT_CANCEL_ENTER */ + {DUAL_ENTRY(_pthread_cancel_leave)} /* PJT_CANCEL_LEAVE */ +}; + +static int init_once = 0; + +/* + * 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; + +void +_thread_init_hack(void) +{ + + _libpthread_init(NULL); +} + + +/* + * 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, first = 0; + + /* Check if this function has already been called: */ + if ((_thr_initial != NULL) && (curthread == NULL)) + /* Only initialize the threaded application once. */ + return; + + /* + * 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 (_ioctl(fd, TIOCSCTTY, (char *) NULL) == -1) + PANIC("Can't set controlling terminal"); + } + + /* Initialize pthread private data. */ + init_private(); + + /* Set the initial thread. */ + if (curthread == NULL) { + first = 1; + /* Create and initialize the initial thread. */ + curthread = _thr_alloc(NULL); + if (curthread == NULL) + PANIC("Can't allocate initial thread"); + init_main_thread(curthread); + } + /* + * Add the thread to the thread list queue. + */ + THR_LIST_ADD(curthread); + _thread_active_threads = 1; + + /* Setup the thread specific data */ + _tcb_set(curthread->tcb); + + if (first) { + _thr_initial = curthread; + _thr_signal_init(); + if (_thread_event_mask & TD_CREATE) + _thr_report_creation(curthread, curthread); + } +} + +/* + * 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) +{ + struct sched_param sched_param; + + /* Setup the thread attributes. */ + thr_self(&thread->tid); + thread->attr = _pthread_attr_default; + /* + * 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(_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 = _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->cancel_enable = 1; + thread->cancel_async = 0; + + /* Initialize the mutex queue: */ + TAILQ_INIT(&thread->mutexq); + TAILQ_INIT(&thread->pp_mutexq); + + thread->state = PS_RUNNING; + + _thr_getscheduler(thread->tid, &thread->attr.sched_policy, + &sched_param); + thread->attr.prio = sched_param.sched_priority; + +#ifdef _PTHREAD_FORCED_UNWIND + thread->unwind_stackend = _usrstack; +#endif + + /* Others cleared to zero by thr_alloc() */ +} + +static void +init_private(void) +{ + size_t len; + int mib[2]; + char *env; + + _thr_umutex_init(&_mutex_static_lock); + _thr_umutex_init(&_cond_static_lock); + _thr_umutex_init(&_rwlock_static_lock); + _thr_umutex_init(&_keytable_lock); + _thr_urwlock_init(&_thr_atfork_lock); + _thr_umutex_init(&_thr_event_lock); + _thr_once_init(); + _thr_spinlock_init(); + _thr_list_init(); + _thr_wake_addr_init(); + _sleepq_init(); + + /* + * 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"); + len = sizeof(_thr_is_smp); + sysctlbyname("kern.smp.cpus", &_thr_is_smp, &len, NULL, 0); + _thr_is_smp = (_thr_is_smp > 1); + _thr_page_size = getpagesize(); + _thr_guard_default = _thr_page_size; + _pthread_attr_default.guardsize_attr = _thr_guard_default; + _pthread_attr_default.stacksize_attr = _thr_stack_default; + env = getenv("LIBPTHREAD_SPINLOOPS"); + if (env) + _thr_spinloops = atoi(env); + env = getenv("LIBPTHREAD_YIELDLOOPS"); + if (env) + _thr_yieldloops = atoi(env); + TAILQ_INIT(&_thr_atfork_list); + } + init_once = 1; +} diff --git a/lib/libthr/thread/thr_join.c b/lib/libthr/thread/thr_join.c new file mode 100644 index 0000000..a39ff67 --- /dev/null +++ b/lib/libthr/thread/thr_join.c @@ -0,0 +1,149 @@ +/* + * 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 "namespace.h" +#include <errno.h> +#include <pthread.h> +#include "un-namespace.h" + +#include "thr_private.h" + +int _pthread_timedjoin_np(pthread_t pthread, void **thread_return, + const struct timespec *abstime); +static int join_common(pthread_t, void **, const struct timespec *); + +__weak_reference(_pthread_join, pthread_join); +__weak_reference(_pthread_timedjoin_np, pthread_timedjoin_np); + +static void backout_join(void *arg) +{ + struct pthread *pthread = (struct pthread *)arg; + struct pthread *curthread = _get_curthread(); + + THR_THREAD_LOCK(curthread, pthread); + pthread->joiner = NULL; + THR_THREAD_UNLOCK(curthread, pthread); +} + +int +_pthread_join(pthread_t pthread, void **thread_return) +{ + return (join_common(pthread, thread_return, NULL)); +} + +int +_pthread_timedjoin_np(pthread_t pthread, void **thread_return, + const struct timespec *abstime) +{ + if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || + abstime->tv_nsec >= 1000000000) + return (EINVAL); + + return (join_common(pthread, thread_return, abstime)); +} + +/* + * Cancellation behavior: + * if the thread is canceled, joinee is not recycled. + */ +static int +join_common(pthread_t pthread, void **thread_return, + const struct timespec *abstime) +{ + struct pthread *curthread = _get_curthread(); + struct timespec ts, ts2, *tsp; + void *tmp; + long tid; + int ret = 0; + + if (pthread == NULL) + return (EINVAL); + + if (pthread == curthread) + return (EDEADLK); + + if ((ret = _thr_find_thread(curthread, pthread, 1)) != 0) + return (ESRCH); + + if ((pthread->flags & THR_FLAGS_DETACHED) != 0) { + ret = EINVAL; + } else if (pthread->joiner != NULL) { + /* Multiple joiners are not supported. */ + ret = ENOTSUP; + } + if (ret) { + THR_THREAD_UNLOCK(curthread, pthread); + return (ret); + } + /* Set the running thread to be the joiner: */ + pthread->joiner = curthread; + + THR_THREAD_UNLOCK(curthread, pthread); + + THR_CLEANUP_PUSH(curthread, backout_join, pthread); + _thr_cancel_enter(curthread); + + tid = pthread->tid; + while (pthread->tid != TID_TERMINATED) { + _thr_testcancel(curthread); + if (abstime != NULL) { + clock_gettime(CLOCK_REALTIME, &ts); + TIMESPEC_SUB(&ts2, abstime, &ts); + if (ts2.tv_sec < 0) { + ret = ETIMEDOUT; + break; + } + tsp = &ts2; + } else + tsp = NULL; + ret = _thr_umtx_wait(&pthread->tid, tid, tsp); + if (ret == ETIMEDOUT) + break; + } + + _thr_cancel_leave(curthread, 0); + THR_CLEANUP_POP(curthread, 0); + + if (ret == ETIMEDOUT) { + THR_THREAD_LOCK(curthread, pthread); + pthread->joiner = NULL; + THR_THREAD_UNLOCK(curthread, pthread); + } else { + ret = 0; + tmp = pthread->ret; + THR_THREAD_LOCK(curthread, pthread); + pthread->flags |= THR_FLAGS_DETACHED; + pthread->joiner = NULL; + _thr_try_gc(curthread, pthread); /* thread lock released */ + + if (thread_return != NULL) + *thread_return = tmp; + } + return (ret); +} diff --git a/lib/libthr/thread/thr_kern.c b/lib/libthr/thread/thr_kern.c new file mode 100644 index 0000000..48f7c65 --- /dev/null +++ b/lib/libthr/thread/thr_kern.c @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2005 David Xu <davidxu@freebsd.org> + * Copyright (C) 2003 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 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 <sys/types.h> +#include <sys/signalvar.h> +#include <sys/rtprio.h> +#include <sys/mman.h> +#include <pthread.h> + +#include "thr_private.h" + +/*#define DEBUG_THREAD_KERN */ +#ifdef DEBUG_THREAD_KERN +#define DBG_MSG stdout_debug +#else +#define DBG_MSG(x...) +#endif + +static struct umutex addr_lock; +static struct wake_addr *wake_addr_head; +static struct wake_addr default_wake_addr; + +/* + * This is called when the first thread (other than the initial + * thread) is created. + */ +int +_thr_setthreaded(int threaded) +{ + if (((threaded == 0) ^ (__isthreaded == 0)) == 0) + return (0); + + __isthreaded = threaded; + if (threaded != 0) { + _thr_rtld_init(); + } else { + _thr_rtld_fini(); + } + return (0); +} + +void +_thr_assert_lock_level() +{ + PANIC("locklevel <= 0"); +} + +int +_rtp_to_schedparam(const struct rtprio *rtp, int *policy, + struct sched_param *param) +{ + switch(rtp->type) { + case RTP_PRIO_REALTIME: + *policy = SCHED_RR; + param->sched_priority = RTP_PRIO_MAX - rtp->prio; + break; + case RTP_PRIO_FIFO: + *policy = SCHED_FIFO; + param->sched_priority = RTP_PRIO_MAX - rtp->prio; + break; + default: + *policy = SCHED_OTHER; + param->sched_priority = 0; + break; + } + return (0); +} + +int +_schedparam_to_rtp(int policy, const struct sched_param *param, + struct rtprio *rtp) +{ + switch(policy) { + case SCHED_RR: + rtp->type = RTP_PRIO_REALTIME; + rtp->prio = RTP_PRIO_MAX - param->sched_priority; + break; + case SCHED_FIFO: + rtp->type = RTP_PRIO_FIFO; + rtp->prio = RTP_PRIO_MAX - param->sched_priority; + break; + case SCHED_OTHER: + default: + rtp->type = RTP_PRIO_NORMAL; + rtp->prio = 0; + break; + } + return (0); +} + +int +_thr_getscheduler(lwpid_t lwpid, int *policy, struct sched_param *param) +{ + struct rtprio rtp; + int ret; + + ret = rtprio_thread(RTP_LOOKUP, lwpid, &rtp); + if (ret == -1) + return (ret); + _rtp_to_schedparam(&rtp, policy, param); + return (0); +} + +int +_thr_setscheduler(lwpid_t lwpid, int policy, const struct sched_param *param) +{ + struct rtprio rtp; + + _schedparam_to_rtp(policy, param, &rtp); + return (rtprio_thread(RTP_SET, lwpid, &rtp)); +} + +void +_thr_wake_addr_init(void) +{ + _thr_umutex_init(&addr_lock); + wake_addr_head = NULL; +} + +/* + * Allocate wake-address, the memory area is never freed after + * allocated, this becauses threads may be referencing it. + */ +struct wake_addr * +_thr_alloc_wake_addr(void) +{ + struct pthread *curthread; + struct wake_addr *p; + + if (_thr_initial == NULL) { + return &default_wake_addr; + } + + curthread = _get_curthread(); + + THR_LOCK_ACQUIRE(curthread, &addr_lock); + if (wake_addr_head == NULL) { + unsigned i; + unsigned pagesize = getpagesize(); + struct wake_addr *pp = (struct wake_addr *) + mmap(NULL, getpagesize(), PROT_READ|PROT_WRITE, + MAP_ANON|MAP_PRIVATE, -1, 0); + for (i = 1; i < pagesize/sizeof(struct wake_addr); ++i) + pp[i].link = &pp[i+1]; + pp[i-1].link = NULL; + wake_addr_head = &pp[1]; + p = &pp[0]; + } else { + p = wake_addr_head; + wake_addr_head = p->link; + } + THR_LOCK_RELEASE(curthread, &addr_lock); + p->value = 0; + return (p); +} + +void +_thr_release_wake_addr(struct wake_addr *wa) +{ + struct pthread *curthread = _get_curthread(); + + if (wa == &default_wake_addr) + return; + THR_LOCK_ACQUIRE(curthread, &addr_lock); + wa->link = wake_addr_head; + wake_addr_head = wa; + THR_LOCK_RELEASE(curthread, &addr_lock); +} + +/* Sleep on thread wakeup address */ +int +_thr_sleep(struct pthread *curthread, int clockid, + const struct timespec *abstime) +{ + + curthread->will_sleep = 0; + if (curthread->nwaiter_defer > 0) { + _thr_wake_all(curthread->defer_waiters, + curthread->nwaiter_defer); + curthread->nwaiter_defer = 0; + } + + if (curthread->wake_addr->value != 0) + return (0); + + return _thr_umtx_timedwait_uint(&curthread->wake_addr->value, 0, + clockid, abstime, 0); +} + +void +_thr_wake_all(unsigned int *waddrs[], int count) +{ + int i; + + for (i = 0; i < count; ++i) + *waddrs[i] = 1; + _umtx_op(waddrs, UMTX_OP_NWAKE_PRIVATE, count, NULL, NULL); +} diff --git a/lib/libthr/thread/thr_kill.c b/lib/libthr/thread/thr_kill.c new file mode 100644 index 0000000..b54458c58 --- /dev/null +++ b/lib/libthr/thread/thr_kill.c @@ -0,0 +1,70 @@ +/* + * 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. 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 <signal.h> +#include <pthread.h> +#include "un-namespace.h" + +#include "thr_private.h" + +__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 (curthread == pthread) { + if (sig > 0) + _thr_send_sig(pthread, sig); + ret = 0; + } if ((ret = _thr_find_thread(curthread, pthread, /*include dead*/0)) + == 0) { + if (sig > 0) + _thr_send_sig(pthread, sig); + THR_THREAD_UNLOCK(curthread, pthread); + } + + /* Return the completion status: */ + return (ret); +} diff --git a/lib/libthr/thread/thr_list.c b/lib/libthr/thread/thr_list.c new file mode 100644 index 0000000..d27d87d --- /dev/null +++ b/lib/libthr/thread/thr_list.c @@ -0,0 +1,361 @@ +/* + * Copyright (c) 2005 David Xu <davidxu@freebsd.org> + * Copyright (C) 2003 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 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 <sys/types.h> +#include <sys/queue.h> + +#include <stdlib.h> +#include <string.h> +#include <pthread.h> + +#include "thr_private.h" +#include "libc_private.h" + +/*#define DEBUG_THREAD_LIST */ +#ifdef DEBUG_THREAD_LIST +#define DBG_MSG stdout_debug +#else +#define DBG_MSG(x...) +#endif + +#define MAX_THREADS 100000 + +/* + * 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 + +/* + * 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(, pthread) free_threadq; +static struct umutex free_thread_lock = DEFAULT_UMUTEX; +static struct umutex tcb_lock = DEFAULT_UMUTEX; +static int free_thread_count = 0; +static int inited = 0; +static int total_threads; + +LIST_HEAD(thread_hash_head, pthread); +#define HASH_QUEUES 128 +static struct thread_hash_head thr_hashtable[HASH_QUEUES]; +#define THREAD_HASH(thrd) (((unsigned long)thrd >> 8) % HASH_QUEUES) + +static void thr_destroy(struct pthread *curthread, struct pthread *thread); + +void +_thr_list_init(void) +{ + int i; + + _gc_count = 0; + total_threads = 1; + _thr_urwlock_init(&_thr_list_lock); + TAILQ_INIT(&_thread_list); + TAILQ_INIT(&free_threadq); + _thr_umutex_init(&free_thread_lock); + _thr_umutex_init(&tcb_lock); + if (inited) { + for (i = 0; i < HASH_QUEUES; ++i) + LIST_INIT(&thr_hashtable[i]); + } + inited = 1; +} + +void +_thr_gc(struct pthread *curthread) +{ + struct pthread *td, *td_next; + TAILQ_HEAD(, pthread) worklist; + + TAILQ_INIT(&worklist); + THREAD_LIST_WRLOCK(curthread); + + /* Check the threads waiting for GC. */ + TAILQ_FOREACH_SAFE(td, &_thread_gc_list, gcle, td_next) { + if (td->tid != TID_TERMINATED) { + /* make sure we are not still in userland */ + continue; + } + _thr_stack_free(&td->attr); + THR_GCLIST_REMOVE(td); + TAILQ_INSERT_HEAD(&worklist, td, gcle); + } + THREAD_LIST_UNLOCK(curthread); + + while ((td = TAILQ_FIRST(&worklist)) != NULL) { + TAILQ_REMOVE(&worklist, td, gcle); + /* + * XXX we don't free initial thread, because there might + * have some code referencing initial thread. + */ + if (td == _thr_initial) { + DBG_MSG("Initial thread won't be freed\n"); + continue; + } + + _thr_free(curthread, td); + } +} + +struct pthread * +_thr_alloc(struct pthread *curthread) +{ + struct pthread *thread = NULL; + struct tcb *tcb; + + if (curthread != NULL) { + if (GC_NEEDED()) + _thr_gc(curthread); + if (free_thread_count > 0) { + THR_LOCK_ACQUIRE(curthread, &free_thread_lock); + if ((thread = TAILQ_FIRST(&free_threadq)) != NULL) { + TAILQ_REMOVE(&free_threadq, thread, tle); + free_thread_count--; + } + THR_LOCK_RELEASE(curthread, &free_thread_lock); + } + } + if (thread == NULL) { + if (total_threads > MAX_THREADS) + return (NULL); + atomic_fetchadd_int(&total_threads, 1); + thread = calloc(1, sizeof(struct pthread)); + if (thread == NULL) { + atomic_fetchadd_int(&total_threads, -1); + return (NULL); + } + if ((thread->sleepqueue = _sleepq_alloc()) == NULL || + (thread->wake_addr = _thr_alloc_wake_addr()) == NULL) { + thr_destroy(curthread, thread); + atomic_fetchadd_int(&total_threads, -1); + return (NULL); + } + } else { + bzero(&thread->_pthread_startzero, + __rangeof(struct pthread, _pthread_startzero, _pthread_endzero)); + } + if (curthread != NULL) { + THR_LOCK_ACQUIRE(curthread, &tcb_lock); + tcb = _tcb_ctor(thread, 0 /* not initial tls */); + THR_LOCK_RELEASE(curthread, &tcb_lock); + } else { + tcb = _tcb_ctor(thread, 1 /* initial tls */); + } + if (tcb != NULL) { + thread->tcb = tcb; + } else { + thr_destroy(curthread, thread); + atomic_fetchadd_int(&total_threads, -1); + thread = NULL; + } + return (thread); +} + +void +_thr_free(struct pthread *curthread, struct pthread *thread) +{ + DBG_MSG("Freeing thread %p\n", thread); + + /* + * Always free tcb, as we only know it is part of RTLD TLS + * block, but don't know its detail and can not assume how + * it works, so better to avoid caching it here. + */ + if (curthread != NULL) { + THR_LOCK_ACQUIRE(curthread, &tcb_lock); + _tcb_dtor(thread->tcb); + THR_LOCK_RELEASE(curthread, &tcb_lock); + } else { + _tcb_dtor(thread->tcb); + } + thread->tcb = NULL; + if ((curthread == NULL) || (free_thread_count >= MAX_CACHED_THREADS)) { + thr_destroy(curthread, thread); + atomic_fetchadd_int(&total_threads, -1); + } else { + /* + * Add the thread to the free thread list, this also avoids + * pthread id is reused too quickly, may help some buggy apps. + */ + THR_LOCK_ACQUIRE(curthread, &free_thread_lock); + TAILQ_INSERT_TAIL(&free_threadq, thread, tle); + free_thread_count++; + THR_LOCK_RELEASE(curthread, &free_thread_lock); + } +} + +static void +thr_destroy(struct pthread *curthread __unused, struct pthread *thread) +{ + if (thread->sleepqueue != NULL) + _sleepq_free(thread->sleepqueue); + if (thread->wake_addr != NULL) + _thr_release_wake_addr(thread->wake_addr); + free(thread); +} + +/* + * Add the thread to the list of all threads and increment + * number of active threads. + */ +void +_thr_link(struct pthread *curthread, struct pthread *thread) +{ + THREAD_LIST_WRLOCK(curthread); + THR_LIST_ADD(thread); + THREAD_LIST_UNLOCK(curthread); + atomic_add_int(&_thread_active_threads, 1); +} + +/* + * Remove an active thread. + */ +void +_thr_unlink(struct pthread *curthread, struct pthread *thread) +{ + THREAD_LIST_WRLOCK(curthread); + THR_LIST_REMOVE(thread); + THREAD_LIST_UNLOCK(curthread); + atomic_add_int(&_thread_active_threads, -1); +} + +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); +} + +/* + * 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) +{ + int ret; + + if (thread == NULL) + /* Invalid thread: */ + return (EINVAL); + + if ((ret = _thr_find_thread(curthread, thread, include_dead)) == 0) { + thread->refcount++; + THR_CRITICAL_ENTER(curthread); + THR_THREAD_UNLOCK(curthread, thread); + } + + /* Return zero if the thread exists: */ + return (ret); +} + +void +_thr_ref_delete(struct pthread *curthread, struct pthread *thread) +{ + THR_THREAD_LOCK(curthread, thread); + thread->refcount--; + _thr_try_gc(curthread, thread); + THR_CRITICAL_LEAVE(curthread); +} + +/* entered with thread lock held, exit with thread lock released */ +void +_thr_try_gc(struct pthread *curthread, struct pthread *thread) +{ + if (THR_SHOULD_GC(thread)) { + THR_REF_ADD(curthread, thread); + THR_THREAD_UNLOCK(curthread, thread); + THREAD_LIST_WRLOCK(curthread); + THR_THREAD_LOCK(curthread, thread); + THR_REF_DEL(curthread, thread); + if (THR_SHOULD_GC(thread)) { + THR_LIST_REMOVE(thread); + THR_GCLIST_ADD(thread); + } + THR_THREAD_UNLOCK(curthread, thread); + THREAD_LIST_UNLOCK(curthread); + } else { + THR_THREAD_UNLOCK(curthread, thread); + } +} + +/* return with thread lock held if thread is found */ +int +_thr_find_thread(struct pthread *curthread, struct pthread *thread, + int include_dead) +{ + struct pthread *pthread; + int ret; + + if (thread == NULL) + return (EINVAL); + + ret = 0; + THREAD_LIST_RDLOCK(curthread); + pthread = _thr_hash_find(thread); + if (pthread) { + THR_THREAD_LOCK(curthread, pthread); + if (include_dead == 0 && pthread->state == PS_DEAD) { + THR_THREAD_UNLOCK(curthread, pthread); + ret = ESRCH; + } + } else { + ret = ESRCH; + } + THREAD_LIST_UNLOCK(curthread); + return (ret); +} diff --git a/lib/libthr/thread/thr_main_np.c b/lib/libthr/thread/thr_main_np.c new file mode 100644 index 0000000..bfa8b87 --- /dev/null +++ b/lib/libthr/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 "namespace.h" +#include <pthread.h> +#include <pthread_np.h> +#include "un-namespace.h" + +#include "thr_private.h" + +__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/libthr/thread/thr_multi_np.c b/lib/libthr/thread/thr_multi_np.c new file mode 100644 index 0000000..339a21d --- /dev/null +++ b/lib/libthr/thread/thr_multi_np.c @@ -0,0 +1,50 @@ +/* + * 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. 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 <pthread_np.h> +#include "un-namespace.h" + +__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/libthr/thread/thr_mutex.c b/lib/libthr/thread/thr_mutex.c new file mode 100644 index 0000000..7573f28 --- /dev/null +++ b/lib/libthr/thread/thr_mutex.c @@ -0,0 +1,796 @@ +/* + * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * Copyright (c) 2006 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 "namespace.h" +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <sys/param.h> +#include <sys/queue.h> +#include <pthread.h> +#include <pthread_np.h> +#include "un-namespace.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 (__predict_false((m)->m_qe.tqe_prev == NULL))\ + PANIC("mutex is not on list"); \ +} while (0) +#define MUTEX_ASSERT_NOT_OWNED(m) do { \ + if (__predict_false((m)->m_qe.tqe_prev != NULL || \ + (m)->m_qe.tqe_next != NULL)) \ + PANIC("mutex is on list"); \ +} while (0) +#else +#define MUTEX_INIT_LINK(m) +#define MUTEX_ASSERT_IS_OWNED(m) +#define MUTEX_ASSERT_NOT_OWNED(m) +#endif + +/* + * For adaptive mutexes, how many times to spin doing trylock2 + * before entering the kernel to block + */ +#define MUTEX_ADAPTIVE_SPINS 2000 + +/* + * Prototypes + */ +int __pthread_mutex_init(pthread_mutex_t *mutex, + const pthread_mutexattr_t *mutex_attr); +int __pthread_mutex_trylock(pthread_mutex_t *mutex); +int __pthread_mutex_lock(pthread_mutex_t *mutex); +int __pthread_mutex_timedlock(pthread_mutex_t *mutex, + const struct timespec *abstime); +int _pthread_mutex_init_calloc_cb(pthread_mutex_t *mutex, + void *(calloc_cb)(size_t, size_t)); +int _pthread_mutex_getspinloops_np(pthread_mutex_t *mutex, int *count); +int _pthread_mutex_setspinloops_np(pthread_mutex_t *mutex, int count); +int __pthread_mutex_setspinloops_np(pthread_mutex_t *mutex, int count); +int _pthread_mutex_setyieldloops_np(pthread_mutex_t *mutex, int count); +int _pthread_mutex_getyieldloops_np(pthread_mutex_t *mutex, int *count); +int __pthread_mutex_setyieldloops_np(pthread_mutex_t *mutex, int count); + +static int mutex_self_trylock(pthread_mutex_t); +static int mutex_self_lock(pthread_mutex_t, + const struct timespec *abstime); +static int mutex_unlock_common(struct pthread_mutex *, int); +static int mutex_lock_sleep(struct pthread *, pthread_mutex_t, + const struct timespec *); + +__weak_reference(__pthread_mutex_init, pthread_mutex_init); +__strong_reference(__pthread_mutex_init, _pthread_mutex_init); +__weak_reference(__pthread_mutex_lock, pthread_mutex_lock); +__strong_reference(__pthread_mutex_lock, _pthread_mutex_lock); +__weak_reference(__pthread_mutex_timedlock, pthread_mutex_timedlock); +__strong_reference(__pthread_mutex_timedlock, _pthread_mutex_timedlock); +__weak_reference(__pthread_mutex_trylock, pthread_mutex_trylock); +__strong_reference(__pthread_mutex_trylock, _pthread_mutex_trylock); + +/* Single underscore versions provided for libc internal usage: */ +/* 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); + +__weak_reference(_pthread_mutex_getprioceiling, pthread_mutex_getprioceiling); +__weak_reference(_pthread_mutex_setprioceiling, pthread_mutex_setprioceiling); + +__weak_reference(__pthread_mutex_setspinloops_np, pthread_mutex_setspinloops_np); +__strong_reference(__pthread_mutex_setspinloops_np, _pthread_mutex_setspinloops_np); +__weak_reference(_pthread_mutex_getspinloops_np, pthread_mutex_getspinloops_np); + +__weak_reference(__pthread_mutex_setyieldloops_np, pthread_mutex_setyieldloops_np); +__strong_reference(__pthread_mutex_setyieldloops_np, _pthread_mutex_setyieldloops_np); +__weak_reference(_pthread_mutex_getyieldloops_np, pthread_mutex_getyieldloops_np); +__weak_reference(_pthread_mutex_isowned_np, pthread_mutex_isowned_np); + +static int +mutex_init(pthread_mutex_t *mutex, + const struct pthread_mutex_attr *mutex_attr, + void *(calloc_cb)(size_t, size_t)) +{ + const struct pthread_mutex_attr *attr; + struct pthread_mutex *pmutex; + + if (mutex_attr == NULL) { + attr = &_pthread_mutexattr_default; + } else { + attr = mutex_attr; + if (attr->m_type < PTHREAD_MUTEX_ERRORCHECK || + attr->m_type >= PTHREAD_MUTEX_TYPE_MAX) + return (EINVAL); + if (attr->m_protocol < PTHREAD_PRIO_NONE || + attr->m_protocol > PTHREAD_PRIO_PROTECT) + return (EINVAL); + } + if ((pmutex = (pthread_mutex_t) + calloc_cb(1, sizeof(struct pthread_mutex))) == NULL) + return (ENOMEM); + + pmutex->m_flags = attr->m_type; + pmutex->m_owner = NULL; + pmutex->m_count = 0; + pmutex->m_spinloops = 0; + pmutex->m_yieldloops = 0; + MUTEX_INIT_LINK(pmutex); + switch(attr->m_protocol) { + case PTHREAD_PRIO_NONE: + pmutex->m_lock.m_owner = UMUTEX_UNOWNED; + pmutex->m_lock.m_flags = 0; + break; + case PTHREAD_PRIO_INHERIT: + pmutex->m_lock.m_owner = UMUTEX_UNOWNED; + pmutex->m_lock.m_flags = UMUTEX_PRIO_INHERIT; + break; + case PTHREAD_PRIO_PROTECT: + pmutex->m_lock.m_owner = UMUTEX_CONTESTED; + pmutex->m_lock.m_flags = UMUTEX_PRIO_PROTECT; + pmutex->m_lock.m_ceilings[0] = attr->m_ceiling; + break; + } + + if (PMUTEX_TYPE(pmutex->m_flags) == PTHREAD_MUTEX_ADAPTIVE_NP) { + pmutex->m_spinloops = + _thr_spinloops ? _thr_spinloops: MUTEX_ADAPTIVE_SPINS; + pmutex->m_yieldloops = _thr_yieldloops; + } + + *mutex = pmutex; + return (0); +} + +static int +init_static(struct pthread *thread, pthread_mutex_t *mutex) +{ + int ret; + + THR_LOCK_ACQUIRE(thread, &_mutex_static_lock); + + if (*mutex == THR_MUTEX_INITIALIZER) + ret = mutex_init(mutex, &_pthread_mutexattr_default, calloc); + else if (*mutex == THR_ADAPTIVE_MUTEX_INITIALIZER) + ret = mutex_init(mutex, &_pthread_mutexattr_adaptive_default, calloc); + else + ret = 0; + THR_LOCK_RELEASE(thread, &_mutex_static_lock); + + return (ret); +} + +static void +set_inherited_priority(struct pthread *curthread, struct pthread_mutex *m) +{ + struct pthread_mutex *m2; + + m2 = TAILQ_LAST(&curthread->pp_mutexq, mutex_queue); + if (m2 != NULL) + m->m_lock.m_ceilings[1] = m2->m_lock.m_ceilings[0]; + else + m->m_lock.m_ceilings[1] = -1; +} + +int +__pthread_mutex_init(pthread_mutex_t *mutex, + const pthread_mutexattr_t *mutex_attr) +{ + return mutex_init(mutex, mutex_attr ? *mutex_attr : NULL, calloc); +} + +/* This function is used internally by malloc. */ +int +_pthread_mutex_init_calloc_cb(pthread_mutex_t *mutex, + void *(calloc_cb)(size_t, size_t)) +{ + static const struct pthread_mutex_attr attr = { + .m_type = PTHREAD_MUTEX_NORMAL, + .m_protocol = PTHREAD_PRIO_NONE, + .m_ceiling = 0 + }; + int ret; + + ret = mutex_init(mutex, &attr, calloc_cb); + if (ret == 0) + (*mutex)->m_flags |= PMUTEX_FLAG_PRIVATE; + return (ret); +} + +void +_mutex_fork(struct pthread *curthread) +{ + struct pthread_mutex *m; + + /* + * Fix mutex ownership for child process. + * note that process shared mutex should not + * be inherited because owner is forking thread + * which is in parent process, they should be + * removed from the owned mutex list, current, + * process shared mutex is not supported, so I + * am not worried. + */ + + TAILQ_FOREACH(m, &curthread->mutexq, m_qe) + m->m_lock.m_owner = TID(curthread); + TAILQ_FOREACH(m, &curthread->pp_mutexq, m_qe) + m->m_lock.m_owner = TID(curthread) | UMUTEX_CONTESTED; +} + +int +_pthread_mutex_destroy(pthread_mutex_t *mutex) +{ + pthread_mutex_t m; + int ret; + + m = *mutex; + if (m < THR_MUTEX_DESTROYED) { + ret = 0; + } else if (m == THR_MUTEX_DESTROYED) { + ret = EINVAL; + } else { + if (m->m_owner != NULL) { + ret = EBUSY; + } else { + *mutex = THR_MUTEX_DESTROYED; + MUTEX_ASSERT_NOT_OWNED(m); + free(m); + ret = 0; + } + } + + return (ret); +} + +#define ENQUEUE_MUTEX(curthread, m) \ + do { \ + (m)->m_owner = curthread; \ + /* Add to the list of owned mutexes: */ \ + MUTEX_ASSERT_NOT_OWNED((m)); \ + if (((m)->m_lock.m_flags & UMUTEX_PRIO_PROTECT) == 0) \ + TAILQ_INSERT_TAIL(&curthread->mutexq, (m), m_qe);\ + else \ + TAILQ_INSERT_TAIL(&curthread->pp_mutexq, (m), m_qe);\ + } while (0) + +#define DEQUEUE_MUTEX(curthread, m) \ + (m)->m_owner = NULL; \ + MUTEX_ASSERT_IS_OWNED(m); \ + if (__predict_true(((m)->m_lock.m_flags & UMUTEX_PRIO_PROTECT) == 0)) \ + TAILQ_REMOVE(&curthread->mutexq, (m), m_qe); \ + else { \ + TAILQ_REMOVE(&curthread->pp_mutexq, (m), m_qe); \ + set_inherited_priority(curthread, m); \ + } \ + MUTEX_INIT_LINK(m); + +#define CHECK_AND_INIT_MUTEX \ + if (__predict_false((m = *mutex) <= THR_MUTEX_DESTROYED)) { \ + if (m == THR_MUTEX_DESTROYED) \ + return (EINVAL); \ + int ret; \ + ret = init_static(_get_curthread(), mutex); \ + if (ret) \ + return (ret); \ + m = *mutex; \ + } + +static int +mutex_trylock_common(pthread_mutex_t *mutex) +{ + struct pthread *curthread = _get_curthread(); + struct pthread_mutex *m = *mutex; + uint32_t id; + int ret; + + id = TID(curthread); + if (m->m_flags & PMUTEX_FLAG_PRIVATE) + THR_CRITICAL_ENTER(curthread); + ret = _thr_umutex_trylock(&m->m_lock, id); + if (__predict_true(ret == 0)) { + ENQUEUE_MUTEX(curthread, m); + } else if (m->m_owner == curthread) { + ret = mutex_self_trylock(m); + } /* else {} */ + if (ret && (m->m_flags & PMUTEX_FLAG_PRIVATE)) + THR_CRITICAL_LEAVE(curthread); + return (ret); +} + +int +__pthread_mutex_trylock(pthread_mutex_t *mutex) +{ + struct pthread_mutex *m; + + CHECK_AND_INIT_MUTEX + + return (mutex_trylock_common(mutex)); +} + +static int +mutex_lock_sleep(struct pthread *curthread, struct pthread_mutex *m, + const struct timespec *abstime) +{ + uint32_t id, owner; + int count; + int ret; + + if (m->m_owner == curthread) + return mutex_self_lock(m, abstime); + + id = TID(curthread); + /* + * For adaptive mutexes, spin for a bit in the expectation + * that if the application requests this mutex type then + * the lock is likely to be released quickly and it is + * faster than entering the kernel + */ + if (__predict_false( + (m->m_lock.m_flags & + (UMUTEX_PRIO_PROTECT | UMUTEX_PRIO_INHERIT)) != 0)) + goto sleep_in_kernel; + + if (!_thr_is_smp) + goto yield_loop; + + count = m->m_spinloops; + while (count--) { + owner = m->m_lock.m_owner; + if ((owner & ~UMUTEX_CONTESTED) == 0) { + if (atomic_cmpset_acq_32(&m->m_lock.m_owner, owner, id|owner)) { + ret = 0; + goto done; + } + } + CPU_SPINWAIT; + } + +yield_loop: + count = m->m_yieldloops; + while (count--) { + _sched_yield(); + owner = m->m_lock.m_owner; + if ((owner & ~UMUTEX_CONTESTED) == 0) { + if (atomic_cmpset_acq_32(&m->m_lock.m_owner, owner, id|owner)) { + ret = 0; + goto done; + } + } + } + +sleep_in_kernel: + if (abstime == NULL) { + ret = __thr_umutex_lock(&m->m_lock, id); + } else if (__predict_false( + abstime->tv_nsec < 0 || + abstime->tv_nsec >= 1000000000)) { + ret = EINVAL; + } else { + ret = __thr_umutex_timedlock(&m->m_lock, id, abstime); + } +done: + if (ret == 0) + ENQUEUE_MUTEX(curthread, m); + + return (ret); +} + +static inline int +mutex_lock_common(struct pthread_mutex *m, + const struct timespec *abstime, int cvattach) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + if (!cvattach && m->m_flags & PMUTEX_FLAG_PRIVATE) + THR_CRITICAL_ENTER(curthread); + if (_thr_umutex_trylock2(&m->m_lock, TID(curthread)) == 0) { + ENQUEUE_MUTEX(curthread, m); + ret = 0; + } else { + ret = mutex_lock_sleep(curthread, m, abstime); + } + if (ret && (m->m_flags & PMUTEX_FLAG_PRIVATE) && !cvattach) + THR_CRITICAL_LEAVE(curthread); + return (ret); +} + +int +__pthread_mutex_lock(pthread_mutex_t *mutex) +{ + struct pthread_mutex *m; + + _thr_check_init(); + + CHECK_AND_INIT_MUTEX + + return (mutex_lock_common(m, NULL, 0)); +} + +int +__pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *abstime) +{ + struct pthread_mutex *m; + + _thr_check_init(); + + CHECK_AND_INIT_MUTEX + + return (mutex_lock_common(m, abstime, 0)); +} + +int +_pthread_mutex_unlock(pthread_mutex_t *mutex) +{ + struct pthread_mutex *mp; + + mp = *mutex; + return (mutex_unlock_common(mp, 0)); +} + +int +_mutex_cv_lock(struct pthread_mutex *m, int count) +{ + int error; + + error = mutex_lock_common(m, NULL, 1); + if (error == 0) + m->m_count = count; + return (error); +} + +int +_mutex_cv_unlock(struct pthread_mutex *m, int *count) +{ + + /* + * Clear the count in case this is a recursive mutex. + */ + *count = m->m_count; + m->m_count = 0; + (void)mutex_unlock_common(m, 1); + return (0); +} + +int +_mutex_cv_attach(struct pthread_mutex *m, int count) +{ + struct pthread *curthread = _get_curthread(); + + ENQUEUE_MUTEX(curthread, m); + m->m_count = count; + return (0); +} + +int +_mutex_cv_detach(struct pthread_mutex *mp, int *recurse) +{ + struct pthread *curthread = _get_curthread(); + int defered; + int error; + + if ((error = _mutex_owned(curthread, mp)) != 0) + return (error); + + /* + * Clear the count in case this is a recursive mutex. + */ + *recurse = mp->m_count; + mp->m_count = 0; + DEQUEUE_MUTEX(curthread, mp); + + /* Will this happen in real-world ? */ + if ((mp->m_flags & PMUTEX_FLAG_DEFERED) != 0) { + defered = 1; + mp->m_flags &= ~PMUTEX_FLAG_DEFERED; + } else + defered = 0; + + if (defered) { + _thr_wake_all(curthread->defer_waiters, + curthread->nwaiter_defer); + curthread->nwaiter_defer = 0; + } + return (0); +} + +static int +mutex_self_trylock(struct pthread_mutex *m) +{ + int ret; + + switch (PMUTEX_TYPE(m->m_flags)) { + case PTHREAD_MUTEX_ERRORCHECK: + case PTHREAD_MUTEX_NORMAL: + ret = EBUSY; + break; + + case PTHREAD_MUTEX_RECURSIVE: + /* Increment the lock count: */ + if (m->m_count + 1 > 0) { + m->m_count++; + ret = 0; + } else + ret = EAGAIN; + break; + + default: + /* Trap invalid mutex types; */ + ret = EINVAL; + } + + return (ret); +} + +static int +mutex_self_lock(struct pthread_mutex *m, const struct timespec *abstime) +{ + struct timespec ts1, ts2; + int ret; + + switch (PMUTEX_TYPE(m->m_flags)) { + case PTHREAD_MUTEX_ERRORCHECK: + case PTHREAD_MUTEX_ADAPTIVE_NP: + if (abstime) { + if (abstime->tv_sec < 0 || abstime->tv_nsec < 0 || + abstime->tv_nsec >= 1000000000) { + ret = EINVAL; + } else { + clock_gettime(CLOCK_REALTIME, &ts1); + TIMESPEC_SUB(&ts2, abstime, &ts1); + __sys_nanosleep(&ts2, NULL); + ret = ETIMEDOUT; + } + } else { + /* + * 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. + */ + ret = 0; + if (abstime) { + if (abstime->tv_sec < 0 || abstime->tv_nsec < 0 || + abstime->tv_nsec >= 1000000000) { + ret = EINVAL; + } else { + clock_gettime(CLOCK_REALTIME, &ts1); + TIMESPEC_SUB(&ts2, abstime, &ts1); + __sys_nanosleep(&ts2, NULL); + ret = ETIMEDOUT; + } + } else { + ts1.tv_sec = 30; + ts1.tv_nsec = 0; + for (;;) + __sys_nanosleep(&ts1, NULL); + } + break; + + case PTHREAD_MUTEX_RECURSIVE: + /* Increment the lock count: */ + if (m->m_count + 1 > 0) { + m->m_count++; + ret = 0; + } else + ret = EAGAIN; + break; + + default: + /* Trap invalid mutex types; */ + ret = EINVAL; + } + + return (ret); +} + +static int +mutex_unlock_common(struct pthread_mutex *m, int cv) +{ + struct pthread *curthread = _get_curthread(); + uint32_t id; + int defered; + + if (__predict_false(m <= THR_MUTEX_DESTROYED)) { + if (m == THR_MUTEX_DESTROYED) + return (EINVAL); + return (EPERM); + } + + /* + * Check if the running thread is not the owner of the mutex. + */ + if (__predict_false(m->m_owner != curthread)) + return (EPERM); + + id = TID(curthread); + if (__predict_false( + PMUTEX_TYPE(m->m_flags) == PTHREAD_MUTEX_RECURSIVE && + m->m_count > 0)) { + m->m_count--; + } else { + if ((m->m_flags & PMUTEX_FLAG_DEFERED) != 0) { + defered = 1; + m->m_flags &= ~PMUTEX_FLAG_DEFERED; + } else + defered = 0; + + DEQUEUE_MUTEX(curthread, m); + _thr_umutex_unlock(&m->m_lock, id); + + if (curthread->will_sleep == 0 && defered) { + _thr_wake_all(curthread->defer_waiters, + curthread->nwaiter_defer); + curthread->nwaiter_defer = 0; + } + } + if (!cv && m->m_flags & PMUTEX_FLAG_PRIVATE) + THR_CRITICAL_LEAVE(curthread); + return (0); +} + +int +_pthread_mutex_getprioceiling(pthread_mutex_t *mutex, + int *prioceiling) +{ + struct pthread_mutex *m; + int ret; + + m = *mutex; + if ((m <= THR_MUTEX_DESTROYED) || + (m->m_lock.m_flags & UMUTEX_PRIO_PROTECT) == 0) + ret = EINVAL; + else { + *prioceiling = m->m_lock.m_ceilings[0]; + ret = 0; + } + + return (ret); +} + +int +_pthread_mutex_setprioceiling(pthread_mutex_t *mutex, + int ceiling, int *old_ceiling) +{ + struct pthread *curthread = _get_curthread(); + struct pthread_mutex *m, *m1, *m2; + int ret; + + m = *mutex; + if ((m <= THR_MUTEX_DESTROYED) || + (m->m_lock.m_flags & UMUTEX_PRIO_PROTECT) == 0) + return (EINVAL); + + ret = __thr_umutex_set_ceiling(&m->m_lock, ceiling, old_ceiling); + if (ret != 0) + return (ret); + + if (m->m_owner == curthread) { + MUTEX_ASSERT_IS_OWNED(m); + m1 = TAILQ_PREV(m, mutex_queue, m_qe); + m2 = TAILQ_NEXT(m, m_qe); + if ((m1 != NULL && m1->m_lock.m_ceilings[0] > (u_int)ceiling) || + (m2 != NULL && m2->m_lock.m_ceilings[0] < (u_int)ceiling)) { + TAILQ_REMOVE(&curthread->pp_mutexq, m, m_qe); + TAILQ_FOREACH(m2, &curthread->pp_mutexq, m_qe) { + if (m2->m_lock.m_ceilings[0] > (u_int)ceiling) { + TAILQ_INSERT_BEFORE(m2, m, m_qe); + return (0); + } + } + TAILQ_INSERT_TAIL(&curthread->pp_mutexq, m, m_qe); + } + } + return (0); +} + +int +_pthread_mutex_getspinloops_np(pthread_mutex_t *mutex, int *count) +{ + struct pthread_mutex *m; + + CHECK_AND_INIT_MUTEX + + *count = m->m_spinloops; + return (0); +} + +int +__pthread_mutex_setspinloops_np(pthread_mutex_t *mutex, int count) +{ + struct pthread_mutex *m; + + CHECK_AND_INIT_MUTEX + + m->m_spinloops = count; + return (0); +} + +int +_pthread_mutex_getyieldloops_np(pthread_mutex_t *mutex, int *count) +{ + struct pthread_mutex *m; + + CHECK_AND_INIT_MUTEX + + *count = m->m_yieldloops; + return (0); +} + +int +__pthread_mutex_setyieldloops_np(pthread_mutex_t *mutex, int count) +{ + struct pthread_mutex *m; + + CHECK_AND_INIT_MUTEX + + m->m_yieldloops = count; + return (0); +} + +int +_pthread_mutex_isowned_np(pthread_mutex_t *mutex) +{ + struct pthread_mutex *m; + + m = *mutex; + if (m <= THR_MUTEX_DESTROYED) + return (0); + return (m->m_owner == _get_curthread()); +} + +int +_mutex_owned(struct pthread *curthread, const struct pthread_mutex *mp) +{ + if (__predict_false(mp <= THR_MUTEX_DESTROYED)) { + if (mp == THR_MUTEX_DESTROYED) + return (EINVAL); + return (EPERM); + } + if (mp->m_owner != curthread) + return (EPERM); + return (0); +} diff --git a/lib/libthr/thread/thr_mutexattr.c b/lib/libthr/thread/thr_mutexattr.c new file mode 100644 index 0000000..7c48ed22 --- /dev/null +++ b/lib/libthr/thread/thr_mutexattr.c @@ -0,0 +1,255 @@ +/* + * 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. 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$ + */ + +/* + * 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. 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 "namespace.h" +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <pthread.h> +#include <pthread_np.h> +#include "un-namespace.h" + +#include "thr_private.h" + +__weak_reference(_pthread_mutexattr_init, pthread_mutexattr_init); +__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); +__weak_reference(_pthread_mutexattr_destroy, pthread_mutexattr_destroy); +__weak_reference(_pthread_mutexattr_getpshared, pthread_mutexattr_getpshared); +__weak_reference(_pthread_mutexattr_setpshared, pthread_mutexattr_setpshared); +__weak_reference(_pthread_mutexattr_getprotocol, pthread_mutexattr_getprotocol); +__weak_reference(_pthread_mutexattr_setprotocol, pthread_mutexattr_setprotocol); +__weak_reference(_pthread_mutexattr_getprioceiling, pthread_mutexattr_getprioceiling); +__weak_reference(_pthread_mutexattr_setprioceiling, pthread_mutexattr_setprioceiling); + +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); +} + +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) { + ret = EINVAL; + } 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; +} + +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); +} + +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); + + /* Only PTHREAD_PROCESS_PRIVATE is supported. */ + if (pshared != PTHREAD_PROCESS_PRIVATE) + return (EINVAL); + + return (0); +} + +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_RR_PRIORITY; + } + return(ret); +} + +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); +} + diff --git a/lib/libthr/thread/thr_once.c b/lib/libthr/thread/thr_once.c new file mode 100644 index 0000000..4f70374 --- /dev/null +++ b/lib/libthr/thread/thr_once.c @@ -0,0 +1,99 @@ +/* + * 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 "namespace.h" +#include <pthread.h> +#include "un-namespace.h" + +#include "thr_private.h" + +__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_WAIT 0x03 + +/* + * 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; + + if (atomic_cmpset_rel_int(&once_control->state, ONCE_IN_PROGRESS, ONCE_NEVER_DONE)) + return; + atomic_store_rel_int(&once_control->state, ONCE_NEVER_DONE); + _thr_umtx_wake(&once_control->state, INT_MAX, 0); +} + +int +_pthread_once(pthread_once_t *once_control, void (*init_routine) (void)) +{ + struct pthread *curthread; + int state; + + _thr_check_init(); + + for (;;) { + state = once_control->state; + if (state == ONCE_DONE) + return (0); + if (state == ONCE_NEVER_DONE) { + if (atomic_cmpset_acq_int(&once_control->state, state, ONCE_IN_PROGRESS)) + break; + } else if (state == ONCE_IN_PROGRESS) { + if (atomic_cmpset_acq_int(&once_control->state, state, ONCE_WAIT)) + _thr_umtx_wait_uint(&once_control->state, ONCE_WAIT, NULL, 0); + } else if (state == ONCE_WAIT) { + _thr_umtx_wait_uint(&once_control->state, state, NULL, 0); + } else + return (EINVAL); + } + + curthread = _get_curthread(); + THR_CLEANUP_PUSH(curthread, once_cancel_handler, once_control); + init_routine(); + THR_CLEANUP_POP(curthread, 0); + if (atomic_cmpset_rel_int(&once_control->state, ONCE_IN_PROGRESS, ONCE_DONE)) + return (0); + atomic_store_rel_int(&once_control->state, ONCE_DONE); + _thr_umtx_wake(&once_control->state, INT_MAX, 0); + return (0); +} + +void +_thr_once_init() +{ +} diff --git a/lib/libthr/thread/thr_printf.c b/lib/libthr/thread/thr_printf.c new file mode 100644 index 0000000..7d32ae7 --- /dev/null +++ b/lib/libthr/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. + * + * $FreeBSD$ + */ + +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <pthread.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/libthr/thread/thr_private.h b/lib/libthr/thread/thr_private.h new file mode 100644 index 0000000..4469f6b --- /dev/null +++ b/lib/libthr/thread/thr_private.h @@ -0,0 +1,908 @@ +/* + * Copyright (C) 2005 Daniel M. Eischen <deischen@freebsd.org> + * Copyright (c) 2005 David Xu <davidxu@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 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$ + */ + +#ifndef _THR_PRIVATE_H +#define _THR_PRIVATE_H + +/* + * Include files. + */ +#include <sys/types.h> +#include <sys/time.h> +#include <sys/cdefs.h> +#include <sys/queue.h> +#include <sys/param.h> +#include <sys/cpuset.h> +#include <machine/atomic.h> +#include <errno.h> +#include <limits.h> +#include <signal.h> +#include <stddef.h> +#include <stdio.h> +#include <unistd.h> +#include <ucontext.h> +#include <sys/thr.h> +#include <pthread.h> + +#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 FB10_COMPAT(func, sym) \ + WEAK_REF(func, SYM_FB10(sym)); \ + SYM_COMPAT(sym, SYM_FB10(sym), FBSD_1.0) + +#define FB10_COMPAT_PRIVATE(func, sym) \ + WEAK_REF(func, SYM_FBP10(sym)); \ + SYM_DEFAULT(sym, SYM_FBP10(sym), FBSDprivate_1.0) + +#include "pthread_md.h" +#include "thr_umtx.h" +#include "thread_db.h" + +#ifdef _PTHREAD_FORCED_UNWIND +#define _BSD_SOURCE +#include <unwind.h> +#endif + +typedef TAILQ_HEAD(pthreadlist, pthread) pthreadlist; +typedef TAILQ_HEAD(atfork_head, pthread_atfork) atfork_head; +TAILQ_HEAD(mutex_queue, pthread_mutex); + +/* Signal to do cancellation */ +#define SIGCANCEL SIGTHR + +/* + * Kernel fatal error handler macro. + */ +#define PANIC(string) _thread_exit(__FILE__,__LINE__,string) + +/* Output debug messages like this: */ +#define stdout_debug(args...) _thread_printf(STDOUT_FILENO, ##args) +#define stderr_debug(args...) _thread_printf(STDERR_FILENO, ##args) + +#ifdef _PTHREADS_INVARIANTS +#define THR_ASSERT(cond, msg) do { \ + if (__predict_false(!(cond))) \ + PANIC(msg); \ +} while (0) +#else +#define THR_ASSERT(cond, msg) +#endif + +#ifdef PIC +# define STATIC_LIB_REQUIRE(name) +#else +# define STATIC_LIB_REQUIRE(name) __asm (".globl " #name) +#endif + +#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) + +/* XXX These values should be same as those defined in pthread.h */ +#define THR_MUTEX_INITIALIZER ((struct pthread_mutex *)NULL) +#define THR_ADAPTIVE_MUTEX_INITIALIZER ((struct pthread_mutex *)1) +#define THR_MUTEX_DESTROYED ((struct pthread_mutex *)2) +#define THR_COND_INITIALIZER ((struct pthread_cond *)NULL) +#define THR_COND_DESTROYED ((struct pthread_cond *)1) +#define THR_RWLOCK_INITIALIZER ((struct pthread_rwlock *)NULL) +#define THR_RWLOCK_DESTROYED ((struct pthread_rwlock *)1) + +#define PMUTEX_FLAG_TYPE_MASK 0x0ff +#define PMUTEX_FLAG_PRIVATE 0x100 +#define PMUTEX_FLAG_DEFERED 0x200 +#define PMUTEX_TYPE(mtxflags) ((mtxflags) & PMUTEX_FLAG_TYPE_MASK) + +#define MAX_DEFER_WAITERS 50 + +struct pthread_mutex { + /* + * Lock for accesses to this structure. + */ + struct umutex m_lock; + int m_flags; + struct pthread *m_owner; + int m_count; + int m_spinloops; + int m_yieldloops; + /* + * Link for all mutexes a thread currently owns. + */ + TAILQ_ENTRY(pthread_mutex) m_qe; +}; + +struct pthread_mutex_attr { + enum pthread_mutextype m_type; + int m_protocol; + int m_ceiling; +}; + +#define PTHREAD_MUTEXATTR_STATIC_INITIALIZER \ + { PTHREAD_MUTEX_DEFAULT, PTHREAD_PRIO_NONE, 0, MUTEX_FLAGS_PRIVATE } + +struct pthread_cond { + __uint32_t __has_user_waiters; + __uint32_t __has_kern_waiters; + __uint32_t __flags; + __uint32_t __clock_id; +}; + +struct pthread_cond_attr { + int c_pshared; + int c_clockid; +}; + +struct pthread_barrier { + struct umutex b_lock; + struct ucond b_cv; + int64_t b_cycle; + int b_count; + int b_waiters; + int b_refcount; + int b_destroying; +}; + +struct pthread_barrierattr { + int pshared; +}; + +struct pthread_spinlock { + struct umutex s_lock; +}; + +/* + * Flags for condition variables. + */ +#define COND_FLAGS_PRIVATE 0x01 +#define COND_FLAGS_INITED 0x02 +#define COND_FLAGS_BUSY 0x04 + +/* + * Cleanup definitions. + */ +struct pthread_cleanup { + struct pthread_cleanup *prev; + void (*routine)(void *); + void *routine_arg; + int onheap; +}; + +#define THR_CLEANUP_PUSH(td, func, arg) { \ + struct pthread_cleanup __cup; \ + \ + __cup.routine = func; \ + __cup.routine_arg = arg; \ + __cup.onheap = 0; \ + __cup.prev = (td)->cleanup; \ + (td)->cleanup = &__cup; + +#define THR_CLEANUP_POP(td, exec) \ + (td)->cleanup = __cup.prev; \ + 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 { +#define pthread_attr_start_copy sched_policy + int sched_policy; + int sched_inherit; + int prio; + int suspend; +#define THR_STACK_USER 0x100 /* 0xFF reserved for <pthread.h> */ + int flags; + void *stackaddr_attr; + size_t stacksize_attr; + size_t guardsize_attr; +#define pthread_attr_end_copy cpuset + cpuset_t *cpuset; + size_t cpusetsize; +}; + +struct wake_addr { + struct wake_addr *link; + unsigned int value; + char pad[12]; +}; + +struct sleepqueue { + TAILQ_HEAD(, pthread) sq_blocked; + SLIST_HEAD(, sleepqueue) sq_freeq; + LIST_ENTRY(sleepqueue) sq_hash; + SLIST_ENTRY(sleepqueue) sq_flink; + void *sq_wchan; + int sq_type; +}; + +/* + * Thread creation state attributes. + */ +#define THR_CREATE_RUNNING 0 +#define THR_CREATE_SUSPENDED 1 + +/* + * Miscellaneous definitions. + */ +#define THR_STACK_DEFAULT (sizeof(void *) / 4 * 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_STACK_INITIAL (THR_STACK_DEFAULT * 2) + +/* + * Define priorities returned by kernel. + */ +#define THR_MIN_PRIORITY (_thr_priorities[SCHED_OTHER-1].pri_min) +#define THR_MAX_PRIORITY (_thr_priorities[SCHED_OTHER-1].pri_max) +#define THR_DEF_PRIORITY (_thr_priorities[SCHED_OTHER-1].pri_default) + +#define THR_MIN_RR_PRIORITY (_thr_priorities[SCHED_RR-1].pri_min) +#define THR_MAX_RR_PRIORITY (_thr_priorities[SCHED_RR-1].pri_max) +#define THR_DEF_RR_PRIORITY (_thr_priorities[SCHED_RR-1].pri_default) + +/* XXX The SCHED_FIFO should have same priority range as SCHED_RR */ +#define THR_MIN_FIFO_PRIORITY (_thr_priorities[SCHED_FIFO_1].pri_min) +#define THR_MAX_FIFO_PRIORITY (_thr_priorities[SCHED_FIFO-1].pri_max) +#define THR_DEF_FIFO_PRIORITY (_thr_priorities[SCHED_FIFO-1].pri_default) + +struct pthread_prio { + int pri_min; + int pri_max; + int pri_default; +}; + +struct pthread_rwlockattr { + int pshared; +}; + +struct pthread_rwlock { + struct urwlock lock; + struct pthread *owner; +}; + +/* + * Thread states. + */ +enum pthread_state { + PS_RUNNING, + PS_DEAD +}; + +struct pthread_specific_elem { + const void *data; + int seqno; +}; + +struct pthread_key { + volatile int allocated; + int seqno; + void (*destructor)(void *); +}; + +/* + * lwpid_t is 32bit but kernel thr API exports tid as long type + * in very earily date. + */ +#define TID(thread) ((uint32_t) ((thread)->tid)) + +/* + * Thread structure. + */ +struct pthread { +#define _pthread_startzero tid + /* Kernel thread id. */ + long tid; +#define TID_TERMINATED 1 + + /* + * Lock for accesses to this thread structure. + */ + struct umutex lock; + + /* Internal condition variable cycle number. */ + uint32_t cycle; + + /* How many low level locks the thread held. */ + int locklevel; + + /* + * Set to non-zero when this thread has entered a critical + * region. We allow for recursive entries into critical regions. + */ + int critical_count; + + /* Signal blocked counter. */ + int sigblock; + + /* Queue entry for list of all threads. */ + TAILQ_ENTRY(pthread) tle; /* link for all threads in process */ + + /* Queue entry for GC lists. */ + TAILQ_ENTRY(pthread) gcle; + + /* Hash queue entry. */ + LIST_ENTRY(pthread) hle; + + /* Sleep queue entry */ + TAILQ_ENTRY(pthread) wle; + + /* Threads reference count. */ + int refcount; + + /* + * Thread start routine, argument, stack pointer and thread + * attributes. + */ + void *(*start_routine)(void *); + void *arg; + struct pthread_attr attr; + +#define SHOULD_CANCEL(thr) \ + ((thr)->cancel_pending && (thr)->cancel_enable && \ + (thr)->no_cancel == 0) + + /* Cancellation is enabled */ + int cancel_enable; + + /* Cancellation request is pending */ + int cancel_pending; + + /* Thread is at cancellation point */ + int cancel_point; + + /* Cancellation is temporarily disabled */ + int no_cancel; + + /* Asynchronouse cancellation is enabled */ + int cancel_async; + + /* Cancellation is in progress */ + int cancelling; + + /* Thread temporary signal mask. */ + sigset_t sigmask; + + /* Thread should unblock SIGCANCEL. */ + int unblock_sigcancel; + + /* In sigsuspend state */ + int in_sigsuspend; + + /* deferred signal info */ + siginfo_t deferred_siginfo; + + /* signal mask to restore. */ + sigset_t deferred_sigmask; + + /* the sigaction should be used for deferred signal. */ + struct sigaction deferred_sigact; + + /* Force new thread to exit. */ + int force_exit; + + /* Thread state: */ + enum pthread_state state; + + /* + * 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; + + /* Miscellaneous flags; only set with scheduling lock held. */ + int flags; +#define THR_FLAGS_PRIVATE 0x0001 +#define THR_FLAGS_NEED_SUSPEND 0x0002 /* thread should be suspended */ +#define THR_FLAGS_SUSPENDED 0x0004 /* thread is suspended */ +#define THR_FLAGS_DETACHED 0x0008 /* thread is detached */ + + /* Thread list flags; only set with thread list lock held. */ + int tlflags; +#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 */ + + /* Queue of currently owned NORMAL or PRIO_INHERIT type mutexes. */ + struct mutex_queue mutexq; + + /* Queue of all owned PRIO_PROTECT mutexes. */ + struct mutex_queue pp_mutexq; + + void *ret; + struct pthread_specific_elem *specific; + int specific_data_count; + + /* Number rwlocks rdlocks held. */ + int rdlock_count; + + /* + * Current locks bitmap for rtld. */ + int rtld_bits; + + /* Thread control block */ + struct tcb *tcb; + + /* Cleanup handlers Link List */ + struct pthread_cleanup *cleanup; + +#ifdef _PTHREAD_FORCED_UNWIND + struct _Unwind_Exception ex; + void *unwind_stackend; + int unwind_disabled; +#endif + + /* + * Magic value to help recognize a valid thread structure + * from an invalid one: + */ +#define THR_MAGIC ((u_int32_t) 0xd09ba115) + u_int32_t magic; + + /* Enable event reporting */ + int report_events; + + /* Event mask */ + int event_mask; + + /* Event */ + td_event_msg_t event_buf; + + /* Wait channel */ + void *wchan; + + /* Referenced mutex. */ + struct pthread_mutex *mutex_obj; + + /* Thread will sleep. */ + int will_sleep; + + /* Number of threads deferred. */ + int nwaiter_defer; + + /* Deferred threads from pthread_cond_signal. */ + unsigned int *defer_waiters[MAX_DEFER_WAITERS]; +#define _pthread_endzero wake_addr + + struct wake_addr *wake_addr; +#define WAKE_ADDR(td) ((td)->wake_addr) + + /* Sleep queue */ + struct sleepqueue *sleepqueue; + +}; + +#define THR_SHOULD_GC(thrd) \ + ((thrd)->refcount == 0 && (thrd)->state == PS_DEAD && \ + ((thrd)->flags & THR_FLAGS_DETACHED) != 0) + +#define THR_IN_CRITICAL(thrd) \ + (((thrd)->locklevel > 0) || \ + ((thrd)->critical_count > 0)) + +#define THR_CRITICAL_ENTER(thrd) \ + (thrd)->critical_count++ + +#define THR_CRITICAL_LEAVE(thrd) \ + do { \ + (thrd)->critical_count--; \ + _thr_ast(thrd); \ + } while (0) + +#define THR_UMUTEX_TRYLOCK(thrd, lck) \ + _thr_umutex_trylock((lck), TID(thrd)) + +#define THR_UMUTEX_LOCK(thrd, lck) \ + _thr_umutex_lock((lck), TID(thrd)) + +#define THR_UMUTEX_TIMEDLOCK(thrd, lck, timo) \ + _thr_umutex_timedlock((lck), TID(thrd), (timo)) + +#define THR_UMUTEX_UNLOCK(thrd, lck) \ + _thr_umutex_unlock((lck), TID(thrd)) + +#define THR_LOCK_ACQUIRE(thrd, lck) \ +do { \ + (thrd)->locklevel++; \ + _thr_umutex_lock(lck, TID(thrd)); \ +} while (0) + +#define THR_LOCK_ACQUIRE_SPIN(thrd, lck) \ +do { \ + (thrd)->locklevel++; \ + _thr_umutex_lock_spin(lck, TID(thrd)); \ +} while (0) + +#ifdef _PTHREADS_INVARIANTS +#define THR_ASSERT_LOCKLEVEL(thrd) \ +do { \ + if (__predict_false((thrd)->locklevel <= 0)) \ + _thr_assert_lock_level(); \ +} while (0) +#else +#define THR_ASSERT_LOCKLEVEL(thrd) +#endif + +#define THR_LOCK_RELEASE(thrd, lck) \ +do { \ + THR_ASSERT_LOCKLEVEL(thrd); \ + _thr_umutex_unlock((lck), TID(thrd)); \ + (thrd)->locklevel--; \ + _thr_ast(thrd); \ +} while (0) + +#define THR_LOCK(curthrd) THR_LOCK_ACQUIRE(curthrd, &(curthrd)->lock) +#define THR_UNLOCK(curthrd) THR_LOCK_RELEASE(curthrd, &(curthrd)->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) + +#define THREAD_LIST_RDLOCK(curthrd) \ +do { \ + (curthrd)->locklevel++; \ + _thr_rwl_rdlock(&_thr_list_lock); \ +} while (0) + +#define THREAD_LIST_WRLOCK(curthrd) \ +do { \ + (curthrd)->locklevel++; \ + _thr_rwl_wrlock(&_thr_list_lock); \ +} while (0) + +#define THREAD_LIST_UNLOCK(curthrd) \ +do { \ + _thr_rwl_unlock(&_thr_list_lock); \ + (curthrd)->locklevel--; \ + _thr_ast(curthrd); \ +} while (0) + +/* + * 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 THR_REF_ADD(curthread, pthread) { \ + THR_CRITICAL_ENTER(curthread); \ + pthread->refcount++; \ +} while (0) + +#define THR_REF_DEL(curthread, pthread) { \ + pthread->refcount--; \ + THR_CRITICAL_LEAVE(curthread); \ +} while (0) + +#define GC_NEEDED() (_gc_count >= 5) + +#define SHOULD_REPORT_EVENT(curthr, e) \ + (curthr->report_events && \ + (((curthr)->event_mask | _thread_event_mask ) & e) != 0) + +extern int __isthreaded; + +/* + * Global variables for the pthread kernel. + */ + +extern char *_usrstack __hidden; +extern struct pthread *_thr_initial __hidden; + +/* For debugger */ +extern int _libthr_debug; +extern int _thread_event_mask; +extern struct pthread *_thread_last_event; + +/* List of all threads: */ +extern pthreadlist _thread_list; + +/* List of threads needing GC: */ +extern pthreadlist _thread_gc_list __hidden; + +extern int _thread_active_threads; +extern atfork_head _thr_atfork_list __hidden; +extern struct urwlock _thr_atfork_lock __hidden; + +/* Default thread attributes: */ +extern struct pthread_attr _pthread_attr_default __hidden; + +/* Default mutex attributes: */ +extern struct pthread_mutex_attr _pthread_mutexattr_default __hidden; +extern struct pthread_mutex_attr _pthread_mutexattr_adaptive_default __hidden; + +/* Default condition variable attributes: */ +extern struct pthread_cond_attr _pthread_condattr_default __hidden; + +extern struct pthread_prio _thr_priorities[] __hidden; + +extern pid_t _thr_pid __hidden; +extern int _thr_is_smp __hidden; + +extern size_t _thr_guard_default __hidden; +extern size_t _thr_stack_default __hidden; +extern size_t _thr_stack_initial __hidden; +extern int _thr_page_size __hidden; +extern int _thr_spinloops __hidden; +extern int _thr_yieldloops __hidden; + +/* Garbage thread count. */ +extern int _gc_count __hidden; + +extern struct umutex _mutex_static_lock __hidden; +extern struct umutex _cond_static_lock __hidden; +extern struct umutex _rwlock_static_lock __hidden; +extern struct umutex _keytable_lock __hidden; +extern struct urwlock _thr_list_lock __hidden; +extern struct umutex _thr_event_lock __hidden; + +/* + * Function prototype definitions. + */ +__BEGIN_DECLS +int _thr_setthreaded(int) __hidden; +int _mutex_cv_lock(struct pthread_mutex *, int count) __hidden; +int _mutex_cv_unlock(struct pthread_mutex *, int *count) __hidden; +int _mutex_cv_attach(struct pthread_mutex *, int count) __hidden; +int _mutex_cv_detach(struct pthread_mutex *, int *count) __hidden; +int _mutex_owned(struct pthread *, const struct pthread_mutex *) __hidden; +int _mutex_reinit(pthread_mutex_t *) __hidden; +void _mutex_fork(struct pthread *curthread) __hidden; +void _libpthread_init(struct pthread *) __hidden; +struct pthread *_thr_alloc(struct pthread *) __hidden; +void _thread_exit(const char *, int, const char *) __hidden __dead2; +int _thr_ref_add(struct pthread *, struct pthread *, int) __hidden; +void _thr_ref_delete(struct pthread *, struct pthread *) __hidden; +void _thr_ref_delete_unlocked(struct pthread *, struct pthread *) __hidden; +int _thr_find_thread(struct pthread *, struct pthread *, int) __hidden; +void _thr_rtld_init(void) __hidden; +void _thr_rtld_fini(void) __hidden; +void _thr_rtld_postfork_child(void) __hidden; +int _thr_stack_alloc(struct pthread_attr *) __hidden; +void _thr_stack_free(struct pthread_attr *) __hidden; +void _thr_free(struct pthread *, struct pthread *) __hidden; +void _thr_gc(struct pthread *) __hidden; +void _thread_cleanupspecific(void) __hidden; +void _thread_printf(int, const char *, ...) __hidden; +void _thr_spinlock_init(void) __hidden; +void _thr_cancel_enter(struct pthread *) __hidden; +void _thr_cancel_enter2(struct pthread *, int) __hidden; +void _thr_cancel_leave(struct pthread *, int) __hidden; +void _thr_testcancel(struct pthread *) __hidden; +void _thr_signal_block(struct pthread *) __hidden; +void _thr_signal_unblock(struct pthread *) __hidden; +void _thr_signal_init(void) __hidden; +void _thr_signal_deinit(void) __hidden; +int _thr_send_sig(struct pthread *, int sig) __hidden; +void _thr_list_init(void) __hidden; +void _thr_hash_add(struct pthread *) __hidden; +void _thr_hash_remove(struct pthread *) __hidden; +struct pthread *_thr_hash_find(struct pthread *) __hidden; +void _thr_link(struct pthread *, struct pthread *) __hidden; +void _thr_unlink(struct pthread *, struct pthread *) __hidden; +void _thr_assert_lock_level(void) __hidden __dead2; +void _thr_ast(struct pthread *) __hidden; +void _thr_once_init(void) __hidden; +void _thr_report_creation(struct pthread *curthread, + struct pthread *newthread) __hidden; +void _thr_report_death(struct pthread *curthread) __hidden; +int _thr_getscheduler(lwpid_t, int *, struct sched_param *) __hidden; +int _thr_setscheduler(lwpid_t, int, const struct sched_param *) __hidden; +void _thr_signal_prefork(void) __hidden; +void _thr_signal_postfork(void) __hidden; +void _thr_signal_postfork_child(void) __hidden; +void _thr_try_gc(struct pthread *, struct pthread *) __hidden; +int _rtp_to_schedparam(const struct rtprio *rtp, int *policy, + struct sched_param *param) __hidden; +int _schedparam_to_rtp(int policy, const struct sched_param *param, + struct rtprio *rtp) __hidden; +void _thread_bp_create(void); +void _thread_bp_death(void); +int _sched_yield(void); + +void _pthread_cleanup_push(void (*)(void *), void *); +void _pthread_cleanup_pop(int); +void _pthread_exit_mask(void *status, sigset_t *mask) __dead2 __hidden; +void _pthread_cancel_enter(int maycancel); +void _pthread_cancel_leave(int maycancel); + +/* #include <fcntl.h> */ +#ifdef _SYS_FCNTL_H_ +int __sys_fcntl(int, int, ...); +int __sys_open(const char *, int, ...); +int __sys_openat(int, const char *, int, ...); +#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(const ucontext_t *); +int __sys_sigaltstack(const struct sigaltstack *, struct sigaltstack *); +int __sys_sigwait(const sigset_t *, int *); +int __sys_sigtimedwait(const sigset_t *, siginfo_t *, + const struct timespec *); +int __sys_sigwaitinfo(const sigset_t *set, siginfo_t *info); +#endif + +/* #include <time.h> */ +#ifdef _TIME_H_ +int __sys_nanosleep(const struct timespec *, struct timespec *); +#endif + +/* #include <sys/ucontext.h> */ +#ifdef _SYS_UCONTEXT_H_ +int __sys_setcontext(const ucontext_t *ucp); +int __sys_swapcontext(ucontext_t *oucp, const ucontext_t *ucp); +#endif + +/* #include <unistd.h> */ +#ifdef _UNISTD_H_ +int __sys_close(int); +int __sys_fork(void); +pid_t __sys_getpid(void); +ssize_t __sys_read(int, void *, size_t); +ssize_t __sys_write(int, const void *, size_t); +void __sys_exit(int); +#endif + +static inline int +_thr_isthreaded(void) +{ + return (__isthreaded != 0); +} + +static inline int +_thr_is_inited(void) +{ + return (_thr_initial != NULL); +} + +static inline void +_thr_check_init(void) +{ + if (_thr_initial == NULL) + _libpthread_init(NULL); +} + +struct wake_addr *_thr_alloc_wake_addr(void); +void _thr_release_wake_addr(struct wake_addr *); +int _thr_sleep(struct pthread *, int, const struct timespec *); + +void _thr_wake_addr_init(void) __hidden; + +static inline void +_thr_clear_wake(struct pthread *td) +{ + td->wake_addr->value = 0; +} + +static inline int +_thr_is_woken(struct pthread *td) +{ + return td->wake_addr->value != 0; +} + +static inline void +_thr_set_wake(unsigned int *waddr) +{ + *waddr = 1; + _thr_umtx_wake(waddr, INT_MAX, 0); +} + +void _thr_wake_all(unsigned int *waddrs[], int) __hidden; + +static inline struct pthread * +_sleepq_first(struct sleepqueue *sq) +{ + return TAILQ_FIRST(&sq->sq_blocked); +} + +void _sleepq_init(void) __hidden; +struct sleepqueue *_sleepq_alloc(void) __hidden; +void _sleepq_free(struct sleepqueue *) __hidden; +void _sleepq_lock(void *) __hidden; +void _sleepq_unlock(void *) __hidden; +struct sleepqueue *_sleepq_lookup(void *) __hidden; +void _sleepq_add(void *, struct pthread *) __hidden; +int _sleepq_remove(struct sleepqueue *, struct pthread *) __hidden; +void _sleepq_drop(struct sleepqueue *, + void (*cb)(struct pthread *, void *arg), void *) __hidden; + +struct dl_phdr_info; +void __pthread_cxa_finalize(struct dl_phdr_info *phdr_info); +void _thr_tsd_unload(struct dl_phdr_info *phdr_info) __hidden; +void _thr_sigact_unload(struct dl_phdr_info *phdr_info) __hidden; +void _thr_stack_fix_protection(struct pthread *thrd); + +__END_DECLS + +#endif /* !_THR_PRIVATE_H */ diff --git a/lib/libthr/thread/thr_pspinlock.c b/lib/libthr/thread/thr_pspinlock.c new file mode 100644 index 0000000..9e1f96e --- /dev/null +++ b/lib/libthr/thread/thr_pspinlock.c @@ -0,0 +1,138 @@ +/*- + * 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 "namespace.h" +#include <errno.h> +#include <stdlib.h> +#include <pthread.h> +#include "un-namespace.h" + +#include "thr_private.h" + +#define SPIN_COUNT 100000 + +__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 { + _thr_umutex_init(&lck->s_lock); + *lock = lck; + ret = 0; + } + + return (ret); +} + +int +_pthread_spin_destroy(pthread_spinlock_t *lock) +{ + int ret; + + if (lock == NULL || *lock == NULL) + ret = EINVAL; + else { + free(*lock); + *lock = NULL; + ret = 0; + } + + return (ret); +} + +int +_pthread_spin_trylock(pthread_spinlock_t *lock) +{ + struct pthread *curthread = _get_curthread(); + struct pthread_spinlock *lck; + int ret; + + if (lock == NULL || (lck = *lock) == NULL) + ret = EINVAL; + else + ret = THR_UMUTEX_TRYLOCK(curthread, &lck->s_lock); + return (ret); +} + +int +_pthread_spin_lock(pthread_spinlock_t *lock) +{ + struct pthread *curthread = _get_curthread(); + struct pthread_spinlock *lck; + int ret, count; + + if (lock == NULL || (lck = *lock) == NULL) + ret = EINVAL; + else { + count = SPIN_COUNT; + while ((ret = THR_UMUTEX_TRYLOCK(curthread, &lck->s_lock)) != 0) { + while (lck->s_lock.m_owner) { + if (!_thr_is_smp) { + _pthread_yield(); + } else { + CPU_SPINWAIT; + + if (--count <= 0) { + count = SPIN_COUNT; + _pthread_yield(); + } + } + } + } + ret = 0; + } + + return (ret); +} + +int +_pthread_spin_unlock(pthread_spinlock_t *lock) +{ + struct pthread *curthread = _get_curthread(); + struct pthread_spinlock *lck; + int ret; + + if (lock == NULL || (lck = *lock) == NULL) + ret = EINVAL; + else { + ret = THR_UMUTEX_UNLOCK(curthread, &lck->s_lock); + } + return (ret); +} diff --git a/lib/libthr/thread/thr_resume_np.c b/lib/libthr/thread/thr_resume_np.c new file mode 100644 index 0000000..a3066bc --- /dev/null +++ b/lib/libthr/thread/thr_resume_np.c @@ -0,0 +1,89 @@ +/* + * 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. 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 <pthread_np.h> +#include "un-namespace.h" + +#include "thr_private.h" + +__weak_reference(_pthread_resume_np, pthread_resume_np); +__weak_reference(_pthread_resume_all_np, pthread_resume_all_np); + +static void resume_common(struct pthread *thread); + +/* Resume a thread: */ +int +_pthread_resume_np(pthread_t thread) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + /* Add a reference to the thread: */ + if ((ret = _thr_find_thread(curthread, thread, /*include dead*/0)) == 0) { + /* Lock the threads scheduling queue: */ + resume_common(thread); + THR_THREAD_UNLOCK(curthread, thread); + } + return (ret); +} + +void +_pthread_resume_all_np(void) +{ + struct pthread *curthread = _get_curthread(); + struct pthread *thread; + + /* Take the thread list lock: */ + THREAD_LIST_RDLOCK(curthread); + + TAILQ_FOREACH(thread, &_thread_list, tle) { + if (thread != curthread) { + THR_THREAD_LOCK(curthread, thread); + resume_common(thread); + THR_THREAD_UNLOCK(curthread, thread); + } + } + + /* Release the thread list lock: */ + THREAD_LIST_UNLOCK(curthread); +} + +static void +resume_common(struct pthread *thread) +{ + /* Clear the suspend flag: */ + thread->flags &= ~THR_FLAGS_NEED_SUSPEND; + thread->cycle++; + _thr_umtx_wake(&thread->cycle, 1, 0); +} diff --git a/lib/libthr/thread/thr_rtld.c b/lib/libthr/thread/thr_rtld.c new file mode 100644 index 0000000..d9dd94d --- /dev/null +++ b/lib/libthr/thread/thr_rtld.c @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2006, 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$ + * + */ + + /* + * A lockless rwlock for rtld. + */ +#include <sys/cdefs.h> +#include <sys/mman.h> +#include <link.h> +#include <stdlib.h> +#include <string.h> + +#include "rtld_lock.h" +#include "thr_private.h" + +#undef errno +extern int errno; + +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 *); + +struct rtld_lock { + struct urwlock lock; + char _pad[CACHE_LINE_SIZE - sizeof(struct urwlock)]; +}; + +static struct rtld_lock lock_place[MAX_RTLD_LOCKS] __aligned(CACHE_LINE_SIZE); +static int busy_places; + +static void * +_thr_rtld_lock_create(void) +{ + int locki; + struct rtld_lock *l; + static const char fail[] = "_thr_rtld_lock_create failed\n"; + + for (locki = 0; locki < MAX_RTLD_LOCKS; locki++) { + if ((busy_places & (1 << locki)) == 0) + break; + } + if (locki == MAX_RTLD_LOCKS) { + write(2, fail, sizeof(fail) - 1); + return (NULL); + } + busy_places |= (1 << locki); + + l = &lock_place[locki]; + l->lock.rw_flags = URWLOCK_PREFER_READER; + return (l); +} + +static void +_thr_rtld_lock_destroy(void *lock) +{ + int locki; + size_t i; + + locki = (struct rtld_lock *)lock - &lock_place[0]; + for (i = 0; i < sizeof(struct rtld_lock); ++i) + ((char *)lock)[i] = 0; + busy_places &= ~(1 << locki); +} + +#define SAVE_ERRNO() { \ + if (curthread != _thr_initial) \ + errsave = curthread->error; \ + else \ + errsave = errno; \ +} + +#define RESTORE_ERRNO() { \ + if (curthread != _thr_initial) \ + curthread->error = errsave; \ + else \ + errno = errsave; \ +} + +static void +_thr_rtld_rlock_acquire(void *lock) +{ + struct pthread *curthread; + struct rtld_lock *l; + int errsave; + + curthread = _get_curthread(); + SAVE_ERRNO(); + l = (struct rtld_lock *)lock; + + THR_CRITICAL_ENTER(curthread); + while (_thr_rwlock_rdlock(&l->lock, 0, NULL) != 0) + ; + curthread->rdlock_count++; + RESTORE_ERRNO(); +} + +static void +_thr_rtld_wlock_acquire(void *lock) +{ + struct pthread *curthread; + struct rtld_lock *l; + int errsave; + + curthread = _get_curthread(); + SAVE_ERRNO(); + l = (struct rtld_lock *)lock; + + THR_CRITICAL_ENTER(curthread); + while (_thr_rwlock_wrlock(&l->lock, NULL) != 0) + ; + RESTORE_ERRNO(); +} + +static void +_thr_rtld_lock_release(void *lock) +{ + struct pthread *curthread; + struct rtld_lock *l; + int32_t state; + int errsave; + + curthread = _get_curthread(); + SAVE_ERRNO(); + l = (struct rtld_lock *)lock; + + state = l->lock.rw_state; + if (_thr_rwlock_unlock(&l->lock) == 0) { + if ((state & URWLOCK_WRITE_OWNER) == 0) + curthread->rdlock_count--; + THR_CRITICAL_LEAVE(curthread); + } + RESTORE_ERRNO(); +} + +static int +_thr_rtld_set_flag(int mask __unused) +{ + /* + * The caller's code in rtld-elf is broken, it is not signal safe, + * just return zero to fool it. + */ + return (0); +} + +static int +_thr_rtld_clr_flag(int mask __unused) +{ + return (0); +} + +void +_thr_rtld_init(void) +{ + struct RtldLockInfo li; + struct pthread *curthread; + long dummy = -1; + + curthread = _get_curthread(); + + /* force to resolve _umtx_op PLT */ + _umtx_op_err((struct umtx *)&dummy, UMTX_OP_WAKE, 1, 0, 0); + + /* force to resolve errno() PLT */ + __error(); + + /* force to resolve memcpy PLT */ + memcpy(&dummy, &dummy, sizeof(dummy)); + + mprotect(NULL, 0, 0); + _rtld_get_stack_prot(); + + 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; + + /* mask signals, also force to resolve __sys_sigprocmask PLT */ + _thr_signal_block(curthread); + _rtld_thread_init(&li); + _thr_signal_unblock(curthread); +} + +void +_thr_rtld_fini(void) +{ + struct pthread *curthread; + + curthread = _get_curthread(); + _thr_signal_block(curthread); + _rtld_thread_init(NULL); + _thr_signal_unblock(curthread); +} diff --git a/lib/libthr/thread/thr_rwlock.c b/lib/libthr/thread/thr_rwlock.c new file mode 100644 index 0000000..397663e --- /dev/null +++ b/lib/libthr/thread/thr_rwlock.c @@ -0,0 +1,324 @@ +/*- + * 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" + +__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); + +#define CHECK_AND_INIT_RWLOCK \ + if (__predict_false((prwlock = (*rwlock)) <= THR_RWLOCK_DESTROYED)) { \ + if (prwlock == THR_RWLOCK_INITIALIZER) { \ + int ret; \ + ret = init_static(_get_curthread(), rwlock); \ + if (ret) \ + return (ret); \ + } else if (prwlock == THR_RWLOCK_DESTROYED) { \ + return (EINVAL); \ + } \ + prwlock = *rwlock; \ + } + +/* + * Prototypes + */ + +static int +rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr __unused) +{ + pthread_rwlock_t prwlock; + + prwlock = (pthread_rwlock_t)calloc(1, sizeof(struct pthread_rwlock)); + if (prwlock == NULL) + return (ENOMEM); + *rwlock = prwlock; + return (0); +} + +int +_pthread_rwlock_destroy (pthread_rwlock_t *rwlock) +{ + pthread_rwlock_t prwlock; + int ret; + + prwlock = *rwlock; + if (prwlock == THR_RWLOCK_INITIALIZER) + ret = 0; + else if (prwlock == THR_RWLOCK_DESTROYED) + ret = EINVAL; + else { + *rwlock = THR_RWLOCK_DESTROYED; + + free(prwlock); + ret = 0; + } + return (ret); +} + +static int +init_static(struct pthread *thread, pthread_rwlock_t *rwlock) +{ + int ret; + + THR_LOCK_ACQUIRE(thread, &_rwlock_static_lock); + + if (*rwlock == THR_RWLOCK_INITIALIZER) + ret = rwlock_init(rwlock, NULL); + else + ret = 0; + + THR_LOCK_RELEASE(thread, &_rwlock_static_lock); + + return (ret); +} + +int +_pthread_rwlock_init (pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr) +{ + *rwlock = NULL; + return (rwlock_init(rwlock, attr)); +} + +static int +rwlock_rdlock_common(pthread_rwlock_t *rwlock, const struct timespec *abstime) +{ + struct pthread *curthread = _get_curthread(); + pthread_rwlock_t prwlock; + int flags; + int ret; + + CHECK_AND_INIT_RWLOCK + + if (curthread->rdlock_count) { + /* + * 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 ;-) + */ + flags = URWLOCK_PREFER_READER; + } else { + flags = 0; + } + + /* + * POSIX said the validity of the abstimeout parameter need + * not be checked if the lock can be immediately acquired. + */ + ret = _thr_rwlock_tryrdlock(&prwlock->lock, flags); + if (ret == 0) { + curthread->rdlock_count++; + return (ret); + } + + if (__predict_false(abstime && + (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0))) + return (EINVAL); + + for (;;) { + /* goto kernel and lock it */ + ret = __thr_rwlock_rdlock(&prwlock->lock, flags, abstime); + if (ret != EINTR) + break; + + /* if interrupted, try to lock it in userland again. */ + if (_thr_rwlock_tryrdlock(&prwlock->lock, flags) == 0) { + ret = 0; + break; + } + } + if (ret == 0) + curthread->rdlock_count++; + return (ret); +} + +int +_pthread_rwlock_rdlock (pthread_rwlock_t *rwlock) +{ + return (rwlock_rdlock_common(rwlock, NULL)); +} + +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 = _get_curthread(); + pthread_rwlock_t prwlock; + int flags; + int ret; + + CHECK_AND_INIT_RWLOCK + + if (curthread->rdlock_count) { + /* + * 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 ;-) + */ + flags = URWLOCK_PREFER_READER; + } else { + flags = 0; + } + + ret = _thr_rwlock_tryrdlock(&prwlock->lock, flags); + if (ret == 0) + curthread->rdlock_count++; + return (ret); +} + +int +_pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock) +{ + struct pthread *curthread = _get_curthread(); + pthread_rwlock_t prwlock; + int ret; + + CHECK_AND_INIT_RWLOCK + + ret = _thr_rwlock_trywrlock(&prwlock->lock); + if (ret == 0) + prwlock->owner = curthread; + return (ret); +} + +static int +rwlock_wrlock_common (pthread_rwlock_t *rwlock, const struct timespec *abstime) +{ + struct pthread *curthread = _get_curthread(); + pthread_rwlock_t prwlock; + int ret; + + CHECK_AND_INIT_RWLOCK + + /* + * POSIX said the validity of the abstimeout parameter need + * not be checked if the lock can be immediately acquired. + */ + ret = _thr_rwlock_trywrlock(&prwlock->lock); + if (ret == 0) { + prwlock->owner = curthread; + return (ret); + } + + if (__predict_false(abstime && + (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0))) + return (EINVAL); + + for (;;) { + /* goto kernel and lock it */ + ret = __thr_rwlock_wrlock(&prwlock->lock, abstime); + if (ret == 0) { + prwlock->owner = curthread; + break; + } + + if (ret != EINTR) + break; + + /* if interrupted, try to lock it in userland again. */ + if (_thr_rwlock_trywrlock(&prwlock->lock) == 0) { + ret = 0; + prwlock->owner = curthread; + break; + } + } + return (ret); +} + +int +_pthread_rwlock_wrlock (pthread_rwlock_t *rwlock) +{ + return (rwlock_wrlock_common (rwlock, NULL)); +} + +int +_pthread_rwlock_timedwrlock (pthread_rwlock_t *rwlock, + const struct timespec *abstime) +{ + return (rwlock_wrlock_common (rwlock, abstime)); +} + +int +_pthread_rwlock_unlock (pthread_rwlock_t *rwlock) +{ + struct pthread *curthread = _get_curthread(); + pthread_rwlock_t prwlock; + int ret; + int32_t state; + + prwlock = *rwlock; + + if (__predict_false(prwlock <= THR_RWLOCK_DESTROYED)) + return (EINVAL); + + state = prwlock->lock.rw_state; + if (state & URWLOCK_WRITE_OWNER) { + if (__predict_false(prwlock->owner != curthread)) + return (EPERM); + prwlock->owner = NULL; + } + + ret = _thr_rwlock_unlock(&prwlock->lock); + if (ret == 0 && (state & URWLOCK_WRITE_OWNER) == 0) + curthread->rdlock_count--; + + return (ret); +} diff --git a/lib/libthr/thread/thr_rwlockattr.c b/lib/libthr/thread/thr_rwlockattr.c new file mode 100644 index 0000000..73ccdc9 --- /dev/null +++ b/lib/libthr/thread/thr_rwlockattr.c @@ -0,0 +1,99 @@ +/*- + * 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 "namespace.h" +#include <errno.h> +#include <stdlib.h> +#include <pthread.h> +#include "un-namespace.h" + +#include "thr_private.h" + +__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/libthr/thread/thr_self.c b/lib/libthr/thread/thr_self.c new file mode 100644 index 0000000..72cda7e --- /dev/null +++ b/lib/libthr/thread/thr_self.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. 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" + +__weak_reference(_pthread_self, pthread_self); + +pthread_t +_pthread_self(void) +{ + _thr_check_init(); + + /* Return the running thread pointer: */ + return (_get_curthread()); +} diff --git a/lib/libthr/thread/thr_sem.c b/lib/libthr/thread/thr_sem.c new file mode 100644 index 0000000..f20bde3 --- /dev/null +++ b/lib/libthr/thread/thr_sem.c @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2005 David Xu <davidxu@freebsd.org>. + * 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 <stdlib.h> +#include <time.h> +#include <_semaphore.h> +#include "un-namespace.h" + +#include "thr_private.h" + +FB10_COMPAT(_sem_init_compat, sem_init); +FB10_COMPAT(_sem_destroy_compat, sem_destroy); +FB10_COMPAT(_sem_getvalue_compat, sem_getvalue); +FB10_COMPAT(_sem_trywait_compat, sem_trywait); +FB10_COMPAT(_sem_wait_compat, sem_wait); +FB10_COMPAT(_sem_timedwait_compat, sem_timedwait); +FB10_COMPAT(_sem_post_compat, sem_post); + +typedef struct sem *sem_t; + +extern int _libc_sem_init_compat(sem_t *sem, int pshared, unsigned int value); +extern int _libc_sem_destroy_compat(sem_t *sem); +extern int _libc_sem_getvalue_compat(sem_t * __restrict sem, int * __restrict sval); +extern int _libc_sem_trywait_compat(sem_t *sem); +extern int _libc_sem_wait_compat(sem_t *sem); +extern int _libc_sem_timedwait_compat(sem_t * __restrict sem, + const struct timespec * __restrict abstime); +extern int _libc_sem_post_compat(sem_t *sem); + +int _sem_init_compat(sem_t *sem, int pshared, unsigned int value); +int _sem_destroy_compat(sem_t *sem); +int _sem_getvalue_compat(sem_t * __restrict sem, int * __restrict sval); +int _sem_trywait_compat(sem_t *sem); +int _sem_wait_compat(sem_t *sem); +int _sem_timedwait_compat(sem_t * __restrict sem, + const struct timespec * __restrict abstime); +int _sem_post_compat(sem_t *sem); + +int +_sem_init_compat(sem_t *sem, int pshared, unsigned int value) +{ + return _libc_sem_init_compat(sem, pshared, value); +} + +int +_sem_destroy_compat(sem_t *sem) +{ + return _libc_sem_destroy_compat(sem); +} + +int +_sem_getvalue_compat(sem_t * __restrict sem, int * __restrict sval) +{ + return _libc_sem_getvalue_compat(sem, sval); +} + +int +_sem_trywait_compat(sem_t *sem) +{ + return _libc_sem_trywait_compat(sem); +} + +int +_sem_wait_compat(sem_t *sem) +{ + return _libc_sem_wait_compat(sem); +} + +int +_sem_timedwait_compat(sem_t * __restrict sem, + const struct timespec * __restrict abstime) +{ + return _libc_sem_timedwait_compat(sem, abstime); +} + +int +_sem_post_compat(sem_t *sem) +{ + return _libc_sem_post_compat(sem); +} diff --git a/lib/libthr/thread/thr_setprio.c b/lib/libthr/thread/thr_setprio.c new file mode 100644 index 0000000..b1b2352 --- /dev/null +++ b/lib/libthr/thread/thr_setprio.c @@ -0,0 +1,82 @@ +/* + * 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. 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" + +__weak_reference(_pthread_setprio, pthread_setprio); + +int +_pthread_setprio(pthread_t pthread, int prio) +{ + struct pthread *curthread = _get_curthread(); + struct sched_param param; + int ret; + + param.sched_priority = prio; + if (pthread == curthread) { + THR_LOCK(curthread); + if (curthread->attr.sched_policy == SCHED_OTHER || + curthread->attr.prio == prio) { + curthread->attr.prio = prio; + ret = 0; + } else { + ret = _thr_setscheduler(curthread->tid, + curthread->attr.sched_policy, ¶m); + if (ret == -1) + ret = errno; + else + curthread->attr.prio = prio; + } + THR_UNLOCK(curthread); + } else if ((ret = _thr_ref_add(curthread, pthread, /*include dead*/0)) + == 0) { + THR_THREAD_LOCK(curthread, pthread); + if (pthread->attr.sched_policy == SCHED_OTHER || + pthread->attr.prio == prio) { + pthread->attr.prio = prio; + ret = 0; + } else { + ret = _thr_setscheduler(pthread->tid, + curthread->attr.sched_policy, ¶m); + if (ret == -1) + ret = errno; + else + pthread->attr.prio = prio; + } + THR_THREAD_UNLOCK(curthread, pthread); + _thr_ref_delete(curthread, pthread); + } + return (ret); +} diff --git a/lib/libthr/thread/thr_setschedparam.c b/lib/libthr/thread/thr_setschedparam.c new file mode 100644 index 0000000..59d62dc --- /dev/null +++ b/lib/libthr/thread/thr_setschedparam.c @@ -0,0 +1,94 @@ +/* + * 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 "namespace.h" +#include <sys/param.h> +#include <errno.h> +#include <pthread.h> +#include "un-namespace.h" + +#include "thr_private.h" + +__weak_reference(_pthread_setschedparam, pthread_setschedparam); + +/* + * Set a thread's scheduling parameters, this should be done + * in kernel, doing it in userland is no-op. + */ +int +_pthread_setschedparam(pthread_t pthread, int policy, + const struct sched_param *param) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + if (pthread == curthread) { + THR_LOCK(curthread); + if (curthread->attr.sched_policy == policy && + (policy == SCHED_OTHER || + curthread->attr.prio == param->sched_priority)) { + pthread->attr.prio = param->sched_priority; + THR_UNLOCK(curthread); + return (0); + } + ret = _thr_setscheduler(curthread->tid, policy, param); + if (ret == -1) + ret = errno; + else { + curthread->attr.sched_policy = policy; + curthread->attr.prio = param->sched_priority; + } + THR_UNLOCK(curthread); + } else if ((ret = _thr_ref_add(curthread, pthread, /*include dead*/0)) + == 0) { + THR_THREAD_LOCK(curthread, pthread); + if (pthread->attr.sched_policy == policy && + (policy == SCHED_OTHER || + pthread->attr.prio == param->sched_priority)) { + pthread->attr.prio = param->sched_priority; + THR_THREAD_UNLOCK(curthread, pthread); + return (0); + } + ret = _thr_setscheduler(pthread->tid, policy, param); + if (ret == -1) + ret = errno; + else { + pthread->attr.sched_policy = policy; + pthread->attr.prio = param->sched_priority; + } + THR_THREAD_UNLOCK(curthread, pthread); + _thr_ref_delete(curthread, pthread); + } + return (ret); +} diff --git a/lib/libthr/thread/thr_sig.c b/lib/libthr/thread/thr_sig.c new file mode 100644 index 0000000..3dee8b7 --- /dev/null +++ b/lib/libthr/thread/thr_sig.c @@ -0,0 +1,748 @@ +/* + * 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 "namespace.h" +#include <sys/param.h> +#include <sys/types.h> +#include <sys/signalvar.h> +#include <signal.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <pthread.h> +#include "un-namespace.h" +#include "libc_private.h" + +#include "thr_private.h" + +/* #define DEBUG_SIGNAL */ +#ifdef DEBUG_SIGNAL +#define DBG_MSG stdout_debug +#else +#define DBG_MSG(x...) +#endif + +struct usigaction { + struct sigaction sigact; + struct urwlock lock; +}; + +static struct usigaction _thr_sigact[_SIG_MAXSIG]; + +static void thr_sighandler(int, siginfo_t *, void *); +static void handle_signal(struct sigaction *, int, siginfo_t *, ucontext_t *); +static void check_deferred_signal(struct pthread *); +static void check_suspend(struct pthread *); +static void check_cancel(struct pthread *curthread, ucontext_t *ucp); + +int ___pause(void); +int _raise(int); +int __sigtimedwait(const sigset_t *set, siginfo_t *info, + const struct timespec * timeout); +int _sigtimedwait(const sigset_t *set, siginfo_t *info, + const struct timespec * timeout); +int __sigwaitinfo(const sigset_t *set, siginfo_t *info); +int _sigwaitinfo(const sigset_t *set, siginfo_t *info); +int ___sigwait(const sigset_t *set, int *sig); +int _sigwait(const sigset_t *set, int *sig); +int __sigsuspend(const sigset_t *sigmask); +int _sigaction(int, const struct sigaction *, struct sigaction *); +int _setcontext(const ucontext_t *); +int _swapcontext(ucontext_t *, const ucontext_t *); + +static const sigset_t _thr_deferset={{ + 0xffffffff & ~(_SIG_BIT(SIGBUS)|_SIG_BIT(SIGILL)|_SIG_BIT(SIGFPE)| + _SIG_BIT(SIGSEGV)|_SIG_BIT(SIGTRAP)|_SIG_BIT(SIGSYS)), + 0xffffffff, + 0xffffffff, + 0xffffffff}}; + +static const sigset_t _thr_maskset={{ + 0xffffffff, + 0xffffffff, + 0xffffffff, + 0xffffffff}}; + +void +_thr_signal_block(struct pthread *curthread) +{ + + if (curthread->sigblock > 0) { + curthread->sigblock++; + return; + } + __sys_sigprocmask(SIG_BLOCK, &_thr_maskset, &curthread->sigmask); + curthread->sigblock++; +} + +void +_thr_signal_unblock(struct pthread *curthread) +{ + if (--curthread->sigblock == 0) + __sys_sigprocmask(SIG_SETMASK, &curthread->sigmask, NULL); +} + +int +_thr_send_sig(struct pthread *thread, int sig) +{ + return thr_kill(thread->tid, sig); +} + +static inline void +remove_thr_signals(sigset_t *set) +{ + if (SIGISMEMBER(*set, SIGCANCEL)) + SIGDELSET(*set, SIGCANCEL); +} + +static const sigset_t * +thr_remove_thr_signals(const sigset_t *set, sigset_t *newset) +{ + *newset = *set; + remove_thr_signals(newset); + return (newset); +} + +static void +sigcancel_handler(int sig __unused, + siginfo_t *info __unused, ucontext_t *ucp) +{ + struct pthread *curthread = _get_curthread(); + int err; + + if (THR_IN_CRITICAL(curthread)) + return; + err = errno; + check_suspend(curthread); + check_cancel(curthread, ucp); + errno = err; +} + +typedef void (*ohandler)(int sig, int code, + struct sigcontext *scp, char *addr, __sighandler_t *catcher); + +/* + * The signal handler wrapper is entered with all signal masked. + */ +static void +thr_sighandler(int sig, siginfo_t *info, void *_ucp) +{ + struct pthread *curthread = _get_curthread(); + ucontext_t *ucp = _ucp; + struct sigaction act; + int err; + + err = errno; + _thr_rwl_rdlock(&_thr_sigact[sig-1].lock); + act = _thr_sigact[sig-1].sigact; + _thr_rwl_unlock(&_thr_sigact[sig-1].lock); + errno = err; + + /* + * if a thread is in critical region, for example it holds low level locks, + * try to defer the signal processing, however if the signal is synchronous + * signal, it means a bad thing has happened, this is a programming error, + * resuming fault point can not help anything (normally causes deadloop), + * so here we let user code handle it immediately. + */ + if (THR_IN_CRITICAL(curthread) && SIGISMEMBER(_thr_deferset, sig)) { + memcpy(&curthread->deferred_sigact, &act, sizeof(struct sigaction)); + memcpy(&curthread->deferred_siginfo, info, sizeof(siginfo_t)); + curthread->deferred_sigmask = ucp->uc_sigmask; + /* mask all signals, we will restore it later. */ + ucp->uc_sigmask = _thr_deferset; + return; + } + + handle_signal(&act, sig, info, ucp); +} + +static void +handle_signal(struct sigaction *actp, int sig, siginfo_t *info, ucontext_t *ucp) +{ + struct pthread *curthread = _get_curthread(); + ucontext_t uc2; + __siginfohandler_t *sigfunc; + int cancel_point; + int cancel_async; + int cancel_enable; + int in_sigsuspend; + int err; + + /* add previous level mask */ + SIGSETOR(actp->sa_mask, ucp->uc_sigmask); + + /* add this signal's mask */ + if (!(actp->sa_flags & SA_NODEFER)) + SIGADDSET(actp->sa_mask, sig); + + in_sigsuspend = curthread->in_sigsuspend; + curthread->in_sigsuspend = 0; + + /* + * if thread is in deferred cancellation mode, disable cancellation + * in signal handler. + * if user signal handler calls a cancellation point function, e.g, + * it calls write() to write data to file, because write() is a + * cancellation point, the thread is immediately cancelled if + * cancellation is pending, to avoid this problem while thread is in + * deferring mode, cancellation is temporarily disabled. + */ + cancel_point = curthread->cancel_point; + cancel_async = curthread->cancel_async; + cancel_enable = curthread->cancel_enable; + curthread->cancel_point = 0; + if (!cancel_async) + curthread->cancel_enable = 0; + + /* restore correct mask before calling user handler */ + __sys_sigprocmask(SIG_SETMASK, &actp->sa_mask, NULL); + + sigfunc = actp->sa_sigaction; + + /* + * We have already reset cancellation point flags, so if user's code + * longjmp()s out of its signal handler, wish its jmpbuf was set + * outside of a cancellation point, in most cases, this would be + * true. however, ther is no way to save cancel_enable in jmpbuf, + * so after setjmps() returns once more, the user code may need to + * re-set cancel_enable flag by calling pthread_setcancelstate(). + */ + if ((actp->sa_flags & SA_SIGINFO) != 0) + (*(sigfunc))(sig, info, ucp); + else { + ((ohandler)(*sigfunc))( + sig, info->si_code, (struct sigcontext *)ucp, + info->si_addr, (__sighandler_t *)sigfunc); + } + err = errno; + + curthread->in_sigsuspend = in_sigsuspend; + curthread->cancel_point = cancel_point; + curthread->cancel_enable = cancel_enable; + + memcpy(&uc2, ucp, sizeof(uc2)); + SIGDELSET(uc2.uc_sigmask, SIGCANCEL); + + /* reschedule cancellation */ + check_cancel(curthread, &uc2); + errno = err; + __sys_sigreturn(&uc2); +} + +void +_thr_ast(struct pthread *curthread) +{ + + if (!THR_IN_CRITICAL(curthread)) { + check_deferred_signal(curthread); + check_suspend(curthread); + check_cancel(curthread, NULL); + } +} + +/* reschedule cancellation */ +static void +check_cancel(struct pthread *curthread, ucontext_t *ucp) +{ + + if (__predict_true(!curthread->cancel_pending || + !curthread->cancel_enable || curthread->no_cancel)) + return; + + /* + * Otherwise, we are in defer mode, and we are at + * cancel point, tell kernel to not block the current + * thread on next cancelable system call. + * + * There are three cases we should call thr_wake() to + * turn on TDP_WAKEUP or send SIGCANCEL in kernel: + * 1) we are going to call a cancelable system call, + * non-zero cancel_point means we are already in + * cancelable state, next system call is cancelable. + * 2) because _thr_ast() may be called by + * THR_CRITICAL_LEAVE() which is used by rtld rwlock + * and any libthr internal locks, when rtld rwlock + * is used, it is mostly caused my an unresolved PLT. + * those routines may clear the TDP_WAKEUP flag by + * invoking some system calls, in those cases, we + * also should reenable the flag. + * 3) thread is in sigsuspend(), and the syscall insists + * on getting a signal before it agrees to return. + */ + if (curthread->cancel_point) { + if (curthread->in_sigsuspend && ucp) { + SIGADDSET(ucp->uc_sigmask, SIGCANCEL); + curthread->unblock_sigcancel = 1; + _thr_send_sig(curthread, SIGCANCEL); + } else + thr_wake(curthread->tid); + } else if (curthread->cancel_async) { + /* + * asynchronous cancellation mode, act upon + * immediately. + */ + _pthread_exit_mask(PTHREAD_CANCELED, + ucp? &ucp->uc_sigmask : NULL); + } +} + +static void +check_deferred_signal(struct pthread *curthread) +{ + ucontext_t *uc; + struct sigaction act; + siginfo_t info; + + if (__predict_true(curthread->deferred_siginfo.si_signo == 0)) + return; + +#if defined(__amd64__) || defined(__i386__) + uc = alloca(__getcontextx_size()); + __fillcontextx((char *)uc); +#else + ucontext_t ucv; + uc = &ucv; + getcontext(uc); +#endif + if (curthread->deferred_siginfo.si_signo != 0) { + act = curthread->deferred_sigact; + uc->uc_sigmask = curthread->deferred_sigmask; + memcpy(&info, &curthread->deferred_siginfo, sizeof(siginfo_t)); + /* remove signal */ + curthread->deferred_siginfo.si_signo = 0; + if (act.sa_flags & SA_RESETHAND) { + struct sigaction tact; + + tact = act; + tact.sa_handler = SIG_DFL; + _sigaction(info.si_signo, &tact, NULL); + } + handle_signal(&act, info.si_signo, &info, uc); + } +} + +static void +check_suspend(struct pthread *curthread) +{ + uint32_t cycle; + + if (__predict_true((curthread->flags & + (THR_FLAGS_NEED_SUSPEND | THR_FLAGS_SUSPENDED)) + != THR_FLAGS_NEED_SUSPEND)) + return; + + if (curthread->force_exit) + return; + + /* + * Blocks SIGCANCEL which other threads must send. + */ + _thr_signal_block(curthread); + + /* + * Increase critical_count, here we don't use THR_LOCK/UNLOCK + * because we are leaf code, we don't want to recursively call + * ourself. + */ + curthread->critical_count++; + THR_UMUTEX_LOCK(curthread, &(curthread)->lock); + while ((curthread->flags & (THR_FLAGS_NEED_SUSPEND | + THR_FLAGS_SUSPENDED)) == THR_FLAGS_NEED_SUSPEND) { + curthread->cycle++; + cycle = curthread->cycle; + + /* Wake the thread suspending us. */ + _thr_umtx_wake(&curthread->cycle, INT_MAX, 0); + + /* + * if we are from pthread_exit, we don't want to + * suspend, just go and die. + */ + if (curthread->state == PS_DEAD) + break; + curthread->flags |= THR_FLAGS_SUSPENDED; + THR_UMUTEX_UNLOCK(curthread, &(curthread)->lock); + _thr_umtx_wait_uint(&curthread->cycle, cycle, NULL, 0); + THR_UMUTEX_LOCK(curthread, &(curthread)->lock); + curthread->flags &= ~THR_FLAGS_SUSPENDED; + } + THR_UMUTEX_UNLOCK(curthread, &(curthread)->lock); + curthread->critical_count--; + + _thr_signal_unblock(curthread); +} + +void +_thr_signal_init(void) +{ + struct sigaction act; + + /* Install SIGCANCEL handler. */ + SIGFILLSET(act.sa_mask); + act.sa_flags = SA_SIGINFO; + act.sa_sigaction = (__siginfohandler_t *)&sigcancel_handler; + __sys_sigaction(SIGCANCEL, &act, NULL); + + /* Unblock SIGCANCEL */ + SIGEMPTYSET(act.sa_mask); + SIGADDSET(act.sa_mask, SIGCANCEL); + __sys_sigprocmask(SIG_UNBLOCK, &act.sa_mask, NULL); +} + +void +_thr_sigact_unload(struct dl_phdr_info *phdr_info) +{ +#if 0 + struct pthread *curthread = _get_curthread(); + struct urwlock *rwlp; + struct sigaction *actp; + struct sigaction kact; + void (*handler)(int); + int sig; + + _thr_signal_block(curthread); + for (sig = 1; sig <= _SIG_MAXSIG; sig++) { + actp = &_thr_sigact[sig-1].sigact; +retry: + handler = actp->sa_handler; + if (handler != SIG_DFL && handler != SIG_IGN && + __elf_phdr_match_addr(phdr_info, handler)) { + rwlp = &_thr_sigact[sig-1].lock; + _thr_rwl_wrlock(rwlp); + if (handler != actp->sa_handler) { + _thr_rwl_unlock(rwlp); + goto retry; + } + actp->sa_handler = SIG_DFL; + actp->sa_flags = SA_SIGINFO; + SIGEMPTYSET(actp->sa_mask); + if (__sys_sigaction(sig, NULL, &kact) == 0 && + kact.sa_handler != SIG_DFL && + kact.sa_handler != SIG_IGN) + __sys_sigaction(sig, actp, NULL); + _thr_rwl_unlock(rwlp); + } + } + _thr_signal_unblock(curthread); +#endif +} + +void +_thr_signal_prefork(void) +{ + int i; + + for (i = 1; i <= _SIG_MAXSIG; ++i) + _thr_rwl_rdlock(&_thr_sigact[i-1].lock); +} + +void +_thr_signal_postfork(void) +{ + int i; + + for (i = 1; i <= _SIG_MAXSIG; ++i) + _thr_rwl_unlock(&_thr_sigact[i-1].lock); +} + +void +_thr_signal_postfork_child(void) +{ + int i; + + for (i = 1; i <= _SIG_MAXSIG; ++i) + bzero(&_thr_sigact[i-1].lock, sizeof(struct urwlock)); +} + +void +_thr_signal_deinit(void) +{ +} + +__weak_reference(___pause, pause); + +int +___pause(void) +{ + sigset_t oset; + + if (_sigprocmask(SIG_BLOCK, NULL, &oset) == -1) + return (-1); + return (__sigsuspend(&oset)); +} + +__weak_reference(_raise, raise); + +int +_raise(int sig) +{ + return _thr_send_sig(_get_curthread(), sig); +} + +__weak_reference(_sigaction, sigaction); + +int +_sigaction(int sig, const struct sigaction * act, struct sigaction * oact) +{ + struct sigaction newact, oldact, oldact2; + sigset_t oldset; + int ret = 0, err = 0; + + if (!_SIG_VALID(sig) || sig == SIGCANCEL) { + errno = EINVAL; + return (-1); + } + + if (act) + newact = *act; + + __sys_sigprocmask(SIG_SETMASK, &_thr_maskset, &oldset); + _thr_rwl_wrlock(&_thr_sigact[sig-1].lock); + + if (act != NULL) { + oldact2 = _thr_sigact[sig-1].sigact; + + /* + * if a new sig handler is SIG_DFL or SIG_IGN, + * don't remove old handler from _thr_sigact[], + * so deferred signals still can use the handlers, + * multiple threads invoking sigaction itself is + * a race condition, so it is not a problem. + */ + if (newact.sa_handler != SIG_DFL && + newact.sa_handler != SIG_IGN) { + _thr_sigact[sig-1].sigact = newact; + remove_thr_signals( + &_thr_sigact[sig-1].sigact.sa_mask); + newact.sa_flags &= ~SA_NODEFER; + newact.sa_flags |= SA_SIGINFO; + newact.sa_sigaction = thr_sighandler; + newact.sa_mask = _thr_maskset; /* mask all signals */ + } + if ((ret = __sys_sigaction(sig, &newact, &oldact))) { + err = errno; + _thr_sigact[sig-1].sigact = oldact2; + } + } else if (oact != NULL) { + ret = __sys_sigaction(sig, NULL, &oldact); + err = errno; + } + + if (oldact.sa_handler != SIG_DFL && + oldact.sa_handler != SIG_IGN) { + if (act != NULL) + oldact = oldact2; + else if (oact != NULL) + oldact = _thr_sigact[sig-1].sigact; + } + + _thr_rwl_unlock(&_thr_sigact[sig-1].lock); + __sys_sigprocmask(SIG_SETMASK, &oldset, NULL); + + if (ret == 0) { + if (oact != NULL) + *oact = oldact; + } else { + errno = err; + } + return (ret); +} + +__weak_reference(_sigprocmask, sigprocmask); + +int +_sigprocmask(int how, const sigset_t *set, sigset_t *oset) +{ + const sigset_t *p = set; + sigset_t newset; + + if (how != SIG_UNBLOCK) { + if (set != NULL) { + newset = *set; + SIGDELSET(newset, SIGCANCEL); + p = &newset; + } + } + return (__sys_sigprocmask(how, p, oset)); +} + +__weak_reference(_pthread_sigmask, pthread_sigmask); + +int +_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) +{ + if (_sigprocmask(how, set, oset)) + return (errno); + return (0); +} + +__weak_reference(__sigsuspend, sigsuspend); + +int +_sigsuspend(const sigset_t * set) +{ + sigset_t newset; + + return (__sys_sigsuspend(thr_remove_thr_signals(set, &newset))); +} + +int +__sigsuspend(const sigset_t * set) +{ + struct pthread *curthread; + sigset_t newset; + int ret, old; + + curthread = _get_curthread(); + + old = curthread->in_sigsuspend; + curthread->in_sigsuspend = 1; + _thr_cancel_enter(curthread); + ret = __sys_sigsuspend(thr_remove_thr_signals(set, &newset)); + _thr_cancel_leave(curthread, 1); + curthread->in_sigsuspend = old; + if (curthread->unblock_sigcancel) { + curthread->unblock_sigcancel = 0; + SIGEMPTYSET(newset); + SIGADDSET(newset, SIGCANCEL); + __sys_sigprocmask(SIG_UNBLOCK, &newset, NULL); + } + + return (ret); +} + +__weak_reference(___sigwait, sigwait); +__weak_reference(__sigtimedwait, sigtimedwait); +__weak_reference(__sigwaitinfo, sigwaitinfo); + +int +_sigtimedwait(const sigset_t *set, siginfo_t *info, + const struct timespec * timeout) +{ + sigset_t newset; + + return (__sys_sigtimedwait(thr_remove_thr_signals(set, &newset), info, + timeout)); +} + +/* + * Cancellation behavior: + * Thread may be canceled at start, if thread got signal, + * it is not canceled. + */ +int +__sigtimedwait(const sigset_t *set, siginfo_t *info, + const struct timespec * timeout) +{ + struct pthread *curthread = _get_curthread(); + sigset_t newset; + int ret; + + _thr_cancel_enter(curthread); + ret = __sys_sigtimedwait(thr_remove_thr_signals(set, &newset), info, + timeout); + _thr_cancel_leave(curthread, (ret == -1)); + return (ret); +} + +int +_sigwaitinfo(const sigset_t *set, siginfo_t *info) +{ + sigset_t newset; + + return (__sys_sigwaitinfo(thr_remove_thr_signals(set, &newset), info)); +} + +/* + * Cancellation behavior: + * Thread may be canceled at start, if thread got signal, + * it is not canceled. + */ +int +__sigwaitinfo(const sigset_t *set, siginfo_t *info) +{ + struct pthread *curthread = _get_curthread(); + sigset_t newset; + int ret; + + _thr_cancel_enter(curthread); + ret = __sys_sigwaitinfo(thr_remove_thr_signals(set, &newset), info); + _thr_cancel_leave(curthread, ret == -1); + return (ret); +} + +int +_sigwait(const sigset_t *set, int *sig) +{ + sigset_t newset; + + return (__sys_sigwait(thr_remove_thr_signals(set, &newset), sig)); +} + +/* + * Cancellation behavior: + * Thread may be canceled at start, if thread got signal, + * it is not canceled. + */ +int +___sigwait(const sigset_t *set, int *sig) +{ + struct pthread *curthread = _get_curthread(); + sigset_t newset; + int ret; + + do { + _thr_cancel_enter(curthread); + ret = __sys_sigwait(thr_remove_thr_signals(set, &newset), sig); + _thr_cancel_leave(curthread, (ret != 0)); + } while (ret == EINTR); + return (ret); +} + +__weak_reference(_setcontext, setcontext); +int +_setcontext(const ucontext_t *ucp) +{ + ucontext_t uc; + + (void) memcpy(&uc, ucp, sizeof(uc)); + remove_thr_signals(&uc.uc_sigmask); + return __sys_setcontext(&uc); +} + +__weak_reference(_swapcontext, swapcontext); +int +_swapcontext(ucontext_t *oucp, const ucontext_t *ucp) +{ + ucontext_t uc; + + (void) memcpy(&uc, ucp, sizeof(uc)); + remove_thr_signals(&uc.uc_sigmask); + return __sys_swapcontext(oucp, &uc); +} diff --git a/lib/libthr/thread/thr_single_np.c b/lib/libthr/thread/thr_single_np.c new file mode 100644 index 0000000..81bdf8c --- /dev/null +++ b/lib/libthr/thread/thr_single_np.c @@ -0,0 +1,49 @@ +/* + * 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. 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 <pthread_np.h> +#include "un-namespace.h" + +__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/libthr/thread/thr_sleepq.c b/lib/libthr/thread/thr_sleepq.c new file mode 100644 index 0000000..8549a87 --- /dev/null +++ b/lib/libthr/thread/thr_sleepq.c @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2010 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 <stdlib.h> +#include "thr_private.h" + +#define HASHSHIFT 9 +#define HASHSIZE (1 << HASHSHIFT) +#define SC_HASH(wchan) ((unsigned) \ + ((((uintptr_t)(wchan) >> 3) \ + ^ ((uintptr_t)(wchan) >> (HASHSHIFT + 3))) \ + & (HASHSIZE - 1))) +#define SC_LOOKUP(wc) &sc_table[SC_HASH(wc)] + +struct sleepqueue_chain { + struct umutex sc_lock; + LIST_HEAD(, sleepqueue) sc_queues; + int sc_type; +}; + +static struct sleepqueue_chain sc_table[HASHSIZE]; + +void +_sleepq_init(void) +{ + int i; + + for (i = 0; i < HASHSIZE; ++i) { + LIST_INIT(&sc_table[i].sc_queues); + _thr_umutex_init(&sc_table[i].sc_lock); + } +} + +struct sleepqueue * +_sleepq_alloc(void) +{ + struct sleepqueue *sq; + + sq = calloc(1, sizeof(struct sleepqueue)); + TAILQ_INIT(&sq->sq_blocked); + SLIST_INIT(&sq->sq_freeq); + return (sq); +} + +void +_sleepq_free(struct sleepqueue *sq) +{ + free(sq); +} + +void +_sleepq_lock(void *wchan) +{ + struct pthread *curthread = _get_curthread(); + struct sleepqueue_chain *sc; + + sc = SC_LOOKUP(wchan); + THR_LOCK_ACQUIRE_SPIN(curthread, &sc->sc_lock); +} + +void +_sleepq_unlock(void *wchan) +{ + struct sleepqueue_chain *sc; + struct pthread *curthread = _get_curthread(); + + sc = SC_LOOKUP(wchan); + THR_LOCK_RELEASE(curthread, &sc->sc_lock); +} + +struct sleepqueue * +_sleepq_lookup(void *wchan) +{ + struct sleepqueue_chain *sc; + struct sleepqueue *sq; + + sc = SC_LOOKUP(wchan); + LIST_FOREACH(sq, &sc->sc_queues, sq_hash) + if (sq->sq_wchan == wchan) + return (sq); + return (NULL); +} + +void +_sleepq_add(void *wchan, struct pthread *td) +{ + struct sleepqueue_chain *sc; + struct sleepqueue *sq; + + sq = _sleepq_lookup(wchan); + if (sq != NULL) { + SLIST_INSERT_HEAD(&sq->sq_freeq, td->sleepqueue, sq_flink); + } else { + sc = SC_LOOKUP(wchan); + sq = td->sleepqueue; + LIST_INSERT_HEAD(&sc->sc_queues, sq, sq_hash); + sq->sq_wchan = wchan; + /* sq->sq_type = type; */ + } + td->sleepqueue = NULL; + td->wchan = wchan; + TAILQ_INSERT_TAIL(&sq->sq_blocked, td, wle); +} + +int +_sleepq_remove(struct sleepqueue *sq, struct pthread *td) +{ + int rc; + + TAILQ_REMOVE(&sq->sq_blocked, td, wle); + if (TAILQ_EMPTY(&sq->sq_blocked)) { + LIST_REMOVE(sq, sq_hash); + td->sleepqueue = sq; + rc = 0; + } else { + td->sleepqueue = SLIST_FIRST(&sq->sq_freeq); + SLIST_REMOVE_HEAD(&sq->sq_freeq, sq_flink); + rc = 1; + } + td->wchan = NULL; + return (rc); +} + +void +_sleepq_drop(struct sleepqueue *sq, + void (*cb)(struct pthread *, void *arg), void *arg) +{ + struct pthread *td; + struct sleepqueue *sq2; + + td = TAILQ_FIRST(&sq->sq_blocked); + if (td == NULL) + return; + LIST_REMOVE(sq, sq_hash); + TAILQ_REMOVE(&sq->sq_blocked, td, wle); + if (cb != NULL) + cb(td, arg); + td->sleepqueue = sq; + td->wchan = NULL; + sq2 = SLIST_FIRST(&sq->sq_freeq); + TAILQ_FOREACH(td, &sq->sq_blocked, wle) { + if (cb != NULL) + cb(td, arg); + td->sleepqueue = sq2; + td->wchan = NULL; + sq2 = SLIST_NEXT(sq2, sq_flink); + } + TAILQ_INIT(&sq->sq_blocked); + SLIST_INIT(&sq->sq_freeq); +} diff --git a/lib/libthr/thread/thr_spec.c b/lib/libthr/thread/thr_spec.c new file mode 100644 index 0000000..86dc794 --- /dev/null +++ b/lib/libthr/thread/thr_spec.c @@ -0,0 +1,258 @@ +/* + * 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. 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 <signal.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <pthread.h> +#include "un-namespace.h" +#include "libc_private.h" + +#include "thr_private.h" + +/* Static variables: */ +struct pthread_key _thread_keytable[PTHREAD_KEYS_MAX]; + +__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; + int i; + + _thr_check_init(); + + curthread = _get_curthread(); + + /* 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 *); + const 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 = curthread->specific[key].data; + destructor = _thread_keytable[key].destructor; + } + curthread->specific[key].data = NULL; + curthread->specific_data_count--; + } + else if (curthread->specific[key].data != NULL) { + /* + * This can happen if the key is deleted via + * pthread_key_delete without first setting the value + * to NULL in all threads. POSIX says that the + * destructor is not invoked in this case. + */ + curthread->specific[key].data = NULL; + curthread->specific_data_count--; + } + + /* + * If there is a destructor, 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(__DECONST(void *, 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 *) + calloc(1, 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; + const 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 = 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 (__DECONST(void *, data)); +} + +void +_thr_tsd_unload(struct dl_phdr_info *phdr_info) +{ + struct pthread *curthread = _get_curthread(); + void (*destructor)(void *); + int key; + + THR_LOCK_ACQUIRE(curthread, &_keytable_lock); + for (key = 0; key < PTHREAD_KEYS_MAX; key++) { + if (_thread_keytable[key].allocated) { + destructor = _thread_keytable[key].destructor; + if (destructor != NULL) { + if (__elf_phdr_match_addr(phdr_info, destructor)) + _thread_keytable[key].destructor = NULL; + } + } + } + THR_LOCK_RELEASE(curthread, &_keytable_lock); +} diff --git a/lib/libthr/thread/thr_spinlock.c b/lib/libthr/thread/thr_spinlock.c new file mode 100644 index 0000000..ecc8067 --- /dev/null +++ b/lib/libthr/thread/thr_spinlock.c @@ -0,0 +1,130 @@ +/* + * 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. 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 <pthread.h> +#include <libc_private.h> +#include <spinlock.h> + +#include "thr_private.h" + +#define MAX_SPINLOCKS 72 + +/* + * These data structures are used to trace all spinlocks + * in libc. + */ +struct spinlock_extra { + spinlock_t *owner; + struct umutex lock; +}; + +static struct umutex spinlock_static_lock = DEFAULT_UMUTEX; +static struct spinlock_extra extra[MAX_SPINLOCKS]; +static int spinlock_count; +static int initialized; + +static void init_spinlock(spinlock_t *lck); + +/* + * 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; + THR_UMUTEX_UNLOCK(_get_curthread(), &_extra->lock); +} + +void +_spinlock(spinlock_t *lck) +{ + struct spinlock_extra *_extra; + + if (!__isthreaded) + PANIC("Spinlock called when not threaded."); + if (!initialized) + PANIC("Spinlocks not initialized."); + if (lck->fname == NULL) + init_spinlock(lck); + _extra = (struct spinlock_extra *)lck->fname; + THR_UMUTEX_LOCK(_get_curthread(), &_extra->lock); +} + +void +_spinlock_debug(spinlock_t *lck, char *fname __unused, int lineno __unused) +{ + _spinlock(lck); +} + +static void +init_spinlock(spinlock_t *lck) +{ + struct pthread *curthread = _get_curthread(); + + THR_UMUTEX_LOCK(curthread, &spinlock_static_lock); + if ((lck->fname == NULL) && (spinlock_count < MAX_SPINLOCKS)) { + lck->fname = (char *)&extra[spinlock_count]; + _thr_umutex_init(&extra[spinlock_count].lock); + extra[spinlock_count].owner = lck; + spinlock_count++; + } + THR_UMUTEX_UNLOCK(curthread, &spinlock_static_lock); + if (lck->fname == NULL) + PANIC("Warning: exceeded max spinlocks"); +} + +void +_thr_spinlock_init(void) +{ + int i; + + _thr_umutex_init(&spinlock_static_lock); + if (initialized != 0) { + /* + * called after fork() to reset state of libc spin locks, + * it is not quite right since libc may be in inconsistent + * state, resetting the locks to allow current thread to be + * able to hold them may not help things too much, but + * anyway, we do our best. + * it is better to do pthread_atfork in libc. + */ + for (i = 0; i < spinlock_count; i++) + _thr_umutex_init(&extra[i].lock); + } else { + initialized = 1; + } +} diff --git a/lib/libthr/thread/thr_stack.c b/lib/libthr/thread/thr_stack.c new file mode 100644 index 0000000..15a9c82 --- /dev/null +++ b/lib/libthr/thread/thr_stack.c @@ -0,0 +1,315 @@ +/* + * 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 <sys/resource.h> +#include <sys/sysctl.h> +#include <stdlib.h> +#include <pthread.h> +#include <link.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 - _thr_stack_default | top of 2nd thread stack + * | | + * | | + * | | + * | | + * | stack 2 | + * +-----------------------------------+ <-- start of 2nd thread stack + * | | + * | Red Zone | red zone for 1st thread + * | | + * +-----------------------------------+ + * | stack 1 - _thr_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 - _thr_stack_initial | top of main thread stack + * | | ^ + * | | | + * | | | + * | | | stack growth + * | | + * +-----------------------------------+ <-- start of main thread stack + * (USRSTACK) + * high memory + * + */ +static char *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; +} + +void +_thr_stack_fix_protection(struct pthread *thrd) +{ + + mprotect((char *)thrd->attr.stackaddr_attr + + round_up(thrd->attr.guardsize_attr), + round_up(thrd->attr.stacksize_attr), + _rtld_get_stack_prot()); +} + +static void +singlethread_map_stacks_exec(void) +{ + int mib[2]; + struct rlimit rlim; + u_long usrstack; + size_t len; + + mib[0] = CTL_KERN; + mib[1] = KERN_USRSTACK; + len = sizeof(usrstack); + if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), &usrstack, &len, NULL, 0) + == -1) + return; + if (getrlimit(RLIMIT_STACK, &rlim) == -1) + return; + mprotect((void *)(uintptr_t)(usrstack - rlim.rlim_cur), + rlim.rlim_cur, _rtld_get_stack_prot()); +} + +void __pthread_map_stacks_exec(void); +void +__pthread_map_stacks_exec(void) +{ + struct pthread *curthread, *thrd; + struct stack *st; + + if (!_thr_is_inited()) { + singlethread_map_stacks_exec(); + return; + } + curthread = _get_curthread(); + THREAD_LIST_RDLOCK(curthread); + LIST_FOREACH(st, &mstackq, qe) + mprotect((char *)st->stackaddr + st->guardsize, st->stacksize, + _rtld_get_stack_prot()); + LIST_FOREACH(st, &dstackq, qe) + mprotect((char *)st->stackaddr + st->guardsize, st->stacksize, + _rtld_get_stack_prot()); + TAILQ_FOREACH(thrd, &_thread_gc_list, gcle) + _thr_stack_fix_protection(thrd); + TAILQ_FOREACH(thrd, &_thread_list, tle) + _thr_stack_fix_protection(thrd); + THREAD_LIST_UNLOCK(curthread); +} + +int +_thr_stack_alloc(struct pthread_attr *attr) +{ + struct pthread *curthread = _get_curthread(); + struct stack *spare_stack; + 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. + */ + THREAD_LIST_WRLOCK(curthread); + /* + * 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. */ + THREAD_LIST_UNLOCK(curthread); + } + 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. */ + THREAD_LIST_UNLOCK(curthread); + + /* Map the stack and guard page together, and split guard + page from allocated space: */ + if ((stackaddr = mmap(stackaddr, stacksize+guardsize, + _rtld_get_stack_prot(), 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 = (struct stack *) + ((char *)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/libthr/thread/thr_suspend_np.c b/lib/libthr/thread/thr_suspend_np.c new file mode 100644 index 0000000..d5868e8 --- /dev/null +++ b/lib/libthr/thread/thr_suspend_np.c @@ -0,0 +1,144 @@ +/* + * 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. 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 <pthread_np.h> +#include "un-namespace.h" + +#include "thr_private.h" + +static int suspend_common(struct pthread *, struct pthread *, + int); + +__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_THREAD_LOCK(curthread, thread); + suspend_common(curthread, thread, 1); + /* Unlock the threads scheduling queue: */ + THR_THREAD_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; + int ret; + + THREAD_LIST_RDLOCK(curthread); + + TAILQ_FOREACH(thread, &_thread_list, tle) { + if (thread != curthread) { + THR_THREAD_LOCK(curthread, thread); + if (thread->state != PS_DEAD && + !(thread->flags & THR_FLAGS_SUSPENDED)) + thread->flags |= THR_FLAGS_NEED_SUSPEND; + THR_THREAD_UNLOCK(curthread, thread); + } + } + thr_kill(-1, SIGCANCEL); + +restart: + TAILQ_FOREACH(thread, &_thread_list, tle) { + if (thread != curthread) { + /* First try to suspend the thread without waiting */ + THR_THREAD_LOCK(curthread, thread); + ret = suspend_common(curthread, thread, 0); + if (ret == 0) { + THREAD_LIST_UNLOCK(curthread); + /* Can not suspend, try to wait */ + THR_REF_ADD(curthread, thread); + suspend_common(curthread, thread, 1); + THR_REF_DEL(curthread, thread); + _thr_try_gc(curthread, thread); + /* thread lock released */ + + THREAD_LIST_RDLOCK(curthread); + /* + * Because we were blocked, things may have + * been changed, we have to restart the + * process. + */ + goto restart; + } + THR_THREAD_UNLOCK(curthread, thread); + } + } + + THREAD_LIST_UNLOCK(curthread); +} + +static int +suspend_common(struct pthread *curthread, struct pthread *thread, + int waitok) +{ + long tmp; + + while (thread->state != PS_DEAD && + !(thread->flags & THR_FLAGS_SUSPENDED)) { + thread->flags |= THR_FLAGS_NEED_SUSPEND; + tmp = thread->cycle; + _thr_send_sig(thread, SIGCANCEL); + THR_THREAD_UNLOCK(curthread, thread); + if (waitok) { + _thr_umtx_wait_uint(&thread->cycle, tmp, NULL, 0); + THR_THREAD_LOCK(curthread, thread); + } else { + THR_THREAD_LOCK(curthread, thread); + return (0); + } + } + + return (1); +} diff --git a/lib/libthr/thread/thr_switch_np.c b/lib/libthr/thread/thr_switch_np.c new file mode 100644 index 0000000..f6ffb07 --- /dev/null +++ b/lib/libthr/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 "namespace.h" +#include <errno.h> +#include <pthread.h> +#include <pthread_np.h> +#include "un-namespace.h" + +#include "thr_private.h" + + +__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 __unused) +{ + return (ENOTSUP); +} + +int +_pthread_switch_delete_np(pthread_switch_routine_t routine __unused) +{ + return (ENOTSUP); +} diff --git a/lib/libthr/thread/thr_symbols.c b/lib/libthr/thread/thr_symbols.c new file mode 100644 index 0000000..ea323c5 --- /dev/null +++ b/lib/libthr/thread/thr_symbols.c @@ -0,0 +1,58 @@ +/* + * 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. 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" + +/* A collection of symbols needed by debugger */ + +/* int _libthr_debug */ +int _thread_off_tcb = offsetof(struct pthread, tcb); +int _thread_off_tid = offsetof(struct pthread, tid); +int _thread_off_next = offsetof(struct pthread, tle.tqe_next); +int _thread_off_attr_flags = offsetof(struct pthread, attr.flags); +int _thread_off_linkmap = offsetof(Obj_Entry, linkmap); +int _thread_off_tlsindex = offsetof(Obj_Entry, tlsindex); +int _thread_off_report_events = offsetof(struct pthread, report_events); +int _thread_off_event_mask = offsetof(struct pthread, event_mask); +int _thread_off_event_buf = offsetof(struct pthread, event_buf); +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; diff --git a/lib/libthr/thread/thr_syscalls.c b/lib/libthr/thread/thr_syscalls.c new file mode 100644 index 0000000..2327d74 --- /dev/null +++ b/lib/libthr/thread/thr_syscalls.c @@ -0,0 +1,763 @@ +/* + * Copyright (C) 2005 David Xu <davidxu@freebsd.org>. + * Copyright (c) 2003 Daniel Eischen <deischen@freebsd.org>. + * 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$ + */ + +/* + * 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. 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 "namespace.h" +#include <sys/types.h> +#include <sys/mman.h> +#include <sys/param.h> +#include <sys/select.h> +#include <sys/signalvar.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/uio.h> +#include <sys/wait.h> +#include <aio.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <poll.h> +#include <signal.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> +#include <pthread.h> +#include "un-namespace.h" + +#include "thr_private.h" + +extern int __creat(const char *, mode_t); +extern int __pselect(int, fd_set *, fd_set *, fd_set *, + const struct timespec *, const sigset_t *); +extern unsigned __sleep(unsigned int); +extern int __system(const char *); +extern int __tcdrain(int); +extern int __usleep(useconds_t); +extern pid_t __wait(int *); +extern pid_t __waitpid(pid_t, int *, int); +extern int __sys_aio_suspend(const struct aiocb * const[], int, + const struct timespec *); +extern int __sys_accept(int, struct sockaddr *, socklen_t *); +extern int __sys_connect(int, const struct sockaddr *, socklen_t); +extern int __sys_fsync(int); +extern int __sys_msync(void *, size_t, int); +extern int __sys_pselect(int, fd_set *, fd_set *, fd_set *, + const struct timespec *, const sigset_t *); +extern int __sys_poll(struct pollfd *, unsigned, int); +extern ssize_t __sys_recv(int, void *, size_t, int); +extern ssize_t __sys_recvfrom(int, void *, size_t, int, struct sockaddr *, socklen_t *); +extern ssize_t __sys_recvmsg(int, struct msghdr *, int); +extern int __sys_select(int, fd_set *, fd_set *, fd_set *, struct timeval *); +extern int __sys_sendfile(int, int, off_t, size_t, struct sf_hdtr *, + off_t *, int); +extern ssize_t __sys_sendmsg(int, const struct msghdr *, int); +extern ssize_t __sys_sendto(int, const void *,size_t, int, const struct sockaddr *, socklen_t); +extern ssize_t __sys_readv(int, const struct iovec *, int); +extern pid_t __sys_wait4(pid_t, int *, int, struct rusage *); +extern ssize_t __sys_writev(int, const struct iovec *, int); + +int ___creat(const char *, mode_t); +int ___pselect(int, fd_set *, fd_set *, fd_set *, + const struct timespec *, const sigset_t *); +unsigned ___sleep(unsigned); +int ___system(const char *); +int ___tcdrain(int); +int ___usleep(useconds_t useconds); +pid_t ___wait(int *); +pid_t ___waitpid(pid_t, int *, int); +int __accept(int, struct sockaddr *, socklen_t *); +int __aio_suspend(const struct aiocb * const iocbs[], int, + const struct timespec *); +int __close(int); +int __connect(int, const struct sockaddr *, socklen_t); +int __fcntl(int, int,...); +#ifdef SYSCALL_COMPAT +extern int __fcntl_compat(int, int,...); +#endif +int __fsync(int); +int __msync(void *, size_t, int); +int __nanosleep(const struct timespec *, struct timespec *); +int __open(const char *, int,...); +int __openat(int, const char *, int,...); +int __poll(struct pollfd *, unsigned int, int); +ssize_t __read(int, void *buf, size_t); +ssize_t __readv(int, const struct iovec *, int); +ssize_t __recvfrom(int, void *, size_t, int f, struct sockaddr *, socklen_t *); +ssize_t __recvmsg(int, struct msghdr *, int); +int __select(int, fd_set *, fd_set *, fd_set *, struct timeval *); +ssize_t __sendmsg(int, const struct msghdr *, int); +ssize_t __sendto(int, const void *, size_t, int, + const struct sockaddr *, socklen_t); +pid_t __wait3(int *, int, struct rusage *); +pid_t __wait4(pid_t, int *, int, struct rusage *); +ssize_t __write(int, const void *, size_t); +ssize_t __writev(int, const struct iovec *, int); + +__weak_reference(__accept, accept); + +/* + * Cancellation behavior: + * If thread is canceled, no socket is created. + */ +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); +} + +__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); +} + +__weak_reference(__close, close); + +/* + * Cancellation behavior: + * According to manual of close(), the file descriptor is always deleted. + * Here, thread is only canceled after the system call, so the file + * descriptor is always deleted despite whether the thread is canceled + * or not. + */ +int +__close(int fd) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_cancel_enter2(curthread, 0); + ret = __sys_close(fd); + _thr_cancel_leave(curthread, 1); + + return (ret); +} + +__weak_reference(__connect, connect); + +/* + * Cancellation behavior: + * If the thread is canceled, connection is not made. + */ +int +__connect(int fd, const struct sockaddr *name, socklen_t namelen) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_cancel_enter(curthread); + ret = __sys_connect(fd, name, namelen); + _thr_cancel_leave(curthread, ret == -1); + + return (ret); +} + +__weak_reference(___creat, creat); + +/* + * Cancellation behavior: + * If thread is canceled, file is not created. + */ +int +___creat(const char *path, mode_t mode) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_cancel_enter(curthread); + ret = __creat(path, mode); + _thr_cancel_leave(curthread, ret == -1); + + return ret; +} + +__weak_reference(__fcntl, fcntl); + +/* + * Cancellation behavior: + * According to specification, only F_SETLKW is a cancellation point. + * Thread is only canceled at start, or canceled if the system call + * is failure, this means the function does not generate side effect + * if it is canceled. + */ +int +__fcntl(int fd, int cmd,...) +{ + struct pthread *curthread = _get_curthread(); + int ret; + va_list ap; + + va_start(ap, cmd); + if (cmd == F_OSETLKW || cmd == F_SETLKW) { + _thr_cancel_enter(curthread); +#ifdef SYSCALL_COMPAT + ret = __fcntl_compat(fd, cmd, va_arg(ap, void *)); +#else + ret = __sys_fcntl(fd, cmd, va_arg(ap, void *)); +#endif + _thr_cancel_leave(curthread, ret == -1); + } else { +#ifdef SYSCALL_COMPAT + ret = __fcntl_compat(fd, cmd, va_arg(ap, void *)); +#else + ret = __sys_fcntl(fd, cmd, va_arg(ap, void *)); +#endif + } + va_end(ap); + + return (ret); +} + +__weak_reference(__fsync, fsync); + +/* + * Cancellation behavior: + * Thread may be canceled after system call. + */ +int +__fsync(int fd) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_cancel_enter2(curthread, 0); + ret = __sys_fsync(fd); + _thr_cancel_leave(curthread, 1); + + return (ret); +} + +__weak_reference(__msync, msync); + +/* + * Cancellation behavior: + * Thread may be canceled after system call. + */ +int +__msync(void *addr, size_t len, int flags) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_cancel_enter2(curthread, 0); + ret = __sys_msync(addr, len, flags); + _thr_cancel_leave(curthread, 1); + + return ret; +} + +__weak_reference(__nanosleep, nanosleep); + +int +__nanosleep(const struct timespec *time_to_sleep, + struct timespec *time_remaining) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_cancel_enter(curthread); + ret = __sys_nanosleep(time_to_sleep, time_remaining); + _thr_cancel_leave(curthread, 1); + + return (ret); +} + +__weak_reference(__open, open); + +/* + * Cancellation behavior: + * If the thread is canceled, file is not opened. + */ +int +__open(const char *path, int flags,...) +{ + struct pthread *curthread = _get_curthread(); + int ret; + int mode = 0; + va_list ap; + + /* 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); + } + + _thr_cancel_enter(curthread); + ret = __sys_open(path, flags, mode); + _thr_cancel_leave(curthread, ret == -1); + + return ret; +} + +__weak_reference(__openat, openat); + +/* + * Cancellation behavior: + * If the thread is canceled, file is not opened. + */ +int +__openat(int fd, const char *path, int flags, ...) +{ + struct pthread *curthread = _get_curthread(); + int ret; + int mode = 0; + va_list ap; + + + /* 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); + } + + _thr_cancel_enter(curthread); + ret = __sys_openat(fd, path, flags, mode); + _thr_cancel_leave(curthread, ret == -1); + + return ret; +} + +__weak_reference(__poll, poll); + +/* + * Cancellation behavior: + * Thread may be canceled at start, but if the system call returns something, + * the thread is not canceled. + */ +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, ret == -1); + + return ret; +} + +__weak_reference(___pselect, pselect); + +/* + * Cancellation behavior: + * Thread may be canceled at start, but if the system call returns something, + * the thread is not canceled. + */ +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 = __sys_pselect(count, rfds, wfds, efds, timo, mask); + _thr_cancel_leave(curthread, ret == -1); + + return (ret); +} + +__weak_reference(__read, read); + +/* + * Cancellation behavior: + * Thread may be canceled at start, but if the system call got some data, + * the thread is not canceled. + */ +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, ret == -1); + + return ret; +} + +__weak_reference(__readv, readv); + +/* + * Cancellation behavior: + * Thread may be canceled at start, but if the system call got some data, + * the thread is not canceled. + */ +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, ret == -1); + return ret; +} + +__weak_reference(__recvfrom, recvfrom); + +/* + * Cancellation behavior: + * Thread may be canceled at start, but if the system call got some data, + * the thread is not canceled. + */ +ssize_t +__recvfrom(int s, void *b, size_t l, int f, struct sockaddr *from, + socklen_t *fl) +{ + struct pthread *curthread = _get_curthread(); + ssize_t ret; + + _thr_cancel_enter(curthread); + ret = __sys_recvfrom(s, b, l, f, from, fl); + _thr_cancel_leave(curthread, ret == -1); + return (ret); +} + +__weak_reference(__recvmsg, recvmsg); + +/* + * Cancellation behavior: + * Thread may be canceled at start, but if the system call got some data, + * the thread is not canceled. + */ +ssize_t +__recvmsg(int s, struct msghdr *m, int f) +{ + struct pthread *curthread = _get_curthread(); + ssize_t ret; + + _thr_cancel_enter(curthread); + ret = __sys_recvmsg(s, m, f); + _thr_cancel_leave(curthread, ret == -1); + return (ret); +} + +__weak_reference(__select, select); + +/* + * Cancellation behavior: + * Thread may be canceled at start, but if the system call returns something, + * the thread is not canceled. + */ +int +__select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + struct timeval *timeout) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_cancel_enter(curthread); + ret = __sys_select(numfds, readfds, writefds, exceptfds, timeout); + _thr_cancel_leave(curthread, ret == -1); + return ret; +} + +__weak_reference(__sendmsg, sendmsg); + +/* + * Cancellation behavior: + * Thread may be canceled at start, but if the system call sent + * data, the thread is not canceled. + */ +ssize_t +__sendmsg(int s, const struct msghdr *m, int f) +{ + struct pthread *curthread = _get_curthread(); + ssize_t ret; + + _thr_cancel_enter(curthread); + ret = __sys_sendmsg(s, m, f); + _thr_cancel_leave(curthread, ret <= 0); + return (ret); +} + +__weak_reference(__sendto, sendto); + +/* + * Cancellation behavior: + * Thread may be canceled at start, but if the system call sent some + * data, the thread is not canceled. + */ +ssize_t +__sendto(int s, const void *m, size_t l, int f, const struct sockaddr *t, + socklen_t tl) +{ + struct pthread *curthread = _get_curthread(); + ssize_t ret; + + _thr_cancel_enter(curthread); + ret = __sys_sendto(s, m, l, f, t, tl); + _thr_cancel_leave(curthread, ret <= 0); + return (ret); +} + +__weak_reference(___sleep, sleep); + +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); +} + +__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; +} + +__weak_reference(___tcdrain, tcdrain); + +/* + * Cancellation behavior: + * If thread is canceled, the system call is not completed, + * this means not all bytes were drained. + */ +int +___tcdrain(int fd) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_cancel_enter(curthread); + ret = __tcdrain(fd); + _thr_cancel_leave(curthread, ret == -1); + return (ret); +} + +__weak_reference(___usleep, usleep); + +int +___usleep(useconds_t useconds) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_cancel_enter(curthread); + ret = __usleep(useconds); + _thr_cancel_leave(curthread, 1); + + return (ret); +} + +__weak_reference(___wait, wait); + +/* + * Cancellation behavior: + * Thread may be canceled at start, but if the system call returns + * a child pid, the thread is not canceled. + */ +pid_t +___wait(int *istat) +{ + struct pthread *curthread = _get_curthread(); + pid_t ret; + + _thr_cancel_enter(curthread); + ret = __wait(istat); + _thr_cancel_leave(curthread, ret <= 0); + + return ret; +} + +__weak_reference(__wait3, wait3); + +/* + * Cancellation behavior: + * Thread may be canceled at start, but if the system call returns + * a child pid, the thread is not canceled. + */ +pid_t +__wait3(int *status, int options, struct rusage *rusage) +{ + struct pthread *curthread = _get_curthread(); + pid_t ret; + + _thr_cancel_enter(curthread); + ret = _wait4(WAIT_ANY, status, options, rusage); + _thr_cancel_leave(curthread, ret <= 0); + + return (ret); +} + +__weak_reference(__wait4, wait4); + +/* + * Cancellation behavior: + * Thread may be canceled at start, but if the system call returns + * a child pid, the thread is not canceled. + */ +pid_t +__wait4(pid_t pid, int *status, int options, struct rusage *rusage) +{ + struct pthread *curthread = _get_curthread(); + pid_t ret; + + _thr_cancel_enter(curthread); + ret = __sys_wait4(pid, status, options, rusage); + _thr_cancel_leave(curthread, ret <= 0); + + return ret; +} + +__weak_reference(___waitpid, waitpid); + +/* + * Cancellation behavior: + * Thread may be canceled at start, but if the system call returns + * a child pid, the thread is not canceled. + */ +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, ret <= 0); + + return ret; +} + +__weak_reference(__write, write); + +/* + * Cancellation behavior: + * Thread may be canceled at start, but if the thread wrote some data, + * it is not canceled. + */ +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, (ret <= 0)); + return ret; +} + +__weak_reference(__writev, writev); + +/* + * Cancellation behavior: + * Thread may be canceled at start, but if the thread wrote some data, + * it is not canceled. + */ +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, (ret <= 0)); + return ret; +} diff --git a/lib/libthr/thread/thr_umtx.c b/lib/libthr/thread/thr_umtx.c new file mode 100644 index 0000000..a61dab0 --- /dev/null +++ b/lib/libthr/thread/thr_umtx.c @@ -0,0 +1,342 @@ +/* + * 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 "thr_private.h" +#include "thr_umtx.h" + +#ifndef HAS__UMTX_OP_ERR +int _umtx_op_err(void *obj, int op, u_long val, void *uaddr, void *uaddr2) +{ + if (_umtx_op(obj, op, val, uaddr, uaddr2) == -1) + return (errno); + return (0); +} +#endif + +void +_thr_umutex_init(struct umutex *mtx) +{ + static struct umutex default_mtx = DEFAULT_UMUTEX; + + *mtx = default_mtx; +} + +void +_thr_urwlock_init(struct urwlock *rwl) +{ + static struct urwlock default_rwl = DEFAULT_URWLOCK; + *rwl = default_rwl; +} + +int +__thr_umutex_lock(struct umutex *mtx, uint32_t id) +{ + uint32_t owner; + + if ((mtx->m_flags & (UMUTEX_PRIO_PROTECT | UMUTEX_PRIO_INHERIT)) == 0) { + for (;;) { + /* wait in kernel */ + _umtx_op_err(mtx, UMTX_OP_MUTEX_WAIT, 0, 0, 0); + + owner = mtx->m_owner; + if ((owner & ~UMUTEX_CONTESTED) == 0 && + atomic_cmpset_acq_32(&mtx->m_owner, owner, id|owner)) + return (0); + } + } + + return _umtx_op_err(mtx, UMTX_OP_MUTEX_LOCK, 0, 0, 0); +} + +#define SPINLOOPS 1000 + +int +__thr_umutex_lock_spin(struct umutex *mtx, uint32_t id) +{ + uint32_t owner; + + if (!_thr_is_smp) + return __thr_umutex_lock(mtx, id); + + if ((mtx->m_flags & (UMUTEX_PRIO_PROTECT | UMUTEX_PRIO_INHERIT)) == 0) { + for (;;) { + int count = SPINLOOPS; + while (count--) { + owner = mtx->m_owner; + if ((owner & ~UMUTEX_CONTESTED) == 0) { + if (atomic_cmpset_acq_32( + &mtx->m_owner, + owner, id|owner)) { + return (0); + } + } + CPU_SPINWAIT; + } + + /* wait in kernel */ + _umtx_op_err(mtx, UMTX_OP_MUTEX_WAIT, 0, 0, 0); + } + } + + return _umtx_op_err(mtx, UMTX_OP_MUTEX_LOCK, 0, 0, 0); +} + +int +__thr_umutex_timedlock(struct umutex *mtx, uint32_t id, + const struct timespec *abstime) +{ + struct _umtx_time *tm_p, timeout; + size_t tm_size; + uint32_t owner; + int ret; + + if (abstime == NULL) { + tm_p = NULL; + tm_size = 0; + } else { + timeout._clockid = CLOCK_REALTIME; + timeout._flags = UMTX_ABSTIME; + timeout._timeout = *abstime; + tm_p = &timeout; + tm_size = sizeof(timeout); + } + + for (;;) { + if ((mtx->m_flags & (UMUTEX_PRIO_PROTECT | UMUTEX_PRIO_INHERIT)) == 0) { + + /* wait in kernel */ + ret = _umtx_op_err(mtx, UMTX_OP_MUTEX_WAIT, 0, + (void *)tm_size, __DECONST(void *, tm_p)); + + /* now try to lock it */ + owner = mtx->m_owner; + if ((owner & ~UMUTEX_CONTESTED) == 0 && + atomic_cmpset_acq_32(&mtx->m_owner, owner, id|owner)) + return (0); + } else { + ret = _umtx_op_err(mtx, UMTX_OP_MUTEX_LOCK, 0, + (void *)tm_size, __DECONST(void *, tm_p)); + if (ret == 0) + break; + } + if (ret == ETIMEDOUT) + break; + } + return (ret); +} + +int +__thr_umutex_unlock(struct umutex *mtx, uint32_t id) +{ + return _umtx_op_err(mtx, UMTX_OP_MUTEX_UNLOCK, 0, 0, 0); +} + +int +__thr_umutex_trylock(struct umutex *mtx) +{ + return _umtx_op_err(mtx, UMTX_OP_MUTEX_TRYLOCK, 0, 0, 0); +} + +int +__thr_umutex_set_ceiling(struct umutex *mtx, uint32_t ceiling, + uint32_t *oldceiling) +{ + return _umtx_op_err(mtx, UMTX_OP_SET_CEILING, ceiling, oldceiling, 0); +} + +int +_thr_umtx_wait(volatile long *mtx, long id, const struct timespec *timeout) +{ + if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 && + timeout->tv_nsec <= 0))) + return (ETIMEDOUT); + return _umtx_op_err(__DEVOLATILE(void *, mtx), UMTX_OP_WAIT, id, 0, + __DECONST(void*, timeout)); +} + +int +_thr_umtx_wait_uint(volatile u_int *mtx, u_int id, const struct timespec *timeout, int shared) +{ + if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 && + timeout->tv_nsec <= 0))) + return (ETIMEDOUT); + return _umtx_op_err(__DEVOLATILE(void *, mtx), + shared ? UMTX_OP_WAIT_UINT : UMTX_OP_WAIT_UINT_PRIVATE, id, 0, + __DECONST(void*, timeout)); +} + +int +_thr_umtx_timedwait_uint(volatile u_int *mtx, u_int id, int clockid, + const struct timespec *abstime, int shared) +{ + struct _umtx_time *tm_p, timeout; + size_t tm_size; + + if (abstime == NULL) { + tm_p = NULL; + tm_size = 0; + } else { + timeout._clockid = clockid; + timeout._flags = UMTX_ABSTIME; + timeout._timeout = *abstime; + tm_p = &timeout; + tm_size = sizeof(timeout); + } + + return _umtx_op_err(__DEVOLATILE(void *, mtx), + shared ? UMTX_OP_WAIT_UINT : UMTX_OP_WAIT_UINT_PRIVATE, id, + (void *)tm_size, __DECONST(void *, tm_p)); +} + +int +_thr_umtx_wake(volatile void *mtx, int nr_wakeup, int shared) +{ + return _umtx_op_err(__DEVOLATILE(void *, mtx), shared ? UMTX_OP_WAKE : UMTX_OP_WAKE_PRIVATE, + nr_wakeup, 0, 0); +} + +void +_thr_ucond_init(struct ucond *cv) +{ + bzero(cv, sizeof(struct ucond)); +} + +int +_thr_ucond_wait(struct ucond *cv, struct umutex *m, + const struct timespec *timeout, int flags) +{ + if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 && + timeout->tv_nsec <= 0))) { + struct pthread *curthread = _get_curthread(); + _thr_umutex_unlock(m, TID(curthread)); + return (ETIMEDOUT); + } + return _umtx_op_err(cv, UMTX_OP_CV_WAIT, flags, + m, __DECONST(void*, timeout)); +} + +int +_thr_ucond_signal(struct ucond *cv) +{ + if (!cv->c_has_waiters) + return (0); + return _umtx_op_err(cv, UMTX_OP_CV_SIGNAL, 0, NULL, NULL); +} + +int +_thr_ucond_broadcast(struct ucond *cv) +{ + if (!cv->c_has_waiters) + return (0); + return _umtx_op_err(cv, UMTX_OP_CV_BROADCAST, 0, NULL, NULL); +} + +int +__thr_rwlock_rdlock(struct urwlock *rwlock, int flags, + const struct timespec *tsp) +{ + struct _umtx_time timeout, *tm_p; + size_t tm_size; + + if (tsp == NULL) { + tm_p = NULL; + tm_size = 0; + } else { + timeout._timeout = *tsp; + timeout._flags = UMTX_ABSTIME; + timeout._clockid = CLOCK_REALTIME; + tm_p = &timeout; + tm_size = sizeof(timeout); + } + return _umtx_op_err(rwlock, UMTX_OP_RW_RDLOCK, flags, (void *)tm_size, tm_p); +} + +int +__thr_rwlock_wrlock(struct urwlock *rwlock, const struct timespec *tsp) +{ + struct _umtx_time timeout, *tm_p; + size_t tm_size; + + if (tsp == NULL) { + tm_p = NULL; + tm_size = 0; + } else { + timeout._timeout = *tsp; + timeout._flags = UMTX_ABSTIME; + timeout._clockid = CLOCK_REALTIME; + tm_p = &timeout; + tm_size = sizeof(timeout); + } + return _umtx_op_err(rwlock, UMTX_OP_RW_WRLOCK, 0, (void *)tm_size, tm_p); +} + +int +__thr_rwlock_unlock(struct urwlock *rwlock) +{ + return _umtx_op_err(rwlock, UMTX_OP_RW_UNLOCK, 0, NULL, NULL); +} + +void +_thr_rwl_rdlock(struct urwlock *rwlock) +{ + int ret; + + for (;;) { + if (_thr_rwlock_tryrdlock(rwlock, URWLOCK_PREFER_READER) == 0) + return; + ret = __thr_rwlock_rdlock(rwlock, URWLOCK_PREFER_READER, NULL); + if (ret == 0) + return; + if (ret != EINTR) + PANIC("rdlock error"); + } +} + +void +_thr_rwl_wrlock(struct urwlock *rwlock) +{ + int ret; + + for (;;) { + if (_thr_rwlock_trywrlock(rwlock) == 0) + return; + ret = __thr_rwlock_wrlock(rwlock, NULL); + if (ret == 0) + return; + if (ret != EINTR) + PANIC("wrlock error"); + } +} + +void +_thr_rwl_unlock(struct urwlock *rwlock) +{ + if (_thr_rwlock_unlock(rwlock)) + PANIC("unlock error"); +} diff --git a/lib/libthr/thread/thr_umtx.h b/lib/libthr/thread/thr_umtx.h new file mode 100644 index 0000000..d74cc81 --- /dev/null +++ b/lib/libthr/thread/thr_umtx.h @@ -0,0 +1,224 @@ +/*- + * 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, 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$ + */ + +#ifndef _THR_FBSD_UMTX_H_ +#define _THR_FBSD_UMTX_H_ + +#include <strings.h> +#include <sys/umtx.h> + +#define DEFAULT_UMUTEX {0,0,{0,0},{0,0,0,0}} +#define DEFAULT_URWLOCK {0,0,0,0,{0,0,0,0}} + +int _umtx_op_err(void *, int op, u_long, void *, void *) __hidden; +int __thr_umutex_lock(struct umutex *mtx, uint32_t id) __hidden; +int __thr_umutex_lock_spin(struct umutex *mtx, uint32_t id) __hidden; +int __thr_umutex_timedlock(struct umutex *mtx, uint32_t id, + const struct timespec *timeout) __hidden; +int __thr_umutex_unlock(struct umutex *mtx, uint32_t id) __hidden; +int __thr_umutex_trylock(struct umutex *mtx) __hidden; +int __thr_umutex_set_ceiling(struct umutex *mtx, uint32_t ceiling, + uint32_t *oldceiling) __hidden; + +void _thr_umutex_init(struct umutex *mtx) __hidden; +void _thr_urwlock_init(struct urwlock *rwl) __hidden; + +int _thr_umtx_wait(volatile long *mtx, long exp, + const struct timespec *timeout) __hidden; +int _thr_umtx_wait_uint(volatile u_int *mtx, u_int exp, + const struct timespec *timeout, int shared) __hidden; +int _thr_umtx_timedwait_uint(volatile u_int *mtx, u_int exp, int clockid, + const struct timespec *timeout, int shared) __hidden; +int _thr_umtx_wake(volatile void *mtx, int count, int shared) __hidden; +int _thr_ucond_wait(struct ucond *cv, struct umutex *m, + const struct timespec *timeout, int check_unpaking) __hidden; +void _thr_ucond_init(struct ucond *cv) __hidden; +int _thr_ucond_signal(struct ucond *cv) __hidden; +int _thr_ucond_broadcast(struct ucond *cv) __hidden; + +int __thr_rwlock_rdlock(struct urwlock *rwlock, int flags, + const struct timespec *tsp) __hidden; +int __thr_rwlock_wrlock(struct urwlock *rwlock, + const struct timespec *tsp) __hidden; +int __thr_rwlock_unlock(struct urwlock *rwlock) __hidden; + +/* Internal used only */ +void _thr_rwl_rdlock(struct urwlock *rwlock) __hidden; +void _thr_rwl_wrlock(struct urwlock *rwlock) __hidden; +void _thr_rwl_unlock(struct urwlock *rwlock) __hidden; + +static inline int +_thr_umutex_trylock(struct umutex *mtx, uint32_t id) +{ + if (atomic_cmpset_acq_32(&mtx->m_owner, UMUTEX_UNOWNED, id)) + return (0); + if ((mtx->m_flags & UMUTEX_PRIO_PROTECT) == 0) + return (EBUSY); + return (__thr_umutex_trylock(mtx)); +} + +static inline int +_thr_umutex_trylock2(struct umutex *mtx, uint32_t id) +{ + if (atomic_cmpset_acq_32(&mtx->m_owner, UMUTEX_UNOWNED, id) != 0) + return (0); + if ((uint32_t)mtx->m_owner == UMUTEX_CONTESTED && + __predict_true((mtx->m_flags & (UMUTEX_PRIO_PROTECT | UMUTEX_PRIO_INHERIT)) == 0)) + if (atomic_cmpset_acq_32(&mtx->m_owner, UMUTEX_CONTESTED, id | UMUTEX_CONTESTED)) + return (0); + return (EBUSY); +} + +static inline int +_thr_umutex_lock(struct umutex *mtx, uint32_t id) +{ + if (_thr_umutex_trylock2(mtx, id) == 0) + return (0); + return (__thr_umutex_lock(mtx, id)); +} + +static inline int +_thr_umutex_lock_spin(struct umutex *mtx, uint32_t id) +{ + if (_thr_umutex_trylock2(mtx, id) == 0) + return (0); + return (__thr_umutex_lock_spin(mtx, id)); +} + +static inline int +_thr_umutex_timedlock(struct umutex *mtx, uint32_t id, + const struct timespec *timeout) +{ + if (_thr_umutex_trylock2(mtx, id) == 0) + return (0); + return (__thr_umutex_timedlock(mtx, id, timeout)); +} + +static inline int +_thr_umutex_unlock(struct umutex *mtx, uint32_t id) +{ + uint32_t flags = mtx->m_flags; + + if ((flags & (UMUTEX_PRIO_PROTECT | UMUTEX_PRIO_INHERIT)) == 0) { + uint32_t owner; + do { + owner = mtx->m_owner; + if (__predict_false((owner & ~UMUTEX_CONTESTED) != id)) + return (EPERM); + } while (__predict_false(!atomic_cmpset_rel_32(&mtx->m_owner, + owner, UMUTEX_UNOWNED))); + if ((owner & UMUTEX_CONTESTED)) + (void)_umtx_op_err(mtx, UMTX_OP_MUTEX_WAKE2, flags, 0, 0); + return (0); + } + if (atomic_cmpset_rel_32(&mtx->m_owner, id, UMUTEX_UNOWNED)) + return (0); + return (__thr_umutex_unlock(mtx, id)); +} + +static inline int +_thr_rwlock_tryrdlock(struct urwlock *rwlock, int flags) +{ + int32_t state; + int32_t wrflags; + + if (flags & URWLOCK_PREFER_READER || rwlock->rw_flags & URWLOCK_PREFER_READER) + wrflags = URWLOCK_WRITE_OWNER; + else + wrflags = URWLOCK_WRITE_OWNER | URWLOCK_WRITE_WAITERS; + state = rwlock->rw_state; + while (!(state & wrflags)) { + if (__predict_false(URWLOCK_READER_COUNT(state) == URWLOCK_MAX_READERS)) + return (EAGAIN); + if (atomic_cmpset_acq_32(&rwlock->rw_state, state, state + 1)) + return (0); + state = rwlock->rw_state; + } + + return (EBUSY); +} + +static inline int +_thr_rwlock_trywrlock(struct urwlock *rwlock) +{ + int32_t state; + + state = rwlock->rw_state; + while (!(state & URWLOCK_WRITE_OWNER) && URWLOCK_READER_COUNT(state) == 0) { + if (atomic_cmpset_acq_32(&rwlock->rw_state, state, state | URWLOCK_WRITE_OWNER)) + return (0); + state = rwlock->rw_state; + } + + return (EBUSY); +} + +static inline int +_thr_rwlock_rdlock(struct urwlock *rwlock, int flags, struct timespec *tsp) +{ + if (_thr_rwlock_tryrdlock(rwlock, flags) == 0) + return (0); + return (__thr_rwlock_rdlock(rwlock, flags, tsp)); +} + +static inline int +_thr_rwlock_wrlock(struct urwlock *rwlock, struct timespec *tsp) +{ + if (_thr_rwlock_trywrlock(rwlock) == 0) + return (0); + return (__thr_rwlock_wrlock(rwlock, tsp)); +} + +static inline int +_thr_rwlock_unlock(struct urwlock *rwlock) +{ + int32_t state; + + state = rwlock->rw_state; + if (state & URWLOCK_WRITE_OWNER) { + if (atomic_cmpset_rel_32(&rwlock->rw_state, URWLOCK_WRITE_OWNER, 0)) + return (0); + } else { + for (;;) { + if (__predict_false(URWLOCK_READER_COUNT(state) == 0)) + return (EPERM); + if (!((state & (URWLOCK_WRITE_WAITERS | + URWLOCK_READ_WAITERS)) && + URWLOCK_READER_COUNT(state) == 1)) { + if (atomic_cmpset_rel_32(&rwlock->rw_state, + state, state-1)) + return (0); + state = rwlock->rw_state; + } else { + break; + } + } + } + return (__thr_rwlock_unlock(rwlock)); +} +#endif diff --git a/lib/libthr/thread/thr_yield.c b/lib/libthr/thread/thr_yield.c new file mode 100644 index 0000000..c3f68f2 --- /dev/null +++ b/lib/libthr/thread/thr_yield.c @@ -0,0 +1,45 @@ +/* + * 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. 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 <sched.h> +#include "un-namespace.h" + +__weak_reference(_pthread_yield, pthread_yield); + +/* Draft 4 yield */ +void +_pthread_yield(void) +{ + + sched_yield(); +} |