Index: head/lib/libthr/thread/thr_attr.c =================================================================== --- head/lib/libthr/thread/thr_attr.c (revision 350480) +++ head/lib/libthr/thread/thr_attr.c (revision 350481) @@ -1,660 +1,678 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 2003 Craig Rodrigues . * 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 . * Copyright (C) 2001 Jason Evans . * Copyright (c) 2002,2003 Alexey Zelkin * 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 . * 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 __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include #include #include #include #include "un-namespace.h" #include "thr_private.h" static size_t _get_kern_cpuset_size(void); -__weak_reference(_pthread_attr_destroy, pthread_attr_destroy); +__weak_reference(_thr_attr_destroy, _pthread_attr_destroy); +__weak_reference(_thr_attr_destroy, pthread_attr_destroy); int -_pthread_attr_destroy(pthread_attr_t *attr) +_thr_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); +__weak_reference(_thr_attr_get_np, pthread_attr_get_np); +__weak_reference(_thr_attr_get_np, _pthread_attr_get_np); int -_pthread_attr_get_np(pthread_t pthread, pthread_attr_t *dstattr) +_thr_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); +__weak_reference(_thr_attr_getdetachstate, pthread_attr_getdetachstate); +__weak_reference(_thr_attr_getdetachstate, _pthread_attr_getdetachstate); int -_pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate) +_thr_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); +__weak_reference(_thr_attr_getguardsize, pthread_attr_getguardsize); +__weak_reference(_thr_attr_getguardsize, _pthread_attr_getguardsize); int -_pthread_attr_getguardsize(const pthread_attr_t * __restrict attr, +_thr_attr_getguardsize(const pthread_attr_t * __restrict attr, size_t * __restrict 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); +__weak_reference(_thr_attr_getinheritsched, pthread_attr_getinheritsched); +__weak_reference(_thr_attr_getinheritsched, _pthread_attr_getinheritsched); int -_pthread_attr_getinheritsched(const pthread_attr_t * __restrict attr, +_thr_attr_getinheritsched(const pthread_attr_t * __restrict attr, int * __restrict 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); +__weak_reference(_thr_attr_getschedparam, pthread_attr_getschedparam); +__weak_reference(_thr_attr_getschedparam, _pthread_attr_getschedparam); int -_pthread_attr_getschedparam(const pthread_attr_t * __restrict attr, +_thr_attr_getschedparam(const pthread_attr_t * __restrict attr, struct sched_param * __restrict 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); +__weak_reference(_thr_attr_getschedpolicy, pthread_attr_getschedpolicy); +__weak_reference(_thr_attr_getschedpolicy, _pthread_attr_getschedpolicy); int -_pthread_attr_getschedpolicy(const pthread_attr_t * __restrict attr, +_thr_attr_getschedpolicy(const pthread_attr_t * __restrict attr, int * __restrict 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); +__weak_reference(_thr_attr_getscope, pthread_attr_getscope); +__weak_reference(_thr_attr_getscope, _pthread_attr_getscope); int -_pthread_attr_getscope(const pthread_attr_t * __restrict attr, +_thr_attr_getscope(const pthread_attr_t * __restrict attr, int * __restrict 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) + 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); +__weak_reference(_thr_attr_getstackaddr, pthread_attr_getstackaddr); +__weak_reference(_thr_attr_getstackaddr, _pthread_attr_getstackaddr); int -_pthread_attr_getstackaddr(const pthread_attr_t *attr, void **stackaddr) +_thr_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); +__weak_reference(_thr_attr_getstacksize, pthread_attr_getstacksize); +__weak_reference(_thr_attr_getstacksize, _pthread_attr_getstacksize); int -_pthread_attr_getstacksize(const pthread_attr_t * __restrict attr, +_thr_attr_getstacksize(const pthread_attr_t * __restrict attr, size_t * __restrict 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); +__weak_reference(_thr_attr_init, pthread_attr_init); +__weak_reference(_thr_attr_init, _pthread_attr_init); int -_pthread_attr_init(pthread_attr_t *attr) +_thr_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); +__weak_reference(_thr_attr_setdetachstate, pthread_attr_setdetachstate); +__weak_reference(_thr_attr_setdetachstate, _pthread_attr_setdetachstate); int -_pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate) +_thr_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); +__weak_reference(_thr_attr_setguardsize, pthread_attr_setguardsize); +__weak_reference(_thr_attr_setguardsize, _pthread_attr_setguardsize); int -_pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize) +_thr_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); +__weak_reference(_thr_attr_setinheritsched, pthread_attr_setinheritsched); +__weak_reference(_thr_attr_setinheritsched, _pthread_attr_setinheritsched); int -_pthread_attr_setinheritsched(pthread_attr_t *attr, int sched_inherit) +_thr_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); +__weak_reference(_thr_attr_setschedparam, pthread_attr_setschedparam); +__weak_reference(_thr_attr_setschedparam, _pthread_attr_setschedparam); int -_pthread_attr_setschedparam(pthread_attr_t * __restrict attr, +_thr_attr_setschedparam(pthread_attr_t * __restrict attr, const struct sched_param * __restrict 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); +__weak_reference(_thr_attr_setschedpolicy, pthread_attr_setschedpolicy); +__weak_reference(_thr_attr_setschedpolicy, _pthread_attr_setschedpolicy); int -_pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy) +_thr_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); +__weak_reference(_thr_attr_setscope, pthread_attr_setscope); +__weak_reference(_thr_attr_setscope, _pthread_attr_setscope); int -_pthread_attr_setscope(pthread_attr_t *attr, int contentionscope) +_thr_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); +__weak_reference(_thr_attr_setstackaddr, pthread_attr_setstackaddr); +__weak_reference(_thr_attr_setstackaddr, _pthread_attr_setstackaddr); int -_pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr) +_thr_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); +__weak_reference(_thr_attr_setstacksize, pthread_attr_setstacksize); +__weak_reference(_thr_attr_setstacksize, _pthread_attr_setstacksize); int -_pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize) +_thr_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 (((const 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); } Index: head/lib/libthr/thread/thr_cancel.c =================================================================== --- head/lib/libthr/thread/thr_cancel.c (revision 350480) +++ head/lib/libthr/thread/thr_cancel.c (revision 350481) @@ -1,181 +1,185 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * 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 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. */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include #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); +__weak_reference(_thr_cancel, pthread_cancel); +__weak_reference(_thr_cancel, _pthread_cancel); +__weak_reference(_thr_setcancelstate, pthread_setcancelstate); +__weak_reference(_thr_setcancelstate, _pthread_setcancelstate); +__weak_reference(_thr_setcanceltype, pthread_setcanceltype); +__weak_reference(_thr_setcanceltype, _pthread_setcanceltype); +__weak_reference(_Tthr_testcancel, pthread_testcancel); +__weak_reference(_Tthr_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) +_thr_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) +_thr_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; if (curthread->cancel_async) 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) +_thr_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) +_Tthr_testcancel(void) { struct pthread *curthread; _thr_check_init(); 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); } Index: head/lib/libthr/thread/thr_clean.c =================================================================== --- head/lib/libthr/thread/thr_clean.c (revision 350480) +++ head/lib/libthr/thread/thr_clean.c (revision 350481) @@ -1,104 +1,110 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1995 John Birrell . * 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 __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include #include #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); +__weak_reference(_thr_cleanup_pop, pthread_cleanup_pop); +__weak_reference(_thr_cleanup_pop, _pthread_cleanup_pop); +__weak_reference(_thr_cleanup_push, pthread_cleanup_push); +__weak_reference(_thr_cleanup_push, _pthread_cleanup_push); +/* help static linking when libc symbols have preference */ +__weak_reference(__thr_cleanup_push_imp, __pthread_cleanup_push_imp); +__weak_reference(__thr_cleanup_pop_imp, __pthread_cleanup_pop_imp); + void -__pthread_cleanup_push_imp(void (*routine)(void *), void *arg, - struct _pthread_cleanup_info *info) +__thr_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) +__thr_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) +_thr_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))) != NULL) { newbuf->routine = routine; newbuf->routine_arg = arg; newbuf->onheap = 1; newbuf->prev = curthread->cleanup; curthread->cleanup = newbuf; } } void -_pthread_cleanup_pop(int execute) +_thr_cleanup_pop(int execute) { __pthread_cleanup_pop_imp(execute); } Index: head/lib/libthr/thread/thr_cond.c =================================================================== --- head/lib/libthr/thread/thr_cond.c (revision 350480) +++ head/lib/libthr/thread/thr_cond.c (revision 350481) @@ -1,555 +1,559 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2005 David Xu * Copyright (c) 2015 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by Konstantin Belousov * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, 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. */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include #include #include #include "un-namespace.h" #include "thr_private.h" _Static_assert(sizeof(struct pthread_cond) <= PAGE_SIZE, "pthread_cond too large"); /* * 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(__thr_cond_wait, pthread_cond_wait); +__weak_reference(__thr_cond_wait, __pthread_cond_wait); +__weak_reference(_thr_cond_wait, _pthread_cond_wait); __weak_reference(__pthread_cond_timedwait, pthread_cond_timedwait); +__weak_reference(_thr_cond_init, pthread_cond_init); +__weak_reference(_thr_cond_init, _pthread_cond_init); +__weak_reference(_thr_cond_destroy, pthread_cond_destroy); +__weak_reference(_thr_cond_destroy, _pthread_cond_destroy); +__weak_reference(_thr_cond_signal, pthread_cond_signal); +__weak_reference(_thr_cond_signal, _pthread_cond_signal); +__weak_reference(_thr_cond_broadcast, pthread_cond_broadcast); +__weak_reference(_thr_cond_broadcast, _pthread_cond_broadcast); -__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)->kcond.c_flags & USYNC_PROCESS_SHARED) != 0) static void cond_init_body(struct pthread_cond *cvp, const struct pthread_cond_attr *cattr) { if (cattr == NULL) { cvp->kcond.c_clockid = CLOCK_REALTIME; } else { if (cattr->c_pshared) cvp->kcond.c_flags |= USYNC_PROCESS_SHARED; cvp->kcond.c_clockid = cattr->c_clockid; } } static int cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr) { struct pthread_cond *cvp; const struct pthread_cond_attr *cattr; int pshared; cattr = cond_attr != NULL ? *cond_attr : NULL; if (cattr == NULL || cattr->c_pshared == PTHREAD_PROCESS_PRIVATE) { pshared = 0; cvp = calloc(1, sizeof(struct pthread_cond)); if (cvp == NULL) return (ENOMEM); } else { pshared = 1; cvp = __thr_pshared_offpage(cond, 1); if (cvp == NULL) return (EFAULT); } /* * Initialise the condition variable structure: */ cond_init_body(cvp, cattr); *cond = pshared ? THR_PSHARED_PTR : cvp; return (0); } 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 (*cond == THR_PSHARED_PTR) { \ cvp = __thr_pshared_offpage(cond, 0); \ if (cvp == NULL) \ return (EINVAL); \ } else 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 * __restrict cond, +_thr_cond_init(pthread_cond_t * __restrict cond, const pthread_condattr_t * __restrict cond_attr) { *cond = NULL; return (cond_init(cond, cond_attr)); } int -_pthread_cond_destroy(pthread_cond_t *cond) +_thr_cond_destroy(pthread_cond_t *cond) { struct pthread_cond *cvp; int error; error = 0; if (*cond == THR_PSHARED_PTR) { cvp = __thr_pshared_offpage(cond, 0); if (cvp != NULL) { if (cvp->kcond.c_has_waiters) error = EBUSY; else __thr_pshared_destroy(cond); } if (error == 0) *cond = THR_COND_DESTROYED; } else if ((cvp = *cond) == THR_COND_INITIALIZER) { /* nothing */ } else if (cvp == THR_COND_DESTROYED) { error = EINVAL; } else { cvp = *cond; if (cvp->__has_user_waiters || cvp->kcond.c_has_waiters) error = EBUSY; else { *cond = THR_COND_DESTROYED; free(cvp); } } return (error); } /* * Cancellation behavior: * 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; int error, error2, recurse, robust; curthread = _get_curthread(); robust = _mutex_enter_robust(curthread, mp); error = _mutex_cv_detach(mp, &recurse); if (error != 0) { if (robust) _mutex_leave_robust(curthread, mp); return (error); } if (cancel) _thr_cancel_enter2(curthread, 0); error = _thr_ucond_wait(&cvp->kcond, &mp->m_lock, abstime, CVWAIT_ABSTIME | CVWAIT_CLOCKID); if (cancel) _thr_cancel_leave(curthread, 0); /* * Note that PP mutex and ROBUST mutex may return * interesting error codes. */ if (error == 0) { error2 = _mutex_cv_lock(mp, recurse, true); } else if (error == EINTR || error == ETIMEDOUT) { error2 = _mutex_cv_lock(mp, recurse, true); /* * Do not do cancellation on EOWNERDEAD there. The * cancellation cleanup handler will use the protected * state and unlock the mutex without making the state * consistent and the state will be unrecoverable. */ if (error2 == 0 && cancel) { if (robust) { _mutex_leave_robust(curthread, mp); robust = false; } _thr_testcancel(curthread); } if (error == EINTR) error = 0; } else { /* We know that it didn't unlock the mutex. */ _mutex_cv_attach(mp, recurse); if (cancel) { if (robust) { _mutex_leave_robust(curthread, mp); robust = false; } _thr_testcancel(curthread); } error2 = 0; } if (robust) _mutex_leave_robust(curthread, mp); 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; struct sleepqueue *sq; int deferred, error, error2, recurse; curthread = _get_curthread(); if (curthread->wchan != NULL) PANIC("thread %p was already on queue.", curthread); 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; deferred = 0; (void)_mutex_cv_unlock(mp, &recurse, &deferred); curthread->mutex_obj = mp; _sleepq_add(cvp, curthread); for(;;) { _thr_clear_wake(curthread); _sleepq_unlock(cvp); if (deferred) { deferred = 0; if ((mp->m_lock.m_owner & UMUTEX_CONTESTED) == 0) (void)_umtx_op_err(&mp->m_lock, UMTX_OP_MUTEX_WAKE2, mp->m_lock.m_flags, 0, 0); } if (curthread->nwaiter_defer > 0) { _thr_wake_all(curthread->defer_waiters, curthread->nwaiter_defer); curthread->nwaiter_defer = 0; } if (cancel) _thr_cancel_enter2(curthread, 0); error = _thr_sleep(curthread, cvp->kcond.c_clockid, abstime); if (cancel) _thr_cancel_leave(curthread, 0); _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; error2 = _mutex_cv_lock(mp, recurse, false); if (!THR_IN_CRITICAL(curthread)) _pthread_exit(PTHREAD_CANCELED); else /* this should not happen */ return (error2); } else if (error == ETIMEDOUT) { sq = _sleepq_lookup(cvp); cvp->__has_user_waiters = _sleepq_remove(sq, curthread); break; } } _sleepq_unlock(cvp); curthread->mutex_obj = NULL; error2 = _mutex_cv_lock(mp, recurse, false); if (error == 0) error = error2; 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 if (*mutex == THR_PSHARED_PTR) { mp = __thr_pshared_offpage(mutex, 0); if (mp == NULL) return (EINVAL); } else { 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 || CV_PSHARED(cvp)) 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) +_thr_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 * __restrict cond, +__thr_cond_wait(pthread_cond_t * __restrict cond, pthread_mutex_t * __restrict mutex) { return (cond_wait_common(cond, mutex, NULL, 1)); } int -_pthread_cond_timedwait(pthread_cond_t * __restrict cond, +_thr_cond_timedwait(pthread_cond_t * __restrict cond, pthread_mutex_t * __restrict mutex, const struct timespec * __restrict 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(&cvp->kcond); 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 (PMUTEX_OWNER_ID(mp) == TID(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_DEFERRED; } 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 (PMUTEX_OWNER_ID(mp) == TID(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_DEFERRED; } 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(&cvp->kcond); 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) +_thr_cond_signal(pthread_cond_t * cond) { return (cond_signal_common(cond)); } int -_pthread_cond_broadcast(pthread_cond_t * cond) +_thr_cond_broadcast(pthread_cond_t * cond) { return (cond_broadcast_common(cond)); } Index: head/lib/libthr/thread/thr_detach.c =================================================================== --- head/lib/libthr/thread/thr_detach.c (revision 350480) +++ head/lib/libthr/thread/thr_detach.c (revision 350481) @@ -1,69 +1,70 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2005 David Xu * Copyright (C) 2003 Daniel M. Eischen * 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. */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include #include "un-namespace.h" #include "thr_private.h" -__weak_reference(_pthread_detach, pthread_detach); +__weak_reference(_thr_detach, pthread_detach); +__weak_reference(_thr_detach, _pthread_detach); int -_pthread_detach(pthread_t pthread) +_thr_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); } Index: head/lib/libthr/thread/thr_equal.c =================================================================== --- head/lib/libthr/thread/thr_equal.c (revision 350480) +++ head/lib/libthr/thread/thr_equal.c (revision 350481) @@ -1,47 +1,48 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1995 John Birrell . * 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 __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include "un-namespace.h" #include "thr_private.h" -__weak_reference(_pthread_equal, pthread_equal); +__weak_reference(_thr_equal, pthread_equal); +__weak_reference(_thr_equal, _pthread_equal); int -_pthread_equal(pthread_t t1, pthread_t t2) +_thr_equal(pthread_t t1, pthread_t t2) { /* Compare the two thread pointers: */ return (t1 == t2); } Index: head/lib/libthr/thread/thr_exit.c =================================================================== --- head/lib/libthr/thread/thr_exit.c (revision 350480) +++ head/lib/libthr/thread/thr_exit.c (revision 350481) @@ -1,329 +1,330 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1995-1998 John Birrell * 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 __FBSDID("$FreeBSD$"); #include "namespace.h" #include #ifdef _PTHREAD_FORCED_UNWIND #include #endif #include #include #include #include #include #include #include "un-namespace.h" #include "libc_private.h" #include "thr_private.h" static void exit_thread(void) __dead2; -__weak_reference(_pthread_exit, pthread_exit); +__weak_reference(_Tthr_exit, pthread_exit); +__weak_reference(_Tthr_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 dli; void *handle; void *forcedunwind, *getcfa; if (inited) return; handle = RTLD_DEFAULT; if ((forcedunwind = dlsym(handle, "_Unwind_ForcedUnwind")) != NULL) { if (dladdr(forcedunwind, &dli)) { /* * Make sure the address is always valid by holding the library, * also assume functions are in same library. */ if ((handle = dlopen(dli.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 __unused, struct _Unwind_Exception *e __unused) { /* * 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 __unused, _Unwind_Action actions, int64_t exc_class __unused, struct _Unwind_Exception *exc_obj __unused, struct _Unwind_Context *context, void *stop_parameter __unused) { 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) { /* Tell libc that it should call non-trivial TLS dtors. */ __cxa_thread_call_dtors(); 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_exitf(const char *fname, int lineno, const char *fmt, ...) { va_list ap; /* Write an error message to the standard error file descriptor: */ _thread_printf(STDERR_FILENO, "Fatal error '"); va_start(ap, fmt); _thread_vprintf(STDERR_FILENO, fmt, ap); va_end(ap); _thread_printf(STDERR_FILENO, "' at line %d in file %s (errno = %d)\n", lineno, fname, errno); abort(); } void _thread_exit(const char *fname, int lineno, const char *msg) { _thread_exitf(fname, lineno, "%s", msg); } void -_pthread_exit(void *status) +_Tthr_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) PANIC("Thread %p has called " "pthread_exit() from a destructor. POSIX 1003.1 " "1996 s16.2.5.2 does not allow this!", curthread); /* 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(); 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); } __cxa_thread_call_dtors(); exit_thread(); } #else while (curthread->cleanup != NULL) { __pthread_cleanup_pop_imp(1); } __cxa_thread_call_dtors(); exit_thread(); #endif /* _PTHREAD_FORCED_UNWIND */ } static void exit_thread(void) { struct pthread *curthread = _get_curthread(); free(curthread->name); curthread->name = NULL; /* 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 %p exits with resources held!", curthread); #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! */ } Index: head/lib/libthr/thread/thr_fork.c =================================================================== --- head/lib/libthr/thread/thr_fork.c (revision 350480) +++ head/lib/libthr/thread/thr_fork.c (revision 350481) @@ -1,273 +1,274 @@ /* * Copyright (c) 2005 David Xu * Copyright (c) 2003 Daniel Eischen * 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. */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1995-1998 John Birrell * 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 __FBSDID("$FreeBSD$"); #include #include "namespace.h" #include #include #include #include #include #include #include #include "un-namespace.h" #include "libc_private.h" #include "rtld_lock.h" #include "thr_private.h" -__weak_reference(_pthread_atfork, pthread_atfork); +__weak_reference(_thr_atfork, _pthread_atfork); +__weak_reference(_thr_atfork, pthread_atfork); int -_pthread_atfork(void (*prepare)(void), void (*parent)(void), +_thr_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(__thr_fork, _fork); pid_t __thr_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; __thr_malloc_prefork(curthread); _malloc_prefork(); __thr_pshared_atfork_pre(); _rtld_atfork_pre(rtld_locks); } else { was_threaded = 0; } /* * Fork a new process. * There is no easy way to pre-resolve the __sys_fork symbol * without performing the fork. Use the syscall(2) * indirection, the syscall symbol is resolved in * _thr_rtld_init() with side-effect free call. */ ret = syscall(SYS_fork); if (ret == 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; /* before thr_self() */ if (was_threaded) __thr_malloc_postfork(curthread); /* 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_pshared_atfork_post(); } _thr_setthreaded(0); /* reinitalize library. */ _libpthread_init(curthread); /* atfork is reinitialized by _libpthread_init()! */ _thr_rwl_rdlock(&_thr_atfork_lock); if (was_threaded) { _thr_setthreaded(1); _malloc_postfork(); _thr_setthreaded(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) { __thr_malloc_postfork(curthread); _rtld_atfork_post(rtld_locks); __thr_pshared_atfork_post(); _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 (ret); } Index: head/lib/libthr/thread/thr_getthreadid_np.c =================================================================== --- head/lib/libthr/thread/thr_getthreadid_np.c (revision 350480) +++ head/lib/libthr/thread/thr_getthreadid_np.c (revision 350481) @@ -1,51 +1,52 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2011 Jung-uk Kim * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must 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 __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include "un-namespace.h" #include "thr_private.h" -__weak_reference(_pthread_getthreadid_np, pthread_getthreadid_np); +__weak_reference(_thr_getthreadid_np, _pthread_getthreadid_np); +__weak_reference(_thr_getthreadid_np, pthread_getthreadid_np); /* * Provide the equivelant to AIX pthread_getthreadid_np() function. */ int -_pthread_getthreadid_np(void) +_thr_getthreadid_np(void) { struct pthread *curthread; _thr_check_init(); curthread = _get_curthread(); return (TID(curthread)); } Index: head/lib/libthr/thread/thr_init.c =================================================================== --- head/lib/libthr/thread/thr_init.c (revision 350480) +++ head/lib/libthr/thread/thr_init.c (revision 350481) @@ -1,499 +1,497 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 2003 Daniel M. Eischen * Copyright (c) 1995-1998 John Birrell * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by John Birrell. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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, .m_pshared = PTHREAD_PROCESS_PRIVATE, .m_robust = PTHREAD_MUTEX_STALLED, }; struct pthread_mutex_attr _pthread_mutexattr_adaptive_default = { .m_type = PTHREAD_MUTEX_ADAPTIVE_NP, .m_protocol = PTHREAD_PRIO_NONE, .m_ceiling = 0, .m_pshared = PTHREAD_PROCESS_PRIVATE, .m_robust = PTHREAD_MUTEX_STALLED, }; /* Default condition variable attributes: */ struct pthread_cond_attr _pthread_condattr_default = { .c_pshared = PTHREAD_PROCESS_PRIVATE, .c_clockid = CLOCK_REALTIME }; 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 _thr_queuefifo = 4; 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; struct umutex _suspend_all_lock = DEFAULT_UMUTEX; struct pthread *_single_thread; int _suspend_all_cycle; int _suspend_all_waiters; 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(_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 */ - {DUAL_ENTRY(_pthread_mutex_consistent)},/* PJT_MUTEX_CONSISTENT */ - {DUAL_ENTRY(_pthread_mutexattr_getrobust)},/* PJT_MUTEXATTR_GETROBUST */ - {DUAL_ENTRY(_pthread_mutexattr_setrobust)},/* PJT_MUTEXATTR_SETROBUST */ - {DUAL_ENTRY(_pthread_getthreadid_np)}, /* PJT_GETTHREADID_NP */ + [PJT_ATFORK] = {DUAL_ENTRY(_thr_atfork)}, + [PJT_ATTR_DESTROY] = {DUAL_ENTRY(_thr_attr_destroy)}, + [PJT_ATTR_GETDETACHSTATE] = {DUAL_ENTRY(_thr_attr_getdetachstate)}, + [PJT_ATTR_GETGUARDSIZE] = {DUAL_ENTRY(_thr_attr_getguardsize)}, + [PJT_ATTR_GETINHERITSCHED] = {DUAL_ENTRY(_thr_attr_getinheritsched)}, + [PJT_ATTR_GETSCHEDPARAM] = {DUAL_ENTRY(_thr_attr_getschedparam)}, + [PJT_ATTR_GETSCHEDPOLICY] = {DUAL_ENTRY(_thr_attr_getschedpolicy)}, + [PJT_ATTR_GETSCOPE] = {DUAL_ENTRY(_thr_attr_getscope)}, + [PJT_ATTR_GETSTACKADDR] = {DUAL_ENTRY(_thr_attr_getstackaddr)}, + [PJT_ATTR_GETSTACKSIZE] = {DUAL_ENTRY(_thr_attr_getstacksize)}, + [PJT_ATTR_INIT] = {DUAL_ENTRY(_thr_attr_init)}, + [PJT_ATTR_SETDETACHSTATE] = {DUAL_ENTRY(_thr_attr_setdetachstate)}, + [PJT_ATTR_SETGUARDSIZE] = {DUAL_ENTRY(_thr_attr_setguardsize)}, + [PJT_ATTR_SETINHERITSCHED] = {DUAL_ENTRY(_thr_attr_setinheritsched)}, + [PJT_ATTR_SETSCHEDPARAM] = {DUAL_ENTRY(_thr_attr_setschedparam)}, + [PJT_ATTR_SETSCHEDPOLICY] = {DUAL_ENTRY(_thr_attr_setschedpolicy)}, + [PJT_ATTR_SETSCOPE] = {DUAL_ENTRY(_thr_attr_setscope)}, + [PJT_ATTR_SETSTACKADDR] = {DUAL_ENTRY(_thr_attr_setstackaddr)}, + [PJT_ATTR_SETSTACKSIZE] = {DUAL_ENTRY(_thr_attr_setstacksize)}, + [PJT_CANCEL] = {DUAL_ENTRY(_thr_cancel)}, + [PJT_CLEANUP_POP] = {DUAL_ENTRY(_thr_cleanup_pop)}, + [PJT_CLEANUP_PUSH] = {DUAL_ENTRY(_thr_cleanup_push)}, + [PJT_COND_BROADCAST] = {DUAL_ENTRY(_thr_cond_broadcast)}, + [PJT_COND_DESTROY] = {DUAL_ENTRY(_thr_cond_destroy)}, + [PJT_COND_INIT] = {DUAL_ENTRY(_thr_cond_init)}, + [PJT_COND_SIGNAL] = {DUAL_ENTRY(_thr_cond_signal)}, + [PJT_COND_TIMEDWAIT] = {DUAL_ENTRY(_thr_cond_timedwait)}, + [PJT_COND_WAIT] = {(pthread_func_t)__thr_cond_wait, + (pthread_func_t)_thr_cond_wait}, + [PJT_DETACH] = {DUAL_ENTRY(_thr_detach)}, + [PJT_EQUAL] = {DUAL_ENTRY(_thr_equal)}, + [PJT_EXIT] = {DUAL_ENTRY(_Tthr_exit)}, + [PJT_GETSPECIFIC] = {DUAL_ENTRY(_thr_getspecific)}, + [PJT_JOIN] = {DUAL_ENTRY(_thr_join)}, + [PJT_KEY_CREATE] = {DUAL_ENTRY(_thr_key_create)}, + [PJT_KEY_DELETE] = {DUAL_ENTRY(_thr_key_delete)}, + [PJT_KILL] = {DUAL_ENTRY(_Tthr_kill)}, + [PJT_MAIN_NP] = {DUAL_ENTRY(_thr_main_np)}, + [PJT_MUTEXATTR_DESTROY] = {DUAL_ENTRY(_thr_mutexattr_destroy)}, + [PJT_MUTEXATTR_INIT] = {DUAL_ENTRY(_thr_mutexattr_init)}, + [PJT_MUTEXATTR_SETTYPE] = {DUAL_ENTRY(_thr_mutexattr_settype)}, + [PJT_MUTEX_DESTROY] = {DUAL_ENTRY(_thr_mutex_destroy)}, + [PJT_MUTEX_INIT] = {DUAL_ENTRY(__Tthr_mutex_init)}, + [PJT_MUTEX_LOCK] = {DUAL_ENTRY(__Tthr_mutex_lock)}, + [PJT_MUTEX_TRYLOCK] = {DUAL_ENTRY(__Tthr_mutex_trylock)}, + [PJT_MUTEX_UNLOCK] = {DUAL_ENTRY(_thr_mutex_unlock)}, + [PJT_ONCE] = {DUAL_ENTRY(_thr_once)}, + [PJT_RWLOCK_DESTROY] = {DUAL_ENTRY(_thr_rwlock_destroy)}, + [PJT_RWLOCK_INIT] = {DUAL_ENTRY(_thr_rwlock_init)}, + [PJT_RWLOCK_RDLOCK] = {DUAL_ENTRY(_Tthr_rwlock_rdlock)}, + [PJT_RWLOCK_TRYRDLOCK] = {DUAL_ENTRY(_Tthr_rwlock_tryrdlock)}, + [PJT_RWLOCK_TRYWRLOCK] = {DUAL_ENTRY(_Tthr_rwlock_trywrlock)}, + [PJT_RWLOCK_UNLOCK] = {DUAL_ENTRY(_Tthr_rwlock_unlock)}, + [PJT_RWLOCK_WRLOCK] = {DUAL_ENTRY(_Tthr_rwlock_wrlock)}, + [PJT_SELF] = {DUAL_ENTRY(_Tthr_self)}, + [PJT_SETCANCELSTATE] = {DUAL_ENTRY(_thr_setcancelstate)}, + [PJT_SETCANCELTYPE] = {DUAL_ENTRY(_thr_setcanceltype)}, + [PJT_SETSPECIFIC] = {DUAL_ENTRY(_thr_setspecific)}, + [PJT_SIGMASK] = {DUAL_ENTRY(_thr_sigmask)}, + [PJT_TESTCANCEL] = {DUAL_ENTRY(_Tthr_testcancel)}, + [PJT_CLEANUP_POP_IMP] = {DUAL_ENTRY(__thr_cleanup_pop_imp)}, + [PJT_CLEANUP_PUSH_IMP] = {DUAL_ENTRY(__thr_cleanup_push_imp)}, + [PJT_CANCEL_ENTER] = {DUAL_ENTRY(_thr_cancel_enter)}, + [PJT_CANCEL_LEAVE] = {DUAL_ENTRY(_thr_cancel_leave)}, + [PJT_MUTEX_CONSISTENT] = {DUAL_ENTRY(_Tthr_mutex_consistent)}, + [PJT_MUTEXATTR_GETROBUST] = {DUAL_ENTRY(_thr_mutexattr_getrobust)}, + [PJT_MUTEXATTR_SETROBUST] = {DUAL_ENTRY(_thr_mutexattr_setrobust)}, + [PJT_GETTHREADID_NP] = {DUAL_ENTRY(_thr_getthreadid_np)}, }; 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 first, dlopened; /* 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)); __thr_interpose_libc(); /* 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); } else { first = 0; } /* * 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; dlopened = _rtld_is_dlopened(&_thread_autoinit_dummy_decl) != 0; _thr_signal_init(dlopened); if (_thread_event_mask & TD_CREATE) _thr_report_creation(curthread, curthread); /* * Always use our rtld lock implementation. * It is faster because it postpones signal handlers * instead of calling sigprocmask(2). */ _thr_rtld_init(); } } /* * 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; int i; /* 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 queues */ for (i = 0; i < TMQ_NITEMS; i++) TAILQ_INIT(&thread->mq[i]); 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) { struct rlimit rlim; size_t len; int mib[2]; char *env, *env_bigstack, *env_splitstack; _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_umutex_init(&_suspend_all_lock); _thr_spinlock_init(); _thr_list_init(); _thr_wake_addr_init(); _sleepq_init(); _single_thread = NULL; _suspend_all_waiters = 0; /* * Avoid reinitializing some things if they don't need to be, * e.g. after a fork(). */ if (init_once == 0) { __thr_pshared_init(); __thr_malloc_init(); /* 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"); env_bigstack = getenv("LIBPTHREAD_BIGSTACK_MAIN"); env_splitstack = getenv("LIBPTHREAD_SPLITSTACK_MAIN"); if (env_bigstack != NULL || env_splitstack == NULL) { if (getrlimit(RLIMIT_STACK, &rlim) == -1) PANIC("Cannot get stack rlimit"); _thr_stack_initial = rlim.rlim_cur; } _thr_is_smp = sysconf(_SC_NPROCESSORS_CONF); if (_thr_is_smp == -1) PANIC("Cannot get _SC_NPROCESSORS_CONF"); _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); env = getenv("LIBPTHREAD_QUEUE_FIFO"); if (env) _thr_queuefifo = atoi(env); TAILQ_INIT(&_thr_atfork_list); } init_once = 1; } Index: head/lib/libthr/thread/thr_join.c =================================================================== --- head/lib/libthr/thread/thr_join.c (revision 350480) +++ head/lib/libthr/thread/thr_join.c (revision 350481) @@ -1,151 +1,152 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * 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 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. */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #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(_thr_join, pthread_join); +__weak_reference(_thr_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) +_thr_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); } Index: head/lib/libthr/thread/thr_kill.c =================================================================== --- head/lib/libthr/thread/thr_kill.c (revision 350480) +++ head/lib/libthr/thread/thr_kill.c (revision 350481) @@ -1,76 +1,77 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1997 John Birrell . * 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 __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include #include "un-namespace.h" #include "thr_private.h" -__weak_reference(_pthread_kill, pthread_kill); +__weak_reference(_Tthr_kill, _pthread_kill); +__weak_reference(_Tthr_kill, pthread_kill); int -_pthread_kill(pthread_t pthread, int sig) +_Tthr_kill(pthread_t pthread, int sig) { struct pthread *curthread; int ret; /* Check for invalid signal numbers: */ if (sig < 0 || sig > _SIG_MAXSIG) /* Invalid signal: */ return (EINVAL); curthread = _get_curthread(); /* * 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: */ if (curthread == pthread) { if (sig > 0) _thr_send_sig(pthread, sig); ret = 0; } else 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); } Index: head/lib/libthr/thread/thr_main_np.c =================================================================== --- head/lib/libthr/thread/thr_main_np.c (revision 350480) +++ head/lib/libthr/thread/thr_main_np.c (revision 350481) @@ -1,53 +1,54 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2001 Alfred Perlstein * Author: Alfred Perlstein * 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 __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include "un-namespace.h" #include "thr_private.h" -__weak_reference(_pthread_main_np, pthread_main_np); +__weak_reference(_thr_main_np, pthread_main_np); +__weak_reference(_thr_main_np, _pthread_main_np); /* * Provide the equivalent to Solaris thr_main() function. */ int -_pthread_main_np(void) +_thr_main_np(void) { if (!_thr_initial) return (-1); else return (_pthread_equal(_pthread_self(), _thr_initial) ? 1 : 0); } Index: head/lib/libthr/thread/thr_mutex.c =================================================================== --- head/lib/libthr/thread/thr_mutex.c (revision 350480) +++ head/lib/libthr/thread/thr_mutex.c (revision 350481) @@ -1,1193 +1,1194 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1995 John Birrell . * Copyright (c) 2006 David Xu . * Copyright (c) 2015, 2016 The FreeBSD Foundation * * All rights reserved. * * Portions of this software were developed by Konstantin Belousov * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by John Birrell. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include #include #include #include #include #include "un-namespace.h" #include "thr_private.h" _Static_assert(sizeof(struct pthread_mutex) <= PAGE_SIZE, "pthread_mutex is too large for off-page"); /* * 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_consistent(pthread_mutex_t *mutex); -int __pthread_mutex_init(pthread_mutex_t * __restrict mutex, - const pthread_mutexattr_t * __restrict 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 * __restrict mutex, const struct timespec * __restrict abstime); 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 *, bool, int *); static int mutex_lock_sleep(struct pthread *, pthread_mutex_t, const struct timespec *); static void mutex_init_robust(struct pthread *curthread); static int mutex_qidx(struct pthread_mutex *m); static bool is_robust_mutex(struct pthread_mutex *m); static bool is_pshared_mutex(struct pthread_mutex *m); -__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(__Tthr_mutex_init, pthread_mutex_init); +__weak_reference(__Tthr_mutex_init, __pthread_mutex_init); +__strong_reference(__Tthr_mutex_init, _pthread_mutex_init); +__weak_reference(__Tthr_mutex_lock, pthread_mutex_lock); +__weak_reference(__Tthr_mutex_lock, __pthread_mutex_lock); +__strong_reference(__Tthr_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); -__weak_reference(_pthread_mutex_consistent, pthread_mutex_consistent); -__strong_reference(_pthread_mutex_consistent, __pthread_mutex_consistent); +__weak_reference(__Tthr_mutex_trylock, pthread_mutex_trylock); +__weak_reference(__Tthr_mutex_trylock, __pthread_mutex_trylock); +__strong_reference(__Tthr_mutex_trylock, _pthread_mutex_trylock); +__weak_reference(_Tthr_mutex_consistent, pthread_mutex_consistent); +__weak_reference(_Tthr_mutex_consistent, _pthread_mutex_consistent); +__strong_reference(_Tthr_mutex_consistent, __pthread_mutex_consistent); /* 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(_thr_mutex_destroy, pthread_mutex_destroy); +__weak_reference(_thr_mutex_destroy, _pthread_mutex_destroy); +__weak_reference(_thr_mutex_unlock, pthread_mutex_unlock); +__weak_reference(_thr_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 void mutex_init_link(struct pthread_mutex *m) { #if defined(_PTHREADS_INVARIANTS) m->m_qe.tqe_prev = NULL; m->m_qe.tqe_next = NULL; m->m_pqe.tqe_prev = NULL; m->m_pqe.tqe_next = NULL; #endif } static void mutex_assert_is_owned(struct pthread_mutex *m __unused) { #if defined(_PTHREADS_INVARIANTS) if (__predict_false(m->m_qe.tqe_prev == NULL)) PANIC("mutex %p own %#x is not on list %p %p", m, m->m_lock.m_owner, m->m_qe.tqe_prev, m->m_qe.tqe_next); #endif } static void mutex_assert_not_owned(struct pthread *curthread __unused, struct pthread_mutex *m __unused) { #if defined(_PTHREADS_INVARIANTS) if (__predict_false(m->m_qe.tqe_prev != NULL || m->m_qe.tqe_next != NULL)) PANIC("mutex %p own %#x is on list %p %p", m, m->m_lock.m_owner, m->m_qe.tqe_prev, m->m_qe.tqe_next); if (__predict_false(is_robust_mutex(m) && (m->m_lock.m_rb_lnk != 0 || m->m_rb_prev != NULL || (is_pshared_mutex(m) && curthread->robust_list == (uintptr_t)&m->m_lock) || (!is_pshared_mutex(m) && curthread->priv_robust_list == (uintptr_t)&m->m_lock)))) PANIC( "mutex %p own %#x is on robust linkage %p %p head %p phead %p", m, m->m_lock.m_owner, (void *)m->m_lock.m_rb_lnk, m->m_rb_prev, (void *)curthread->robust_list, (void *)curthread->priv_robust_list); #endif } static bool is_pshared_mutex(struct pthread_mutex *m) { return ((m->m_lock.m_flags & USYNC_PROCESS_SHARED) != 0); } static bool is_robust_mutex(struct pthread_mutex *m) { return ((m->m_lock.m_flags & UMUTEX_ROBUST) != 0); } int _mutex_enter_robust(struct pthread *curthread, struct pthread_mutex *m) { #if defined(_PTHREADS_INVARIANTS) if (__predict_false(curthread->inact_mtx != 0)) PANIC("inact_mtx enter"); #endif if (!is_robust_mutex(m)) return (0); mutex_init_robust(curthread); curthread->inact_mtx = (uintptr_t)&m->m_lock; return (1); } void _mutex_leave_robust(struct pthread *curthread, struct pthread_mutex *m __unused) { #if defined(_PTHREADS_INVARIANTS) if (__predict_false(curthread->inact_mtx != (uintptr_t)&m->m_lock)) PANIC("inact_mtx leave"); #endif curthread->inact_mtx = 0; } static int mutex_check_attr(const struct pthread_mutex_attr *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); return (0); } static void mutex_init_robust(struct pthread *curthread) { struct umtx_robust_lists_params rb; if (curthread == NULL) curthread = _get_curthread(); if (curthread->robust_inited) return; rb.robust_list_offset = (uintptr_t)&curthread->robust_list; rb.robust_priv_list_offset = (uintptr_t)&curthread->priv_robust_list; rb.robust_inact_offset = (uintptr_t)&curthread->inact_mtx; _umtx_op(NULL, UMTX_OP_ROBUST_LISTS, sizeof(rb), &rb, NULL); curthread->robust_inited = 1; } static void mutex_init_body(struct pthread_mutex *pmutex, const struct pthread_mutex_attr *attr) { pmutex->m_flags = attr->m_type; 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 (attr->m_pshared == PTHREAD_PROCESS_SHARED) pmutex->m_lock.m_flags |= USYNC_PROCESS_SHARED; if (attr->m_robust == PTHREAD_MUTEX_ROBUST) { mutex_init_robust(NULL); pmutex->m_lock.m_flags |= UMUTEX_ROBUST; } 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; } } 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; int error; if (mutex_attr == NULL) { attr = &_pthread_mutexattr_default; } else { attr = mutex_attr; error = mutex_check_attr(attr); if (error != 0) return (error); } if ((pmutex = (pthread_mutex_t) calloc_cb(1, sizeof(struct pthread_mutex))) == NULL) return (ENOMEM); mutex_init_body(pmutex, attr); *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, __thr_calloc); else if (*mutex == THR_ADAPTIVE_MUTEX_INITIALIZER) ret = mutex_init(mutex, &_pthread_mutexattr_adaptive_default, __thr_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->mq[mutex_qidx(m)], 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; } static void shared_mutex_init(struct pthread_mutex *pmtx, const struct pthread_mutex_attr *mutex_attr) { static const struct pthread_mutex_attr foobar_mutex_attr = { .m_type = PTHREAD_MUTEX_DEFAULT, .m_protocol = PTHREAD_PRIO_NONE, .m_ceiling = 0, .m_pshared = PTHREAD_PROCESS_SHARED, .m_robust = PTHREAD_MUTEX_STALLED, }; bool done; /* * Hack to allow multiple pthread_mutex_init() calls on the * same process-shared mutex. We rely on kernel allocating * zeroed offpage for the mutex, i.e. the * PMUTEX_INITSTAGE_ALLOC value must be zero. */ for (done = false; !done;) { switch (pmtx->m_ps) { case PMUTEX_INITSTAGE_DONE: atomic_thread_fence_acq(); done = true; break; case PMUTEX_INITSTAGE_ALLOC: if (atomic_cmpset_int(&pmtx->m_ps, PMUTEX_INITSTAGE_ALLOC, PMUTEX_INITSTAGE_BUSY)) { if (mutex_attr == NULL) mutex_attr = &foobar_mutex_attr; mutex_init_body(pmtx, mutex_attr); atomic_store_rel_int(&pmtx->m_ps, PMUTEX_INITSTAGE_DONE); done = true; } break; case PMUTEX_INITSTAGE_BUSY: _pthread_yield(); break; default: PANIC("corrupted offpage"); break; } } } int -__pthread_mutex_init(pthread_mutex_t * __restrict mutex, +__Tthr_mutex_init(pthread_mutex_t * __restrict mutex, const pthread_mutexattr_t * __restrict mutex_attr) { struct pthread_mutex *pmtx; int ret; if (mutex_attr != NULL) { ret = mutex_check_attr(*mutex_attr); if (ret != 0) return (ret); } if (mutex_attr == NULL || (*mutex_attr)->m_pshared == PTHREAD_PROCESS_PRIVATE) { __thr_malloc_init(); return (mutex_init(mutex, mutex_attr ? *mutex_attr : NULL, __thr_calloc)); } pmtx = __thr_pshared_offpage(__DECONST(void *, mutex), 1); if (pmtx == NULL) return (EFAULT); *mutex = THR_PSHARED_PTR; shared_mutex_init(pmtx, *mutex_attr); return (0); } /* 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, .m_pshared = PTHREAD_PROCESS_PRIVATE, .m_robust = PTHREAD_MUTEX_STALLED, }; int ret; ret = mutex_init(mutex, &attr, calloc_cb); if (ret == 0) (*mutex)->m_flags |= PMUTEX_FLAG_PRIVATE; return (ret); } /* * Fix mutex ownership for child process. * * Process private mutex ownership is transmitted from the forking * thread to the child process. * * Process shared mutex should not be inherited because owner is * forking thread which is in parent process, they are removed from * the owned mutex list. */ static void queue_fork(struct pthread *curthread, struct mutex_queue *q, struct mutex_queue *qp, uint bit) { struct pthread_mutex *m; TAILQ_INIT(q); TAILQ_FOREACH(m, qp, m_pqe) { TAILQ_INSERT_TAIL(q, m, m_qe); m->m_lock.m_owner = TID(curthread) | bit; } } void _mutex_fork(struct pthread *curthread) { queue_fork(curthread, &curthread->mq[TMQ_NORM], &curthread->mq[TMQ_NORM_PRIV], 0); queue_fork(curthread, &curthread->mq[TMQ_NORM_PP], &curthread->mq[TMQ_NORM_PP_PRIV], UMUTEX_CONTESTED); queue_fork(curthread, &curthread->mq[TMQ_ROBUST_PP], &curthread->mq[TMQ_ROBUST_PP_PRIV], UMUTEX_CONTESTED); curthread->robust_list = 0; } int -_pthread_mutex_destroy(pthread_mutex_t *mutex) +_thr_mutex_destroy(pthread_mutex_t *mutex) { pthread_mutex_t m, m1; int ret; m = *mutex; if (m < THR_MUTEX_DESTROYED) { ret = 0; } else if (m == THR_MUTEX_DESTROYED) { ret = EINVAL; } else { if (m == THR_PSHARED_PTR) { m1 = __thr_pshared_offpage(mutex, 0); if (m1 != NULL) { mutex_assert_not_owned(_get_curthread(), m1); __thr_pshared_destroy(mutex); } *mutex = THR_MUTEX_DESTROYED; return (0); } if (PMUTEX_OWNER_ID(m) != 0 && (uint32_t)m->m_lock.m_owner != UMUTEX_RB_NOTRECOV) { ret = EBUSY; } else { *mutex = THR_MUTEX_DESTROYED; mutex_assert_not_owned(_get_curthread(), m); __thr_free(m); ret = 0; } } return (ret); } static int mutex_qidx(struct pthread_mutex *m) { if ((m->m_lock.m_flags & UMUTEX_PRIO_PROTECT) == 0) return (TMQ_NORM); return (is_robust_mutex(m) ? TMQ_ROBUST_PP : TMQ_NORM_PP); } /* * Both enqueue_mutex() and dequeue_mutex() operate on the * thread-private linkage of the locked mutexes and on the robust * linkage. * * Robust list, as seen by kernel, must be consistent even in the case * of thread termination at arbitrary moment. Since either enqueue or * dequeue for list walked by kernel consists of rewriting a single * forward pointer, it is safe. On the other hand, rewrite of the * back pointer is not atomic WRT the forward one, but kernel does not * care. */ static void enqueue_mutex(struct pthread *curthread, struct pthread_mutex *m, int error) { struct pthread_mutex *m1; uintptr_t *rl; int qidx; /* Add to the list of owned mutexes: */ if (error != EOWNERDEAD) mutex_assert_not_owned(curthread, m); qidx = mutex_qidx(m); TAILQ_INSERT_TAIL(&curthread->mq[qidx], m, m_qe); if (!is_pshared_mutex(m)) TAILQ_INSERT_TAIL(&curthread->mq[qidx + 1], m, m_pqe); if (is_robust_mutex(m)) { rl = is_pshared_mutex(m) ? &curthread->robust_list : &curthread->priv_robust_list; m->m_rb_prev = NULL; if (*rl != 0) { m1 = __containerof((void *)*rl, struct pthread_mutex, m_lock); m->m_lock.m_rb_lnk = (uintptr_t)&m1->m_lock; m1->m_rb_prev = m; } else { m1 = NULL; m->m_lock.m_rb_lnk = 0; } *rl = (uintptr_t)&m->m_lock; } } static void dequeue_mutex(struct pthread *curthread, struct pthread_mutex *m) { struct pthread_mutex *mp, *mn; int qidx; mutex_assert_is_owned(m); qidx = mutex_qidx(m); if (is_robust_mutex(m)) { mp = m->m_rb_prev; if (mp == NULL) { if (is_pshared_mutex(m)) { curthread->robust_list = m->m_lock.m_rb_lnk; } else { curthread->priv_robust_list = m->m_lock.m_rb_lnk; } } else { mp->m_lock.m_rb_lnk = m->m_lock.m_rb_lnk; } if (m->m_lock.m_rb_lnk != 0) { mn = __containerof((void *)m->m_lock.m_rb_lnk, struct pthread_mutex, m_lock); mn->m_rb_prev = m->m_rb_prev; } m->m_lock.m_rb_lnk = 0; m->m_rb_prev = NULL; } TAILQ_REMOVE(&curthread->mq[qidx], m, m_qe); if (!is_pshared_mutex(m)) TAILQ_REMOVE(&curthread->mq[qidx + 1], m, m_pqe); if ((m->m_lock.m_flags & UMUTEX_PRIO_PROTECT) != 0) set_inherited_priority(curthread, m); mutex_init_link(m); } static int check_and_init_mutex(pthread_mutex_t *mutex, struct pthread_mutex **m) { int ret; *m = *mutex; ret = 0; if (*m == THR_PSHARED_PTR) { *m = __thr_pshared_offpage(mutex, 0); if (*m == NULL) ret = EINVAL; else shared_mutex_init(*m, NULL); } else if (__predict_false(*m <= THR_MUTEX_DESTROYED)) { if (*m == THR_MUTEX_DESTROYED) { ret = EINVAL; } else { ret = init_static(_get_curthread(), mutex); if (ret == 0) *m = *mutex; } } return (ret); } int -__pthread_mutex_trylock(pthread_mutex_t *mutex) +__Tthr_mutex_trylock(pthread_mutex_t *mutex) { struct pthread *curthread; struct pthread_mutex *m; uint32_t id; int ret, robust; ret = check_and_init_mutex(mutex, &m); if (ret != 0) return (ret); curthread = _get_curthread(); id = TID(curthread); if (m->m_flags & PMUTEX_FLAG_PRIVATE) THR_CRITICAL_ENTER(curthread); robust = _mutex_enter_robust(curthread, m); ret = _thr_umutex_trylock(&m->m_lock, id); if (__predict_true(ret == 0) || ret == EOWNERDEAD) { enqueue_mutex(curthread, m, ret); if (ret == EOWNERDEAD) m->m_lock.m_flags |= UMUTEX_NONCONSISTENT; } else if (PMUTEX_OWNER_ID(m) == id) { ret = mutex_self_trylock(m); } /* else {} */ if (robust) _mutex_leave_robust(curthread, m); if (ret != 0 && ret != EOWNERDEAD && (m->m_flags & PMUTEX_FLAG_PRIVATE) != 0) THR_CRITICAL_LEAVE(curthread); return (ret); } static int mutex_lock_sleep(struct pthread *curthread, struct pthread_mutex *m, const struct timespec *abstime) { uint32_t id, owner; int count, ret; id = TID(curthread); if (PMUTEX_OWNER_ID(m) == id) return (mutex_self_lock(m, abstime)); /* * 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 | UMUTEX_ROBUST | UMUTEX_NONCONSISTENT)) != 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 || ret == EOWNERDEAD) { enqueue_mutex(curthread, m, ret); if (ret == EOWNERDEAD) m->m_lock.m_flags |= UMUTEX_NONCONSISTENT; } return (ret); } static inline int mutex_lock_common(struct pthread_mutex *m, const struct timespec *abstime, bool cvattach, bool rb_onlist) { struct pthread *curthread; int ret, robust; robust = 0; /* pacify gcc */ curthread = _get_curthread(); if (!cvattach && m->m_flags & PMUTEX_FLAG_PRIVATE) THR_CRITICAL_ENTER(curthread); if (!rb_onlist) robust = _mutex_enter_robust(curthread, m); ret = _thr_umutex_trylock2(&m->m_lock, TID(curthread)); if (ret == 0 || ret == EOWNERDEAD) { enqueue_mutex(curthread, m, ret); if (ret == EOWNERDEAD) m->m_lock.m_flags |= UMUTEX_NONCONSISTENT; } else { ret = mutex_lock_sleep(curthread, m, abstime); } if (!rb_onlist && robust) _mutex_leave_robust(curthread, m); if (ret != 0 && ret != EOWNERDEAD && (m->m_flags & PMUTEX_FLAG_PRIVATE) != 0 && !cvattach) THR_CRITICAL_LEAVE(curthread); return (ret); } int -__pthread_mutex_lock(pthread_mutex_t *mutex) +__Tthr_mutex_lock(pthread_mutex_t *mutex) { struct pthread_mutex *m; int ret; _thr_check_init(); ret = check_and_init_mutex(mutex, &m); if (ret == 0) ret = mutex_lock_common(m, NULL, false, false); return (ret); } int __pthread_mutex_timedlock(pthread_mutex_t * __restrict mutex, const struct timespec * __restrict abstime) { struct pthread_mutex *m; int ret; _thr_check_init(); ret = check_and_init_mutex(mutex, &m); if (ret == 0) ret = mutex_lock_common(m, abstime, false, false); return (ret); } int -_pthread_mutex_unlock(pthread_mutex_t *mutex) +_thr_mutex_unlock(pthread_mutex_t *mutex) { struct pthread_mutex *mp; if (*mutex == THR_PSHARED_PTR) { mp = __thr_pshared_offpage(mutex, 0); if (mp == NULL) return (EINVAL); shared_mutex_init(mp, NULL); } else { mp = *mutex; } return (mutex_unlock_common(mp, false, NULL)); } int _mutex_cv_lock(struct pthread_mutex *m, int count, bool rb_onlist) { int error; error = mutex_lock_common(m, NULL, true, rb_onlist); if (error == 0 || error == EOWNERDEAD) m->m_count = count; return (error); } int _mutex_cv_unlock(struct pthread_mutex *m, int *count, int *defer) { /* * Clear the count in case this is a recursive mutex. */ *count = m->m_count; m->m_count = 0; (void)mutex_unlock_common(m, true, defer); return (0); } int _mutex_cv_attach(struct pthread_mutex *m, int count) { struct pthread *curthread; curthread = _get_curthread(); enqueue_mutex(curthread, m, 0); m->m_count = count; return (0); } int _mutex_cv_detach(struct pthread_mutex *mp, int *recurse) { struct pthread *curthread; int deferred, error; curthread = _get_curthread(); 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_DEFERRED) != 0) { deferred = 1; mp->m_flags &= ~PMUTEX_FLAG_DEFERRED; } else deferred = 0; if (deferred) { _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: case PTHREAD_MUTEX_ADAPTIVE_NP: 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, bool cv, int *mtx_defer) { struct pthread *curthread; uint32_t id; int deferred, error, private, robust; if (__predict_false(m <= THR_MUTEX_DESTROYED)) { if (m == THR_MUTEX_DESTROYED) return (EINVAL); return (EPERM); } curthread = _get_curthread(); id = TID(curthread); /* * Check if the running thread is not the owner of the mutex. */ if (__predict_false(PMUTEX_OWNER_ID(m) != id)) return (EPERM); error = 0; private = (m->m_flags & PMUTEX_FLAG_PRIVATE) != 0; 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_DEFERRED) != 0) { deferred = 1; m->m_flags &= ~PMUTEX_FLAG_DEFERRED; } else deferred = 0; robust = _mutex_enter_robust(curthread, m); dequeue_mutex(curthread, m); error = _thr_umutex_unlock2(&m->m_lock, id, mtx_defer); if (deferred) { if (mtx_defer == NULL) { _thr_wake_all(curthread->defer_waiters, curthread->nwaiter_defer); curthread->nwaiter_defer = 0; } else *mtx_defer = 1; } if (robust) _mutex_leave_robust(curthread, m); } if (!cv && private) THR_CRITICAL_LEAVE(curthread); return (error); } int _pthread_mutex_getprioceiling(const pthread_mutex_t * __restrict mutex, int * __restrict prioceiling) { struct pthread_mutex *m; if (*mutex == THR_PSHARED_PTR) { m = __thr_pshared_offpage(__DECONST(void *, mutex), 0); if (m == NULL) return (EINVAL); shared_mutex_init(m, NULL); } else { m = *mutex; if (m <= THR_MUTEX_DESTROYED) return (EINVAL); } if ((m->m_lock.m_flags & UMUTEX_PRIO_PROTECT) == 0) return (EINVAL); *prioceiling = m->m_lock.m_ceilings[0]; return (0); } int _pthread_mutex_setprioceiling(pthread_mutex_t * __restrict mutex, int ceiling, int * __restrict old_ceiling) { struct pthread *curthread; struct pthread_mutex *m, *m1, *m2; struct mutex_queue *q, *qp; int qidx, ret; if (*mutex == THR_PSHARED_PTR) { m = __thr_pshared_offpage(mutex, 0); if (m == NULL) return (EINVAL); shared_mutex_init(m, NULL); } else { m = *mutex; if (m <= THR_MUTEX_DESTROYED) return (EINVAL); } if ((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); curthread = _get_curthread(); if (PMUTEX_OWNER_ID(m) == TID(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)) { qidx = mutex_qidx(m); q = &curthread->mq[qidx]; qp = &curthread->mq[qidx + 1]; TAILQ_REMOVE(q, m, m_qe); if (!is_pshared_mutex(m)) TAILQ_REMOVE(qp, m, m_pqe); TAILQ_FOREACH(m2, q, m_qe) { if (m2->m_lock.m_ceilings[0] > (u_int)ceiling) { TAILQ_INSERT_BEFORE(m2, m, m_qe); if (!is_pshared_mutex(m)) { while (m2 != NULL && is_pshared_mutex(m2)) { m2 = TAILQ_PREV(m2, mutex_queue, m_qe); } if (m2 == NULL) { TAILQ_INSERT_HEAD(qp, m, m_pqe); } else { TAILQ_INSERT_BEFORE(m2, m, m_pqe); } } return (0); } } TAILQ_INSERT_TAIL(q, m, m_qe); if (!is_pshared_mutex(m)) TAILQ_INSERT_TAIL(qp, m, m_pqe); } } return (0); } int _pthread_mutex_getspinloops_np(pthread_mutex_t *mutex, int *count) { struct pthread_mutex *m; int ret; ret = check_and_init_mutex(mutex, &m); if (ret == 0) *count = m->m_spinloops; return (ret); } int __pthread_mutex_setspinloops_np(pthread_mutex_t *mutex, int count) { struct pthread_mutex *m; int ret; ret = check_and_init_mutex(mutex, &m); if (ret == 0) m->m_spinloops = count; return (ret); } int _pthread_mutex_getyieldloops_np(pthread_mutex_t *mutex, int *count) { struct pthread_mutex *m; int ret; ret = check_and_init_mutex(mutex, &m); if (ret == 0) *count = m->m_yieldloops; return (ret); } int __pthread_mutex_setyieldloops_np(pthread_mutex_t *mutex, int count) { struct pthread_mutex *m; int ret; ret = check_and_init_mutex(mutex, &m); if (ret == 0) m->m_yieldloops = count; return (0); } int _pthread_mutex_isowned_np(pthread_mutex_t *mutex) { struct pthread_mutex *m; if (*mutex == THR_PSHARED_PTR) { m = __thr_pshared_offpage(mutex, 0); if (m == NULL) return (0); shared_mutex_init(m, NULL); } else { m = *mutex; if (m <= THR_MUTEX_DESTROYED) return (0); } return (PMUTEX_OWNER_ID(m) == TID(_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 (PMUTEX_OWNER_ID(mp) != TID(curthread)) return (EPERM); return (0); } int -_pthread_mutex_consistent(pthread_mutex_t *mutex) +_Tthr_mutex_consistent(pthread_mutex_t *mutex) { struct pthread_mutex *m; struct pthread *curthread; if (*mutex == THR_PSHARED_PTR) { m = __thr_pshared_offpage(mutex, 0); if (m == NULL) return (EINVAL); shared_mutex_init(m, NULL); } else { m = *mutex; if (m <= THR_MUTEX_DESTROYED) return (EINVAL); } curthread = _get_curthread(); if ((m->m_lock.m_flags & (UMUTEX_ROBUST | UMUTEX_NONCONSISTENT)) != (UMUTEX_ROBUST | UMUTEX_NONCONSISTENT)) return (EINVAL); if (PMUTEX_OWNER_ID(m) != TID(curthread)) return (EPERM); m->m_lock.m_flags &= ~UMUTEX_NONCONSISTENT; return (0); } Index: head/lib/libthr/thread/thr_mutexattr.c =================================================================== --- head/lib/libthr/thread/thr_mutexattr.c (revision 350480) +++ head/lib/libthr/thread/thr_mutexattr.c (revision 350481) @@ -1,295 +1,300 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1996 Jeffrey Hsu . * 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. */ /* * Copyright (c) 1997 John Birrell . * 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 __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include #include #include #include "un-namespace.h" #include "thr_private.h" -__weak_reference(_pthread_mutexattr_init, pthread_mutexattr_init); +__weak_reference(_thr_mutexattr_init, pthread_mutexattr_init); +__weak_reference(_thr_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(_thr_mutexattr_settype, pthread_mutexattr_settype); +__weak_reference(_thr_mutexattr_settype, _pthread_mutexattr_settype); +__weak_reference(_thr_mutexattr_destroy, pthread_mutexattr_destroy); +__weak_reference(_thr_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); -__weak_reference(_pthread_mutexattr_getrobust, pthread_mutexattr_getrobust); -__weak_reference(_pthread_mutexattr_setrobust, pthread_mutexattr_setrobust); +__weak_reference(_thr_mutexattr_getrobust, pthread_mutexattr_getrobust); +__weak_reference(_thr_mutexattr_getrobust, _pthread_mutexattr_getrobust); +__weak_reference(_thr_mutexattr_setrobust, pthread_mutexattr_setrobust); +__weak_reference(_thr_mutexattr_setrobust, _pthread_mutexattr_setrobust); int -_pthread_mutexattr_init(pthread_mutexattr_t *attr) +_thr_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) +_thr_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(const pthread_mutexattr_t * __restrict attr, int * __restrict 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) +_thr_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 = (*attr)->m_pshared; return (0); } int _pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared) { if (attr == NULL || *attr == NULL || (pshared != PTHREAD_PROCESS_PRIVATE && pshared != PTHREAD_PROCESS_SHARED)) return (EINVAL); (*attr)->m_pshared = pshared; return (0); } int _pthread_mutexattr_getprotocol(const pthread_mutexattr_t * __restrict mattr, int * __restrict 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(const pthread_mutexattr_t * __restrict mattr, int * __restrict prioceiling) { int ret = 0; if (mattr == NULL || *mattr == NULL) ret = EINVAL; else if ((*mattr)->m_protocol != PTHREAD_PRIO_PROTECT) ret = EINVAL; else *prioceiling = (*mattr)->m_ceiling; return (ret); } int _pthread_mutexattr_setprioceiling(pthread_mutexattr_t *mattr, int prioceiling) { int ret = 0; if (mattr == NULL || *mattr == NULL) ret = EINVAL; else if ((*mattr)->m_protocol != PTHREAD_PRIO_PROTECT) ret = EINVAL; else (*mattr)->m_ceiling = prioceiling; return (ret); } int -_pthread_mutexattr_getrobust(pthread_mutexattr_t *mattr, int *robust) +_thr_mutexattr_getrobust(pthread_mutexattr_t *mattr, int *robust) { int ret; if (mattr == NULL || *mattr == NULL) { ret = EINVAL; } else { ret = 0; *robust = (*mattr)->m_robust; } return (ret); } int -_pthread_mutexattr_setrobust(pthread_mutexattr_t *mattr, int robust) +_thr_mutexattr_setrobust(pthread_mutexattr_t *mattr, int robust) { int ret; if (mattr == NULL || *mattr == NULL) { ret = EINVAL; } else if (robust != PTHREAD_MUTEX_STALLED && robust != PTHREAD_MUTEX_ROBUST) { ret = EINVAL; } else { ret = 0; (*mattr)->m_robust = robust; } return (ret); } Index: head/lib/libthr/thread/thr_once.c =================================================================== --- head/lib/libthr/thread/thr_once.c (revision 350480) +++ head/lib/libthr/thread/thr_once.c (revision 350481) @@ -1,105 +1,106 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * 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 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. */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include "un-namespace.h" #include "thr_private.h" -__weak_reference(_pthread_once, pthread_once); +__weak_reference(_thr_once, pthread_once); +__weak_reference(_thr_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; 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)) +_thr_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) { atomic_thread_fence_acq(); return (0); } if (state == ONCE_NEVER_DONE) { if (atomic_cmpset_int(&once_control->state, state, ONCE_IN_PROGRESS)) break; } else if (state == ONCE_IN_PROGRESS) { if (atomic_cmpset_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); } Index: head/lib/libthr/thread/thr_private.h =================================================================== --- head/lib/libthr/thread/thr_private.h (revision 350480) +++ head/lib/libthr/thread/thr_private.h (revision 350481) @@ -1,1019 +1,1099 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (C) 2005 Daniel M. Eischen * Copyright (c) 2005 David Xu * Copyright (c) 1995-1998 John Birrell . * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include __NULLABILITY_PRAGMA_PUSH #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) struct pthread; extern struct pthread *_thr_initial __hidden; #include "pthread_md.h" #include "thr_umtx.h" #include "thread_db.h" #ifdef _PTHREAD_FORCED_UNWIND #define _BSD_SOURCE #include #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(args...) _thread_exitf(__FILE__, __LINE__, ##args) /* 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) /* Magic cookie set for shared pthread locks and cv's pointers */ #define THR_PSHARED_PTR \ ((void *)(uintptr_t)((1ULL << (NBBY * sizeof(long) - 1)) | 1)) /* 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_DEFERRED 0x200 #define PMUTEX_TYPE(mtxflags) ((mtxflags) & PMUTEX_FLAG_TYPE_MASK) #define PMUTEX_OWNER_ID(m) ((m)->m_lock.m_owner & ~UMUTEX_CONTESTED) #define MAX_DEFER_WAITERS 50 /* * Values for pthread_mutex m_ps indicator. */ #define PMUTEX_INITSTAGE_ALLOC 0 #define PMUTEX_INITSTAGE_BUSY 1 #define PMUTEX_INITSTAGE_DONE 2 struct pthread_mutex { /* * Lock for accesses to this structure. */ struct umutex m_lock; int m_flags; int m_count; int m_spinloops; int m_yieldloops; int m_ps; /* pshared init stage */ /* * Link for all mutexes a thread currently owns, of the same * prio type. */ TAILQ_ENTRY(pthread_mutex) m_qe; /* Link for all private mutexes a thread currently owns. */ TAILQ_ENTRY(pthread_mutex) m_pqe; struct pthread_mutex *m_rb_prev; }; struct pthread_mutex_attr { enum pthread_mutextype m_type; int m_protocol; int m_ceiling; int m_pshared; int m_robust; }; #define PTHREAD_MUTEXATTR_STATIC_INITIALIZER \ { PTHREAD_MUTEX_DEFAULT, PTHREAD_PRIO_NONE, 0, MUTEX_FLAGS_PRIVATE, \ PTHREAD_MUTEX_STALLED } struct pthread_cond { __uint32_t __has_user_waiters; struct ucond kcond; }; 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 */ 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; uint32_t 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 * to preserve the ABI for M:N model in very early date (r131431). */ #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; /* deferred signal delivery is performed, do not reenter. */ int deferred_run; /* 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 */ /* * Queues of the owned mutexes. Private queue must have index * + 1 of the corresponding full queue. */ #define TMQ_NORM 0 /* NORMAL or PRIO_INHERIT normal */ #define TMQ_NORM_PRIV 1 /* NORMAL or PRIO_INHERIT normal priv */ #define TMQ_NORM_PP 2 /* PRIO_PROTECT normal mutexes */ #define TMQ_NORM_PP_PRIV 3 /* PRIO_PROTECT normal priv */ #define TMQ_ROBUST_PP 4 /* PRIO_PROTECT robust mutexes */ #define TMQ_ROBUST_PP_PRIV 5 /* PRIO_PROTECT robust priv */ #define TMQ_NITEMS 6 struct mutex_queue mq[TMQ_NITEMS]; 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; int robust_inited; uintptr_t robust_list; uintptr_t priv_robust_list; uintptr_t inact_mtx; /* 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; /* pthread_set/get_name_np */ char *name; }; #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) #ifndef __LIBC_ISTHREADED_DECLARED #define __LIBC_ISTHREADED_DECLARED extern int __isthreaded; #endif /* * Global variables for the pthread kernel. */ extern char *_usrstack __hidden; /* For debugger */ extern int _libthr_debug; extern int _thread_event_mask; extern struct pthread *_thread_last_event; /* Used in symbol lookup of libthread_db */ extern struct pthread_key _thread_keytable[]; /* 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 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; extern int _thr_queuefifo __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; extern struct umutex _suspend_all_lock __hidden; extern int _suspend_all_waiters __hidden; extern int _suspend_all_cycle __hidden; extern struct pthread *_single_thread __hidden; /* * Function prototype definitions. */ __BEGIN_DECLS void _thr_setthreaded(int) __hidden; int _mutex_cv_lock(struct pthread_mutex *, int, bool) __hidden; int _mutex_cv_unlock(struct pthread_mutex *, int *, int *) __hidden; int _mutex_cv_attach(struct pthread_mutex *, int) __hidden; int _mutex_cv_detach(struct pthread_mutex *, int *) __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; int _mutex_enter_robust(struct pthread *curthread, struct pthread_mutex *m) __hidden; void _mutex_leave_robust(struct pthread *curthread, struct pthread_mutex *m) __hidden; void _libpthread_init(struct pthread *) __hidden; struct pthread *_thr_alloc(struct pthread *) __hidden; void _thread_exit(const char *, int, const char *) __hidden __dead2; void _thread_exitf(const char *, int, const char *, ...) __hidden __dead2 __printflike(3, 4); 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_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 __printflike(2, 3); void _thread_vprintf(int, const char *, va_list) __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(int) __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_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_suspend_all_lock(struct pthread *) __hidden; void _thr_suspend_all_unlock(struct pthread *) __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; #ifndef _LIBC_PRIVATE_H_ void _pthread_cancel_enter(int maycancel); void _pthread_cancel_leave(int maycancel); #endif int _pthread_mutex_consistent(pthread_mutex_t * _Nonnull); int _pthread_mutexattr_getrobust(pthread_mutexattr_t * _Nonnull __restrict, int * _Nonnull __restrict); int _pthread_mutexattr_setrobust(pthread_mutexattr_t * _Nonnull, int); /* #include */ #ifdef _SYS_FCNTL_H_ #ifndef _LIBC_PRIVATE_H_ int __sys_fcntl(int, int, ...); int __sys_openat(int, const char *, int, ...); #endif /* _LIBC_PRIVATE_H_ */ #endif /* _SYS_FCNTL_H_ */ /* #include */ #ifdef _SIGNAL_H_ #ifndef _LIBC_PRIVATE_H_ int __sys_sigaction(int, const struct sigaction *, struct sigaction *); int __sys_sigprocmask(int, const sigset_t *, sigset_t *); int __sys_sigsuspend(const sigset_t *); int __sys_sigtimedwait(const sigset_t *, siginfo_t *, const struct timespec *); int __sys_sigwait(const sigset_t *, int *); int __sys_sigwaitinfo(const sigset_t *set, siginfo_t *info); #endif /* _LIBC_PRIVATE_H_ */ #endif /* _SYS_FCNTL_H_ */ /* #include */ #ifdef _TIME_H_ #ifndef _LIBC_PRIVATE_H_ int __sys_clock_nanosleep(clockid_t, int, const struct timespec *, struct timespec *); int __sys_nanosleep(const struct timespec *, struct timespec *); #endif /* _LIBC_PRIVATE_H_ */ #endif /* _SYS_FCNTL_H_ */ /* #include */ #ifdef _SYS_UCONTEXT_H_ #ifndef _LIBC_PRIVATE_H_ int __sys_setcontext(const ucontext_t *ucp); int __sys_swapcontext(ucontext_t *oucp, const ucontext_t *ucp); #endif /* _LIBC_PRIVATE_H_ */ #endif /* _SYS_FCNTL_H_ */ /* #include */ #ifdef _UNISTD_H_ #ifndef _LIBC_PRIVATE_H_ int __sys_close(int); int __sys_fork(void); ssize_t __sys_read(int, void *, size_t); #endif /* _LIBC_PRIVATE_H_ */ #endif /* _SYS_FCNTL_H_ */ 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; int _pthread_mutex_init_calloc_cb(pthread_mutex_t *mutex, void *(calloc_cb)(size_t, size_t)); 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); void __pthread_distribute_static_tls(size_t offset, void *src, size_t len, size_t total_len); int *__error_threaded(void) __hidden; void __thr_interpose_libc(void) __hidden; pid_t __thr_fork(void); int __thr_setcontext(const ucontext_t *ucp); int __thr_sigaction(int sig, const struct sigaction *act, struct sigaction *oact) __hidden; int __thr_sigprocmask(int how, const sigset_t *set, sigset_t *oset); int __thr_sigsuspend(const sigset_t * set); int __thr_sigtimedwait(const sigset_t *set, siginfo_t *info, const struct timespec * timeout); int __thr_sigwait(const sigset_t *set, int *sig); int __thr_sigwaitinfo(const sigset_t *set, siginfo_t *info); int __thr_swapcontext(ucontext_t *oucp, const ucontext_t *ucp); void __thr_map_stacks_exec(void); struct _spinlock; void __thr_spinunlock(struct _spinlock *lck); void __thr_spinlock(struct _spinlock *lck); struct tcb *_tcb_ctor(struct pthread *, int); void _tcb_dtor(struct tcb *); void __thr_pshared_init(void) __hidden; void *__thr_pshared_offpage(void *key, int doalloc) __hidden; void __thr_pshared_destroy(void *key) __hidden; void __thr_pshared_atfork_pre(void) __hidden; void __thr_pshared_atfork_post(void) __hidden; void *__thr_calloc(size_t num, size_t size); void __thr_free(void *cp); void *__thr_malloc(size_t nbytes); void *__thr_realloc(void *cp, size_t nbytes); void __thr_malloc_init(void); void __thr_malloc_prefork(struct pthread *curthread); void __thr_malloc_postfork(struct pthread *curthread); +int _thr_join(pthread_t, void **); +int _Tthr_kill(pthread_t, int); +int _thr_getthreadid_np(void); +void __thr_cleanup_push_imp(void (*)(void *), void *, + struct _pthread_cleanup_info *); +void __thr_cleanup_pop_imp(int); +void _thr_cleanup_push(void (*)(void *), void *); +void _thr_cleanup_pop(int); +void _thr_cancel_enter(struct pthread *); +void _thr_cancel_leave(struct pthread *, int); +void _Tthr_testcancel(void); +int _thr_cancel(pthread_t); +int _thr_atfork(void (*)(void), void (*)(void), void (*)(void)); +int _thr_attr_destroy(pthread_attr_t *); +int _thr_attr_get_np(pthread_t, pthread_attr_t *); +int _thr_attr_getdetachstate(const pthread_attr_t *, int *); +int _thr_attr_getguardsize(const pthread_attr_t * __restrict, + size_t * __restrict); +int _thr_attr_getinheritsched(const pthread_attr_t * __restrict, + int * __restrict); +int _thr_attr_getschedparam(const pthread_attr_t * __restrict, + struct sched_param * __restrict); +int _thr_attr_getschedpolicy(const pthread_attr_t * __restrict, + int * __restrict); +int _thr_attr_getscope(const pthread_attr_t * __restrict, int * __restrict); +int _thr_attr_getstackaddr(const pthread_attr_t *, void **); +int _thr_attr_getstacksize(const pthread_attr_t * __restrict, + size_t * __restrict); +int _thr_attr_init(pthread_attr_t *); +int _thr_attr_setdetachstate(pthread_attr_t *, int); +int _thr_attr_setguardsize(pthread_attr_t *, size_t); +int _thr_attr_setinheritsched(pthread_attr_t *, int); +int _thr_attr_setschedparam(pthread_attr_t * __restrict, + const struct sched_param * __restrict); +int _thr_attr_setschedpolicy(pthread_attr_t *, int); +int _thr_attr_setscope(pthread_attr_t *, int); +int _thr_attr_setstackaddr(pthread_attr_t *, void *); +int _thr_attr_setstacksize(pthread_attr_t *, size_t); +int _thr_cond_init(pthread_cond_t * __restrict, + const pthread_condattr_t * __restrict); +int _thr_cond_destroy(pthread_cond_t *); +int _thr_cond_timedwait(pthread_cond_t * __restrict, + pthread_mutex_t * __restrict, const struct timespec * __restrict); +int _thr_cond_signal(pthread_cond_t * cond); +int _thr_cond_broadcast(pthread_cond_t * cond); +int __thr_cond_wait(pthread_cond_t *, pthread_mutex_t *); +int _thr_cond_wait(pthread_cond_t *, pthread_mutex_t *); +int _thr_detach(pthread_t); +int _thr_equal(pthread_t, pthread_t); +void _Tthr_exit(void *); +int _thr_key_create(pthread_key_t *, void (*)(void *)); +int _thr_key_delete(pthread_key_t); +int _thr_setspecific(pthread_key_t, const void *); +void *_thr_getspecific(pthread_key_t); +int _thr_setcancelstate(int, int *); +int _thr_setcanceltype(int, int *); +pthread_t _Tthr_self(void); +int _thr_rwlock_init(pthread_rwlock_t *, const pthread_rwlockattr_t *); +int _thr_rwlock_destroy(pthread_rwlock_t *); +int _Tthr_rwlock_rdlock(pthread_rwlock_t *); +int _Tthr_rwlock_tryrdlock(pthread_rwlock_t *); +int _Tthr_rwlock_trywrlock(pthread_rwlock_t *); +int _Tthr_rwlock_wrlock(pthread_rwlock_t *); +int _Tthr_rwlock_unlock(pthread_rwlock_t *); +int _thr_once(pthread_once_t *, void (*)(void)); +int _thr_sigmask(int, const sigset_t *, sigset_t *); +int _thr_main_np(void); +int _thr_mutexattr_init(pthread_mutexattr_t *); +int _thr_mutexattr_destroy(pthread_mutexattr_t *); +int _thr_mutexattr_settype(pthread_mutexattr_t *, int); +int _thr_mutexattr_getrobust(pthread_mutexattr_t *, int *); +int _thr_mutexattr_setrobust(pthread_mutexattr_t *, int); +int __Tthr_mutex_init(pthread_mutex_t * __restrict, + const pthread_mutexattr_t * __restrict); +int _Tthr_mutex_consistent(pthread_mutex_t *); +int _thr_mutex_destroy(pthread_mutex_t *); +int _thr_mutex_unlock(pthread_mutex_t *); +int __Tthr_mutex_lock(pthread_mutex_t *); +int __Tthr_mutex_trylock(pthread_mutex_t *); + __END_DECLS __NULLABILITY_PRAGMA_POP #endif /* !_THR_PRIVATE_H */ Index: head/lib/libthr/thread/thr_rwlock.c =================================================================== --- head/lib/libthr/thread/thr_rwlock.c (revision 350480) +++ head/lib/libthr/thread/thr_rwlock.c (revision 350481) @@ -1,377 +1,384 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include "namespace.h" #include #include "un-namespace.h" #include "thr_private.h" _Static_assert(sizeof(struct pthread_rwlock) <= PAGE_SIZE, "pthread_rwlock is too large for off-page"); -__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(_thr_rwlock_destroy, pthread_rwlock_destroy); +__weak_reference(_thr_rwlock_destroy, _pthread_rwlock_destroy); +__weak_reference(_thr_rwlock_init, pthread_rwlock_init); +__weak_reference(_thr_rwlock_init, _pthread_rwlock_init); +__weak_reference(_Tthr_rwlock_rdlock, pthread_rwlock_rdlock); +__weak_reference(_Tthr_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(_Tthr_rwlock_tryrdlock, pthread_rwlock_tryrdlock); +__weak_reference(_Tthr_rwlock_tryrdlock, _pthread_rwlock_tryrdlock); +__weak_reference(_Tthr_rwlock_trywrlock, pthread_rwlock_trywrlock); +__weak_reference(_Tthr_rwlock_trywrlock, _pthread_rwlock_trywrlock); +__weak_reference(_Tthr_rwlock_unlock, pthread_rwlock_unlock); +__weak_reference(_Tthr_rwlock_unlock, _pthread_rwlock_unlock); +__weak_reference(_Tthr_rwlock_wrlock, pthread_rwlock_wrlock); +__weak_reference(_Tthr_rwlock_wrlock, _pthread_rwlock_wrlock); __weak_reference(_pthread_rwlock_timedwrlock, pthread_rwlock_timedwrlock); static int init_static(struct pthread *thread, pthread_rwlock_t *rwlock); static int init_rwlock(pthread_rwlock_t *rwlock, pthread_rwlock_t *rwlock_out); static int __always_inline check_and_init_rwlock(pthread_rwlock_t *rwlock, pthread_rwlock_t *rwlock_out) { if (__predict_false(*rwlock == THR_PSHARED_PTR || *rwlock <= THR_RWLOCK_DESTROYED)) return (init_rwlock(rwlock, rwlock_out)); *rwlock_out = *rwlock; return (0); } static int __noinline init_rwlock(pthread_rwlock_t *rwlock, pthread_rwlock_t *rwlock_out) { pthread_rwlock_t prwlock; int ret; if (*rwlock == THR_PSHARED_PTR) { prwlock = __thr_pshared_offpage(rwlock, 0); if (prwlock == NULL) return (EINVAL); } else if ((prwlock = *rwlock) <= THR_RWLOCK_DESTROYED) { if (prwlock == THR_RWLOCK_INITIALIZER) { ret = init_static(_get_curthread(), rwlock); if (ret != 0) return (ret); } else if (prwlock == THR_RWLOCK_DESTROYED) { return (EINVAL); } prwlock = *rwlock; } *rwlock_out = prwlock; return (0); } static int rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr) { pthread_rwlock_t prwlock; if (attr == NULL || *attr == NULL || (*attr)->pshared == PTHREAD_PROCESS_PRIVATE) { prwlock = calloc(1, sizeof(struct pthread_rwlock)); if (prwlock == NULL) return (ENOMEM); *rwlock = prwlock; } else { prwlock = __thr_pshared_offpage(rwlock, 1); if (prwlock == NULL) return (EFAULT); prwlock->lock.rw_flags |= USYNC_PROCESS_SHARED; *rwlock = THR_PSHARED_PTR; } return (0); } int -_pthread_rwlock_destroy (pthread_rwlock_t *rwlock) +_thr_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 if (prwlock == THR_PSHARED_PTR) { *rwlock = THR_RWLOCK_DESTROYED; __thr_pshared_destroy(rwlock); ret = 0; } 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) +_thr_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; ret = check_and_init_rwlock(rwlock, &prwlock); if (ret != 0) return (ret); 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) +_Tthr_rwlock_rdlock(pthread_rwlock_t *rwlock) { return (rwlock_rdlock_common(rwlock, NULL)); } int _pthread_rwlock_timedrdlock(pthread_rwlock_t * __restrict rwlock, const struct timespec * __restrict abstime) { return (rwlock_rdlock_common(rwlock, abstime)); } int -_pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock) +_Tthr_rwlock_tryrdlock(pthread_rwlock_t *rwlock) { struct pthread *curthread = _get_curthread(); pthread_rwlock_t prwlock; int flags; int ret; ret = check_and_init_rwlock(rwlock, &prwlock); if (ret != 0) return (ret); 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) +_Tthr_rwlock_trywrlock(pthread_rwlock_t *rwlock) { struct pthread *curthread = _get_curthread(); pthread_rwlock_t prwlock; int ret; ret = check_and_init_rwlock(rwlock, &prwlock); if (ret != 0) return (ret); ret = _thr_rwlock_trywrlock(&prwlock->lock); if (ret == 0) prwlock->owner = TID(curthread); return (ret); } static int -rwlock_wrlock_common (pthread_rwlock_t *rwlock, const struct timespec *abstime) +rwlock_wrlock_common(pthread_rwlock_t *rwlock, const struct timespec *abstime) { struct pthread *curthread = _get_curthread(); pthread_rwlock_t prwlock; int ret; ret = check_and_init_rwlock(rwlock, &prwlock); if (ret != 0) return (ret); /* * 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 = TID(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 = TID(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 = TID(curthread); break; } } return (ret); } int -_pthread_rwlock_wrlock (pthread_rwlock_t *rwlock) +_Tthr_rwlock_wrlock(pthread_rwlock_t *rwlock) { return (rwlock_wrlock_common (rwlock, NULL)); } int _pthread_rwlock_timedwrlock(pthread_rwlock_t * __restrict rwlock, const struct timespec * __restrict abstime) { return (rwlock_wrlock_common (rwlock, abstime)); } int -_pthread_rwlock_unlock(pthread_rwlock_t *rwlock) +_Tthr_rwlock_unlock(pthread_rwlock_t *rwlock) { struct pthread *curthread = _get_curthread(); pthread_rwlock_t prwlock; int ret; int32_t state; if (*rwlock == THR_PSHARED_PTR) { prwlock = __thr_pshared_offpage(rwlock, 0); if (prwlock == NULL) return (EINVAL); } else { 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 != TID(curthread))) return (EPERM); prwlock->owner = 0; } ret = _thr_rwlock_unlock(&prwlock->lock); if (ret == 0 && (state & URWLOCK_WRITE_OWNER) == 0) curthread->rdlock_count--; return (ret); } Index: head/lib/libthr/thread/thr_self.c =================================================================== --- head/lib/libthr/thread/thr_self.c (revision 350480) +++ head/lib/libthr/thread/thr_self.c (revision 350481) @@ -1,50 +1,51 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1995 John Birrell . * 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 __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include "un-namespace.h" #include "thr_private.h" -__weak_reference(_pthread_self, pthread_self); +__weak_reference(_Tthr_self, pthread_self); +__weak_reference(_Tthr_self, _pthread_self); pthread_t -_pthread_self(void) +_Tthr_self(void) { _thr_check_init(); /* Return the running thread pointer: */ return (_get_curthread()); } Index: head/lib/libthr/thread/thr_sig.c =================================================================== --- head/lib/libthr/thread/thr_sig.c (revision 350480) +++ head/lib/libthr/thread/thr_sig.c (revision 350481) @@ -1,763 +1,764 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * 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 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. */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include #include #include #include #include #include #include #include "un-namespace.h" #include "libc_private.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 inline struct usigaction * __libc_sigaction_slot(int signo) { return (&_thr_sigact[signo - 1]); } 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 _sigtimedwait(const sigset_t *set, siginfo_t *info, const struct timespec * timeout); int _sigwaitinfo(const sigset_t *set, siginfo_t *info); int _sigwait(const sigset_t *set, int *sig); 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; ucontext_t *ucp; struct sigaction act; struct usigaction *usa; int err; err = errno; curthread = _get_curthread(); ucp = _ucp; usa = __libc_sigaction_slot(sig); _thr_rwl_rdlock(&usa->lock); act = usa->sigact; _thr_rwl_unlock(&usa->lock); errno = err; curthread->deferred_run = 0; /* * 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, there 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; syscall(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 by 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; int uc_len; if (__predict_true(curthread->deferred_siginfo.si_signo == 0 || curthread->deferred_run)) return; curthread->deferred_run = 1; uc_len = __getcontextx_size(); uc = alloca(uc_len); getcontext(uc); if (curthread->deferred_siginfo.si_signo == 0) { curthread->deferred_run = 0; return; } __fillcontextx2((char *)uc); 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; 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 == _single_thread) 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) != 0) { 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); } THR_UMUTEX_UNLOCK(curthread, &(curthread)->lock); curthread->critical_count--; _thr_signal_unblock(curthread); } void _thr_signal_init(int dlopened) { struct sigaction act, nact, oact; struct usigaction *usa; sigset_t oldset; int sig, error; if (dlopened) { __sys_sigprocmask(SIG_SETMASK, &_thr_maskset, &oldset); for (sig = 1; sig <= _SIG_MAXSIG; sig++) { if (sig == SIGCANCEL) continue; error = __sys_sigaction(sig, NULL, &oact); if (error == -1 || oact.sa_handler == SIG_DFL || oact.sa_handler == SIG_IGN) continue; usa = __libc_sigaction_slot(sig); usa->sigact = oact; nact = oact; remove_thr_signals(&usa->sigact.sa_mask); nact.sa_flags &= ~SA_NODEFER; nact.sa_flags |= SA_SIGINFO; nact.sa_sigaction = thr_sighandler; nact.sa_mask = _thr_maskset; (void)__sys_sigaction(sig, &nact, NULL); } __sys_sigprocmask(SIG_SETMASK, &oldset, NULL); } /* 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 __unused) { #if 0 struct pthread *curthread = _get_curthread(); struct urwlock *rwlp; struct sigaction *actp; struct usigaction *usa; struct sigaction kact; void (*handler)(int); int sig; _thr_signal_block(curthread); for (sig = 1; sig <= _SIG_MAXSIG; sig++) { usa = __libc_sigaction_slot(sig); actp = &usa->sigact; retry: handler = actp->sa_handler; if (handler != SIG_DFL && handler != SIG_IGN && __elf_phdr_match_addr(phdr_info, handler)) { rwlp = &usa->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(&__libc_sigaction_slot(i)->lock); } void _thr_signal_postfork(void) { int i; for (i = 1; i <= _SIG_MAXSIG; ++i) _thr_rwl_unlock(&__libc_sigaction_slot(i)->lock); } void _thr_signal_postfork_child(void) { int i; for (i = 1; i <= _SIG_MAXSIG; ++i) { bzero(&__libc_sigaction_slot(i) -> lock, sizeof(struct urwlock)); } } void _thr_signal_deinit(void) { } int __thr_sigaction(int sig, const struct sigaction *act, struct sigaction *oact) { struct sigaction newact, oldact, oldact2; sigset_t oldset; struct usigaction *usa; int ret, err; if (!_SIG_VALID(sig) || sig == SIGCANCEL) { errno = EINVAL; return (-1); } ret = 0; err = 0; usa = __libc_sigaction_slot(sig); __sys_sigprocmask(SIG_SETMASK, &_thr_maskset, &oldset); _thr_rwl_wrlock(&usa->lock); if (act != NULL) { oldact2 = usa->sigact; newact = *act; /* * if a new sig handler is SIG_DFL or SIG_IGN, * don't remove old handler from __libc_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) { usa->sigact = newact; remove_thr_signals(&usa->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 */ } ret = __sys_sigaction(sig, &newact, &oldact); if (ret == -1) { err = errno; usa->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 = usa->sigact; } _thr_rwl_unlock(&usa->lock); __sys_sigprocmask(SIG_SETMASK, &oldset, NULL); if (ret == 0) { if (oact != NULL) *oact = oldact; } else { errno = err; } return (ret); } int __thr_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); +__weak_reference(_thr_sigmask, pthread_sigmask); +__weak_reference(_thr_sigmask, _pthread_sigmask); int -_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) +_thr_sigmask(int how, const sigset_t *set, sigset_t *oset) { if (__thr_sigprocmask(how, set, oset)) return (errno); return (0); } int _sigsuspend(const sigset_t * set) { sigset_t newset; return (__sys_sigsuspend(thr_remove_thr_signals(set, &newset))); } int __thr_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); } 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 __thr_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 __thr_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 __thr_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); } int __thr_setcontext(const ucontext_t *ucp) { ucontext_t uc; if (ucp == NULL) { errno = EINVAL; return (-1); } if (!SIGISMEMBER(ucp->uc_sigmask, SIGCANCEL)) return (__sys_setcontext(ucp)); (void) memcpy(&uc, ucp, sizeof(uc)); SIGDELSET(uc.uc_sigmask, SIGCANCEL); return (__sys_setcontext(&uc)); } int __thr_swapcontext(ucontext_t *oucp, const ucontext_t *ucp) { ucontext_t uc; if (oucp == NULL || ucp == NULL) { errno = EINVAL; return (-1); } if (SIGISMEMBER(ucp->uc_sigmask, SIGCANCEL)) { (void) memcpy(&uc, ucp, sizeof(uc)); SIGDELSET(uc.uc_sigmask, SIGCANCEL); ucp = &uc; } return (__sys_swapcontext(oucp, ucp)); } Index: head/lib/libthr/thread/thr_spec.c =================================================================== --- head/lib/libthr/thread/thr_spec.c (revision 350480) +++ head/lib/libthr/thread/thr_spec.c (revision 350481) @@ -1,244 +1,247 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1995 John Birrell . * 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 __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include #include #include #include #include "un-namespace.h" #include "libc_private.h" #include "thr_private.h" /* Used in symbol lookup of libthread_db */ 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); +__weak_reference(_thr_key_create, pthread_key_create); +__weak_reference(_thr_key_create, _pthread_key_create); +__weak_reference(_thr_key_delete, pthread_key_delete); +__weak_reference(_thr_key_delete, _pthread_key_delete); +__weak_reference(_thr_getspecific, pthread_getspecific); +__weak_reference(_thr_getspecific, _pthread_getspecific); +__weak_reference(_thr_setspecific, pthread_setspecific); +__weak_reference(_thr_setspecific, _pthread_setspecific); - int -_pthread_key_create(pthread_key_t *key, void (*destructor)(void *)) +_thr_key_create(pthread_key_t *key, void (*destructor)(void *)) { struct pthread *curthread; int i; _thr_check_init(); curthread = _get_curthread(); 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++; THR_LOCK_RELEASE(curthread, &_keytable_lock); *key = i + 1; return (0); } } THR_LOCK_RELEASE(curthread, &_keytable_lock); return (EAGAIN); } int -_pthread_key_delete(pthread_key_t userkey) +_thr_key_delete(pthread_key_t userkey) { struct pthread *curthread; int key, ret; key = userkey - 1; if ((unsigned int)key >= PTHREAD_KEYS_MAX) return (EINVAL); curthread = _get_curthread(); THR_LOCK_ACQUIRE(curthread, &_keytable_lock); if (_thread_keytable[key].allocated) { _thread_keytable[key].allocated = 0; ret = 0; } else { ret = EINVAL; } THR_LOCK_RELEASE(curthread, &_keytable_lock); return (ret); } void _thread_cleanupspecific(void) { struct pthread *curthread; void (*destructor)(void *); const void *data; int i, key; curthread = _get_curthread(); if (curthread->specific == NULL) return; 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) { THR_LOCK_RELEASE(curthread, &_keytable_lock); destructor(__DECONST(void *, data)); THR_LOCK_ACQUIRE(curthread, &_keytable_lock); } } } THR_LOCK_RELEASE(curthread, &_keytable_lock); __thr_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); } } int -_pthread_setspecific(pthread_key_t userkey, const void *value) +_thr_setspecific(pthread_key_t userkey, const void *value) { struct pthread *pthread; void *tmp; pthread_key_t key; key = userkey - 1; if ((unsigned int)key >= PTHREAD_KEYS_MAX || !_thread_keytable[key].allocated) return (EINVAL); pthread = _get_curthread(); if (pthread->specific == NULL) { tmp = __thr_calloc(PTHREAD_KEYS_MAX, sizeof(struct pthread_specific_elem)); if (tmp == NULL) return (ENOMEM); pthread->specific = tmp; } 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; return (0); } void * -_pthread_getspecific(pthread_key_t userkey) +_thr_getspecific(pthread_key_t userkey) { struct pthread *pthread; const void *data; pthread_key_t key; /* Check if there is specific data. */ key = userkey - 1; if ((unsigned int)key >= PTHREAD_KEYS_MAX) return (NULL); pthread = _get_curthread(); /* Check if this key has been used before. */ if (_thread_keytable[key].allocated && pthread->specific != NULL && 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; } return (__DECONST(void *, data)); } void _thr_tsd_unload(struct dl_phdr_info *phdr_info) { struct pthread *curthread; void (*destructor)(void *); int key; curthread = _get_curthread(); THR_LOCK_ACQUIRE(curthread, &_keytable_lock); for (key = 0; key < PTHREAD_KEYS_MAX; key++) { if (!_thread_keytable[key].allocated) continue; destructor = _thread_keytable[key].destructor; if (destructor == NULL) continue; if (__elf_phdr_match_addr(phdr_info, destructor)) _thread_keytable[key].destructor = NULL; } THR_LOCK_RELEASE(curthread, &_keytable_lock); }