diff --git a/lib/libthr/thread/thr_create.c b/lib/libthr/thread/thr_create.c index 018000aa2079..f73a6c9231b3 100644 --- a/lib/libthr/thread/thr_create.c +++ b/lib/libthr/thread/thr_create.c @@ -1,292 +1,292 @@ /* * Copyright (c) 2003 Daniel M. Eischen * 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. * * $FreeBSD$ */ #include "namespace.h" #include #include #include #include #include #include #include #include #include #include "un-namespace.h" #include "thr_private.h" static int create_stack(struct pthread_attr *pattr); static void thread_start(struct pthread *curthread); __weak_reference(_pthread_create, pthread_create); int _pthread_create(pthread_t * thread, const pthread_attr_t * attr, void *(*start_routine) (void *), void *arg) { struct pthread *curthread, *new_thread; struct thr_param param; struct sched_param sched_param; struct rtprio rtp; int ret = 0, locked, create_suspended; sigset_t set, oset; cpuset_t *cpusetp = NULL; int cpusetsize = 0; _thr_check_init(); /* * Tell libc and others now they need lock to protect their data. */ if (_thr_isthreaded() == 0 && _thr_setthreaded(1)) return (EAGAIN); curthread = _get_curthread(); if ((new_thread = _thr_alloc(curthread)) == NULL) return (EAGAIN); memset(¶m, 0, sizeof(param)); if (attr == NULL || *attr == NULL) /* Use the default thread attributes: */ new_thread->attr = _pthread_attr_default; else { new_thread->attr = *(*attr); cpusetp = new_thread->attr.cpuset; cpusetsize = new_thread->attr.cpusetsize; new_thread->attr.cpuset = NULL; new_thread->attr.cpusetsize = 0; } if (new_thread->attr.sched_inherit == PTHREAD_INHERIT_SCHED) { /* inherit scheduling contention scope */ if (curthread->attr.flags & PTHREAD_SCOPE_SYSTEM) new_thread->attr.flags |= PTHREAD_SCOPE_SYSTEM; else new_thread->attr.flags &= ~PTHREAD_SCOPE_SYSTEM; new_thread->attr.prio = curthread->attr.prio; new_thread->attr.sched_policy = curthread->attr.sched_policy; } new_thread->tid = TID_TERMINATED; if (create_stack(&new_thread->attr) != 0) { /* Insufficient memory to create a stack: */ _thr_free(curthread, new_thread); return (EAGAIN); } /* * Write a magic value to the thread structure * to help identify valid ones: */ new_thread->magic = THR_MAGIC; new_thread->start_routine = start_routine; new_thread->arg = arg; new_thread->cancel_enable = 1; new_thread->cancel_async = 0; /* Initialize the mutex queue: */ TAILQ_INIT(&new_thread->mutexq); TAILQ_INIT(&new_thread->pp_mutexq); /* Initialise hooks in the thread structure: */ if (new_thread->attr.suspend == THR_CREATE_SUSPENDED) { new_thread->flags = THR_FLAGS_NEED_SUSPEND; create_suspended = 1; } else { create_suspended = 0; } new_thread->state = PS_RUNNING; if (new_thread->attr.flags & PTHREAD_CREATE_DETACHED) new_thread->tlflags |= TLFLAGS_DETACHED; if (curthread->in_sigcancel_handler) new_thread->unblock_sigcancel = 1; else new_thread->unblock_sigcancel = 0; /* Add the new thread. */ new_thread->refcount = 1; _thr_link(curthread, new_thread); /* Return thread pointer eariler so that new thread can use it. */ (*thread) = new_thread; if (SHOULD_REPORT_EVENT(curthread, TD_CREATE) || cpusetp != NULL) { THR_THREAD_LOCK(curthread, new_thread); locked = 1; } else locked = 0; param.start_func = (void (*)(void *)) thread_start; param.arg = new_thread; param.stack_base = new_thread->attr.stackaddr_attr; param.stack_size = new_thread->attr.stacksize_attr; param.tls_base = (char *)new_thread->tcb; param.tls_size = sizeof(struct tcb); param.child_tid = &new_thread->tid; param.parent_tid = &new_thread->tid; param.flags = 0; if (new_thread->attr.flags & PTHREAD_SCOPE_SYSTEM) param.flags |= THR_SYSTEM_SCOPE; if (new_thread->attr.sched_inherit == PTHREAD_INHERIT_SCHED) param.rtp = NULL; else { sched_param.sched_priority = new_thread->attr.prio; _schedparam_to_rtp(new_thread->attr.sched_policy, &sched_param, &rtp); param.rtp = &rtp; } /* Schedule the new thread. */ if (create_suspended) { SIGFILLSET(set); SIGDELSET(set, SIGTRAP); __sys_sigprocmask(SIG_SETMASK, &set, &oset); new_thread->sigmask = oset; SIGDELSET(new_thread->sigmask, SIGCANCEL); } ret = thr_new(¶m, sizeof(param)); if (ret != 0) { ret = errno; /* * Translate EPROCLIM into well-known POSIX code EAGAIN. */ if (ret == EPROCLIM) ret = EAGAIN; } if (create_suspended) __sys_sigprocmask(SIG_SETMASK, &oset, NULL); if (ret != 0) { if (!locked) THR_THREAD_LOCK(curthread, new_thread); new_thread->state = PS_DEAD; new_thread->tid = TID_TERMINATED; if (new_thread->flags & THR_FLAGS_NEED_SUSPEND) { new_thread->cycle++; - _thr_umtx_wake(&new_thread->cycle, INT_MAX); + _thr_umtx_wake(&new_thread->cycle, INT_MAX, 0); } THR_THREAD_UNLOCK(curthread, new_thread); THREAD_LIST_LOCK(curthread); _thread_active_threads--; new_thread->tlflags |= TLFLAGS_DETACHED; _thr_ref_delete_unlocked(curthread, new_thread); THREAD_LIST_UNLOCK(curthread); } else if (locked) { if (cpusetp != NULL) { if (cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID, TID(new_thread), cpusetsize, cpusetp)) { ret = errno; /* kill the new thread */ new_thread->force_exit = 1; THR_THREAD_UNLOCK(curthread, new_thread); goto out; } } _thr_report_creation(curthread, new_thread); THR_THREAD_UNLOCK(curthread, new_thread); out: if (ret) { THREAD_LIST_LOCK(curthread); new_thread->tlflags |= TLFLAGS_DETACHED; THR_GCLIST_ADD(new_thread); THREAD_LIST_UNLOCK(curthread); } } if (ret) (*thread) = 0; return (ret); } static int create_stack(struct pthread_attr *pattr) { int ret; /* Check if a stack was specified in the thread attributes: */ if ((pattr->stackaddr_attr) != NULL) { pattr->guardsize_attr = 0; pattr->flags |= THR_STACK_USER; ret = 0; } else ret = _thr_stack_alloc(pattr); return (ret); } static void thread_start(struct pthread *curthread) { sigset_t set; if (curthread->attr.suspend == THR_CREATE_SUSPENDED) set = curthread->sigmask; /* * This is used as a serialization point to allow parent * to report 'new thread' event to debugger or tweak new thread's * attributes before the new thread does real-world work. */ THR_LOCK(curthread); THR_UNLOCK(curthread); if (curthread->force_exit) _pthread_exit(PTHREAD_CANCELED); if (curthread->unblock_sigcancel) { sigset_t set1; SIGEMPTYSET(set1); SIGADDSET(set1, SIGCANCEL); __sys_sigprocmask(SIG_UNBLOCK, &set1, NULL); } if (curthread->attr.suspend == THR_CREATE_SUSPENDED) { #if 0 /* Done in THR_UNLOCK() */ _thr_ast(curthread); #endif /* * Parent thread have stored signal mask for us, * we should restore it now. */ __sys_sigprocmask(SIG_SETMASK, &set, NULL); } /* Run the current thread's start routine with argument: */ _pthread_exit(curthread->start_routine(curthread->arg)); /* This point should never be reached. */ PANIC("Thread has resumed after exit"); } diff --git a/lib/libthr/thread/thr_exit.c b/lib/libthr/thread/thr_exit.c index b78be1f6e28a..df0170ac37e6 100644 --- a/lib/libthr/thread/thr_exit.c +++ b/lib/libthr/thread/thr_exit.c @@ -1,149 +1,149 @@ /* * 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. * * $FreeBSD$ */ #include "namespace.h" #include #include #include #include #include "un-namespace.h" #include "thr_private.h" void _pthread_exit(void *status); __weak_reference(_pthread_exit, pthread_exit); void _thread_exit(const char *fname, int lineno, const char *msg) { /* Write an error message to the standard error file descriptor: */ _thread_printf(2, "Fatal error '%s' at line %d in file %s (errno = %d)\n", msg, lineno, fname, errno); abort(); } /* * Only called when a thread is cancelled. It may be more useful * to call it from pthread_exit() if other ways of asynchronous or * abnormal thread termination can be found. */ void _thr_exit_cleanup(void) { struct pthread *curthread = _get_curthread(); /* * POSIX states that cancellation/termination of a thread should * not release any visible resources (such as mutexes) and that * it is the applications responsibility. Resources that are * internal to the threads library, including file and fd locks, * are not visible to the application and need to be released. */ /* Unlock all private mutexes: */ _mutex_unlock_private(curthread); /* * This still isn't quite correct because we don't account * for held spinlocks (see libc/stdlib/malloc.c). */ } void _pthread_exit(void *status) { struct pthread *curthread = _get_curthread(); /* Check if this thread is already in the process of exiting: */ if (curthread->cancelling) { char msg[128]; snprintf(msg, sizeof(msg), "Thread %p has called " "pthread_exit() from a destructor. POSIX 1003.1 " "1996 s16.2.5.2 does not allow this!", curthread); PANIC(msg); } /* Flag this thread as exiting. */ curthread->cancelling = 1; _thr_exit_cleanup(); /* Save the return value: */ curthread->ret = status; while (curthread->cleanup != NULL) { _pthread_cleanup_pop(1); } /* Check if there is thread specific data: */ if (curthread->specific != NULL) { /* Run the thread-specific data destructors: */ _thread_cleanupspecific(); } if (!_thr_isthreaded()) exit(0); THREAD_LIST_LOCK(curthread); _thread_active_threads--; if (_thread_active_threads == 0) { THREAD_LIST_UNLOCK(curthread); exit(0); /* Never reach! */ } THR_LOCK(curthread); curthread->state = PS_DEAD; if (curthread->flags & THR_FLAGS_NEED_SUSPEND) { curthread->cycle++; - _thr_umtx_wake(&curthread->cycle, INT_MAX); + _thr_umtx_wake(&curthread->cycle, INT_MAX, 0); } THR_UNLOCK(curthread); /* * Thread was created with initial refcount 1, we drop the * reference count to allow it to be garbage collected. */ curthread->refcount--; if (curthread->tlflags & TLFLAGS_DETACHED) THR_GCLIST_ADD(curthread); THREAD_LIST_UNLOCK(curthread); if (!curthread->force_exit && SHOULD_REPORT_EVENT(curthread, TD_DEATH)) _thr_report_death(curthread); /* * Kernel will do wakeup at the address, so joiner thread * will be resumed if it is sleeping at the address. */ thr_exit(&curthread->tid); PANIC("thr_exit() returned"); /* Never reach! */ } diff --git a/lib/libthr/thread/thr_private.h b/lib/libthr/thread/thr_private.h index 68b5d53f86ab..dbbdda4aee75 100644 --- a/lib/libthr/thread/thr_private.h +++ b/lib/libthr/thread/thr_private.h @@ -1,733 +1,733 @@ /* * 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 #ifndef __hidden #define __hidden __attribute__((visibility("hidden"))) #endif #include "pthread_md.h" #include "thr_umtx.h" #include "thread_db.h" 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 32 /* * Kernel fatal error handler macro. */ #define PANIC(string) _thread_exit(__FILE__,__LINE__,string) /* Output debug messages like this: */ #define stdout_debug(args...) _thread_printf(STDOUT_FILENO, ##args) #define stderr_debug(args...) _thread_printf(STDERR_FILENO, ##args) #ifdef _PTHREADS_INVARIANTS #define THR_ASSERT(cond, msg) do { \ if (__predict_false(!(cond))) \ PANIC(msg); \ } while (0) #else #define THR_ASSERT(cond, msg) #endif #ifdef PIC # define STATIC_LIB_REQUIRE(name) #else # define STATIC_LIB_REQUIRE(name) __asm (".globl " #name) #endif #define TIMESPEC_ADD(dst, src, val) \ do { \ (dst)->tv_sec = (src)->tv_sec + (val)->tv_sec; \ (dst)->tv_nsec = (src)->tv_nsec + (val)->tv_nsec; \ if ((dst)->tv_nsec >= 1000000000) { \ (dst)->tv_sec++; \ (dst)->tv_nsec -= 1000000000; \ } \ } while (0) #define TIMESPEC_SUB(dst, src, val) \ do { \ (dst)->tv_sec = (src)->tv_sec - (val)->tv_sec; \ (dst)->tv_nsec = (src)->tv_nsec - (val)->tv_nsec; \ if ((dst)->tv_nsec < 0) { \ (dst)->tv_sec--; \ (dst)->tv_nsec += 1000000000; \ } \ } while (0) struct pthread_mutex { /* * Lock for accesses to this structure. */ struct umutex m_lock; enum pthread_mutextype m_type; struct pthread *m_owner; int m_flags; int m_count; int m_refcount; int m_spinloops; int m_yieldloops; /* * Link for all mutexes a thread currently owns. */ TAILQ_ENTRY(pthread_mutex) m_qe; }; /* * Flags for mutexes. */ #define MUTEX_FLAGS_PRIVATE 0x01 #define MUTEX_FLAGS_INITED 0x02 #define MUTEX_FLAGS_BUSY 0x04 struct pthread_mutex_attr { enum pthread_mutextype m_type; int m_protocol; int m_ceiling; int m_flags; }; #define PTHREAD_MUTEXATTR_STATIC_INITIALIZER \ { PTHREAD_MUTEX_DEFAULT, PTHREAD_PRIO_NONE, 0, MUTEX_FLAGS_PRIVATE } struct pthread_cond { struct umutex c_lock; struct ucond c_kerncv; int c_pshared; int c_clockid; }; struct pthread_cond_attr { int c_pshared; int c_clockid; }; struct pthread_barrier { struct umutex b_lock; struct ucond b_cv; volatile int64_t b_cycle; volatile int b_count; volatile int b_waiters; }; 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 *next; void (*routine)(void *args); void *routine_arg; int onstack; }; #define THR_CLEANUP_PUSH(td, func, arg) { \ struct pthread_cleanup __cup; \ \ __cup.routine = func; \ __cup.routine_arg = arg; \ __cup.onstack = 1; \ __cup.next = (td)->cleanup; \ (td)->cleanup = &__cup; #define THR_CLEANUP_POP(td, exec) \ (td)->cleanup = __cup.next; \ if ((exec) != 0) \ __cup.routine(__cup.routine_arg); \ } struct pthread_atfork { TAILQ_ENTRY(pthread_atfork) qe; void (*prepare)(void); void (*parent)(void); void (*child)(void); }; struct pthread_attr { int sched_policy; int sched_inherit; int prio; int suspend; #define THR_STACK_USER 0x100 /* 0xFF reserved for */ int flags; void *stackaddr_attr; size_t stacksize_attr; size_t guardsize_attr; cpuset_t *cpuset; size_t cpusetsize; }; /* * Thread creation state attributes. */ #define THR_CREATE_RUNNING 0 #define THR_CREATE_SUSPENDED 1 /* * Miscellaneous definitions. */ #define THR_STACK_DEFAULT (sizeof(void *) / 4 * 1024 * 1024) /* * Maximum size of initial thread's stack. This perhaps deserves to be larger * than the stacks of other threads, since many applications are likely to run * almost entirely on this stack. */ #define THR_STACK_INITIAL (THR_STACK_DEFAULT * 2) /* * Define priorities returned by kernel. */ #define THR_MIN_PRIORITY (_thr_priorities[SCHED_OTHER-1].pri_min) #define THR_MAX_PRIORITY (_thr_priorities[SCHED_OTHER-1].pri_max) #define THR_DEF_PRIORITY (_thr_priorities[SCHED_OTHER-1].pri_default) #define THR_MIN_RR_PRIORITY (_thr_priorities[SCHED_RR-1].pri_min) #define THR_MAX_RR_PRIORITY (_thr_priorities[SCHED_RR-1].pri_max) #define THR_DEF_RR_PRIORITY (_thr_priorities[SCHED_RR-1].pri_default) /* XXX The SCHED_FIFO should have same priority range as SCHED_RR */ #define THR_MIN_FIFO_PRIORITY (_thr_priorities[SCHED_FIFO_1].pri_min) #define THR_MAX_FIFO_PRIORITY (_thr_priorities[SCHED_FIFO-1].pri_max) #define THR_DEF_FIFO_PRIORITY (_thr_priorities[SCHED_FIFO-1].pri_default) struct pthread_prio { int pri_min; int pri_max; int pri_default; }; struct pthread_rwlockattr { int pshared; }; struct pthread_rwlock { struct urwlock lock; struct pthread *owner; }; /* * Thread states. */ enum pthread_state { PS_RUNNING, PS_DEAD }; struct pthread_specific_elem { const void *data; int seqno; }; struct pthread_key { volatile int allocated; int seqno; void (*destructor)(void *); }; /* * lwpid_t is 32bit but kernel thr API exports tid as long type * in very earily date. */ #define TID(thread) ((uint32_t) ((thread)->tid)) /* * Thread structure. */ struct pthread { /* Kernel thread id. */ long tid; #define TID_TERMINATED 1 /* * Lock for accesses to this thread structure. */ struct umutex lock; /* Internal condition variable cycle number. */ - long cycle; + 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; /* 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_point || (thr)->cancel_async) && \ (thr)->cancel_enable && (thr)->cancelling == 0) /* Cancellation is enabled */ int cancel_enable; /* Cancellation request is pending */ int cancel_pending; /* Thread is at cancellation point */ int cancel_point; /* Cancellation should be synchoronized */ int cancel_defer; /* Asynchronouse cancellation is enabled */ int cancel_async; /* Cancellation is in progress */ int cancelling; /* Thread temporary signal mask. */ sigset_t sigmask; /* Thread is in SIGCANCEL handler. */ int in_sigcancel_handler; /* New thread should unblock SIGCANCEL. */ int unblock_sigcancel; /* 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 */ /* 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 */ #define TLFLAGS_DETACHED 0x0008 /* thread is detached */ /* Queue of currently owned NORMAL or PRIO_INHERIT type mutexes. */ struct mutex_queue mutexq; /* Queue of all owned PRIO_PROTECT mutexes. */ struct mutex_queue pp_mutexq; void *ret; struct pthread_specific_elem *specific; int specific_data_count; /* Number rwlocks rdlocks held. */ int rdlock_count; /* * Current locks bitmap for rtld. */ int rtld_bits; /* Thread control block */ struct tcb *tcb; /* Cleanup handlers Link List */ struct pthread_cleanup *cleanup; /* * 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; }; #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) #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_LOCK(curthrd) \ do { \ THR_LOCK_ACQUIRE((curthrd), &_thr_list_lock); \ } while (0) #define THREAD_LIST_UNLOCK(curthrd) \ do { \ THR_LOCK_RELEASE((curthrd), &_thr_list_lock); \ } 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 GC_NEEDED() (_gc_count >= 5) #define SHOULD_REPORT_EVENT(curthr, e) \ (curthr->report_events && \ (((curthr)->event_mask | _thread_event_mask ) & e) != 0) extern int __isthreaded; /* * Global variables for the pthread kernel. */ extern char *_usrstack __hidden; extern struct pthread *_thr_initial __hidden; /* For debugger */ extern int _libthr_debug; extern int _thread_event_mask; extern struct pthread *_thread_last_event; /* List of all threads: */ extern pthreadlist _thread_list; /* List of threads needing GC: */ extern pthreadlist _thread_gc_list __hidden; extern int _thread_active_threads; extern atfork_head _thr_atfork_list __hidden; extern struct umutex _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; /* Default condition variable attributes: */ extern struct pthread_cond_attr _pthread_condattr_default __hidden; extern struct pthread_prio _thr_priorities[] __hidden; extern pid_t _thr_pid __hidden; extern int _thr_is_smp __hidden; extern size_t _thr_guard_default __hidden; extern size_t _thr_stack_default __hidden; extern size_t _thr_stack_initial __hidden; extern int _thr_page_size __hidden; extern int _thr_spinloops __hidden; extern int _thr_yieldloops __hidden; /* Garbage thread count. */ extern int _gc_count __hidden; extern struct umutex _mutex_static_lock __hidden; extern struct umutex _cond_static_lock __hidden; extern struct umutex _rwlock_static_lock __hidden; extern struct umutex _keytable_lock __hidden; extern struct umutex _thr_list_lock __hidden; extern struct umutex _thr_event_lock __hidden; /* * Function prototype definitions. */ __BEGIN_DECLS int _thr_setthreaded(int) __hidden; int _mutex_cv_lock(pthread_mutex_t *, int count) __hidden; int _mutex_cv_unlock(pthread_mutex_t *, int *count) __hidden; int _mutex_reinit(pthread_mutex_t *) __hidden; void _mutex_fork(struct pthread *curthread) __hidden; void _mutex_unlock_private(struct pthread *) __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 _thr_exit_cleanup(void) __hidden; int _thr_ref_add(struct pthread *, struct pthread *, int) __hidden; void _thr_ref_delete(struct pthread *, struct pthread *) __hidden; void _thr_ref_delete_unlocked(struct pthread *, struct pthread *) __hidden; int _thr_find_thread(struct pthread *, struct pthread *, int) __hidden; void _thr_rtld_init(void) __hidden; void _thr_rtld_fini(void) __hidden; 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_dump_info(void) __hidden; void _thread_printf(int, const char *, ...) __hidden; void _thr_spinlock_init(void) __hidden; void _thr_cancel_enter(struct pthread *) __hidden; void _thr_cancel_leave(struct pthread *) __hidden; void _thr_cancel_enter_defer(struct pthread *) __hidden; void _thr_cancel_leave_defer(struct pthread *, int) __hidden; void _thr_testcancel(struct pthread *) __hidden; void _thr_signal_block(struct pthread *) __hidden; void _thr_signal_unblock(struct pthread *) __hidden; void _thr_signal_init(void) __hidden; void _thr_signal_deinit(void) __hidden; int _thr_send_sig(struct pthread *, int sig) __hidden; void _thr_list_init(void) __hidden; void _thr_hash_add(struct pthread *) __hidden; void _thr_hash_remove(struct pthread *) __hidden; struct pthread *_thr_hash_find(struct pthread *) __hidden; void _thr_link(struct pthread *, struct pthread *) __hidden; void _thr_unlink(struct pthread *, struct pthread *) __hidden; void _thr_suspend_check(struct pthread *) __hidden; void _thr_assert_lock_level(void) __hidden __dead2; void _thr_ast(struct pthread *) __hidden; void _thr_once_init(void) __hidden; void _thr_report_creation(struct pthread *curthread, struct pthread *newthread) __hidden; void _thr_report_death(struct pthread *curthread) __hidden; int _thr_getscheduler(lwpid_t, int *, struct sched_param *) __hidden; int _thr_setscheduler(lwpid_t, int, const struct sched_param *) __hidden; 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); /* #include */ #ifdef _SYS_FCNTL_H_ int __sys_fcntl(int, int, ...); int __sys_open(const char *, int, ...); #endif /* #include */ #ifdef _SIGNAL_H_ int __sys_kill(pid_t, int); int __sys_sigaction(int, const struct sigaction *, struct sigaction *); int __sys_sigpending(sigset_t *); int __sys_sigprocmask(int, const sigset_t *, sigset_t *); int __sys_sigsuspend(const sigset_t *); int __sys_sigreturn(ucontext_t *); int __sys_sigaltstack(const struct sigaltstack *, struct sigaltstack *); int __sys_sigwait(const sigset_t *, int *); int __sys_sigtimedwait(const sigset_t *, siginfo_t *, const struct timespec *); int __sys_sigwaitinfo(const sigset_t *set, siginfo_t *info); #endif /* #include */ #ifdef _TIME_H_ int __sys_nanosleep(const struct timespec *, struct timespec *); #endif /* #include */ #ifdef _UNISTD_H_ int __sys_close(int); int __sys_fork(void); pid_t __sys_getpid(void); ssize_t __sys_read(int, void *, size_t); ssize_t __sys_write(int, const void *, size_t); void __sys_exit(int); #endif int _umtx_op_err(void *, int op, u_long, void *, void *) __hidden; 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); } __END_DECLS #endif /* !_THR_PRIVATE_H */ diff --git a/lib/libthr/thread/thr_resume_np.c b/lib/libthr/thread/thr_resume_np.c index b477a66c5d99..50b561ac8777 100644 --- a/lib/libthr/thread/thr_resume_np.c +++ b/lib/libthr/thread/thr_resume_np.c @@ -1,91 +1,91 @@ /* * 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. * * $FreeBSD$ */ #include "namespace.h" #include #include #include #include "un-namespace.h" #include "thr_private.h" __weak_reference(_pthread_resume_np, pthread_resume_np); __weak_reference(_pthread_resume_all_np, pthread_resume_all_np); static void resume_common(struct pthread *thread); /* Resume a thread: */ int _pthread_resume_np(pthread_t thread) { struct pthread *curthread = _get_curthread(); int ret; /* Add a reference to the thread: */ if ((ret = _thr_ref_add(curthread, thread, /*include dead*/0)) == 0) { /* Lock the threads scheduling queue: */ THR_THREAD_LOCK(curthread, thread); resume_common(thread); THR_THREAD_UNLOCK(curthread, thread); _thr_ref_delete(curthread, thread); } return (ret); } void _pthread_resume_all_np(void) { struct pthread *curthread = _get_curthread(); struct pthread *thread; /* Take the thread list lock: */ THREAD_LIST_LOCK(curthread); TAILQ_FOREACH(thread, &_thread_list, tle) { if (thread != curthread) { THR_THREAD_LOCK(curthread, thread); resume_common(thread); THR_THREAD_UNLOCK(curthread, thread); } } /* Release the thread list lock: */ THREAD_LIST_UNLOCK(curthread); } static void resume_common(struct pthread *thread) { /* Clear the suspend flag: */ thread->flags &= ~THR_FLAGS_NEED_SUSPEND; thread->cycle++; - _thr_umtx_wake(&thread->cycle, 1); + _thr_umtx_wake(&thread->cycle, 1, 0); } diff --git a/lib/libthr/thread/thr_sem.c b/lib/libthr/thread/thr_sem.c index ada158c11328..0dd5599976f5 100644 --- a/lib/libthr/thread/thr_sem.c +++ b/lib/libthr/thread/thr_sem.c @@ -1,291 +1,291 @@ /* * Copyright (C) 2005 David Xu . * Copyright (C) 2000 Jason Evans . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice(s), this list of conditions and the following disclaimer as * the first lines of this file unmodified other than the possible * addition of one or more copyright notices. * 2. Redistributions in binary form must reproduce the above copyright * notice(s), this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include "namespace.h" #include #include #include #include #include #include #include #include #include <_semaphore.h> #include "un-namespace.h" #include "thr_private.h" __weak_reference(_sem_init, sem_init); __weak_reference(_sem_destroy, sem_destroy); __weak_reference(_sem_getvalue, sem_getvalue); __weak_reference(_sem_trywait, sem_trywait); __weak_reference(_sem_wait, sem_wait); __weak_reference(_sem_timedwait, sem_timedwait); __weak_reference(_sem_post, sem_post); static inline int sem_check_validity(sem_t *sem) { if ((sem != NULL) && ((*sem)->magic == SEM_MAGIC)) return (0); else { errno = EINVAL; return (-1); } } static sem_t sem_alloc(unsigned int value, semid_t semid, int system_sem) { sem_t sem; if (value > SEM_VALUE_MAX) { errno = EINVAL; return (NULL); } sem = (sem_t)malloc(sizeof(struct sem)); if (sem == NULL) { errno = ENOSPC; return (NULL); } bzero(sem, sizeof(*sem)); /* * Fortunatly count and nwaiters are adjacency, so we can * use umtx_wait to wait on it, umtx_wait needs an address * can be accessed as a long interger. */ sem->count = (u_int32_t)value; sem->nwaiters = 0; sem->magic = SEM_MAGIC; sem->semid = semid; sem->syssem = system_sem; return (sem); } int _sem_init(sem_t *sem, int pshared, unsigned int value) { semid_t semid; semid = (semid_t)SEM_USER; if ((pshared != 0) && (ksem_init(&semid, value) != 0)) return (-1); (*sem) = sem_alloc(value, semid, pshared); if ((*sem) == NULL) { if (pshared != 0) ksem_destroy(semid); return (-1); } return (0); } int _sem_destroy(sem_t *sem) { int retval; if (sem_check_validity(sem) != 0) return (-1); /* * If this is a system semaphore let the kernel track it otherwise * make sure there are no waiters. */ if ((*sem)->syssem != 0) retval = ksem_destroy((*sem)->semid); else { retval = 0; (*sem)->magic = 0; } if (retval == 0) free(*sem); return (retval); } int _sem_getvalue(sem_t * __restrict sem, int * __restrict sval) { int retval; if (sem_check_validity(sem) != 0) return (-1); if ((*sem)->syssem != 0) retval = ksem_getvalue((*sem)->semid, sval); else { *sval = (int)(*sem)->count; retval = 0; } return (retval); } int _sem_trywait(sem_t *sem) { int val; if (sem_check_validity(sem) != 0) return (-1); if ((*sem)->syssem != 0) return (ksem_trywait((*sem)->semid)); while ((val = (*sem)->count) > 0) { if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1)) return (0); } errno = EAGAIN; return (-1); } static void sem_cancel_handler(void *arg) { sem_t *sem = arg; atomic_add_int(&(*sem)->nwaiters, -1); if ((*sem)->nwaiters && (*sem)->count) - _thr_umtx_wake(&(*sem)->count, 1); + _thr_umtx_wake(&(*sem)->count, 1, 0); } int _sem_wait(sem_t *sem) { struct pthread *curthread; int val, retval; if (sem_check_validity(sem) != 0) return (-1); curthread = _get_curthread(); if ((*sem)->syssem != 0) { _thr_cancel_enter(curthread); retval = ksem_wait((*sem)->semid); _thr_cancel_leave(curthread); return (retval); } _pthread_testcancel(); do { while ((val = (*sem)->count) > 0) { if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1)) return (0); } atomic_add_int(&(*sem)->nwaiters, 1); THR_CLEANUP_PUSH(curthread, sem_cancel_handler, sem); _thr_cancel_enter(curthread); - retval = _thr_umtx_wait_uint(&(*sem)->count, 0, NULL); + retval = _thr_umtx_wait_uint(&(*sem)->count, 0, NULL, 0); _thr_cancel_leave(curthread); THR_CLEANUP_POP(curthread, 0); atomic_add_int(&(*sem)->nwaiters, -1); } while (retval == 0); errno = retval; return (-1); } int _sem_timedwait(sem_t * __restrict sem, const struct timespec * __restrict abstime) { struct timespec ts, ts2; struct pthread *curthread; int val, retval; if (sem_check_validity(sem) != 0) return (-1); curthread = _get_curthread(); if ((*sem)->syssem != 0) { _thr_cancel_enter(curthread); retval = ksem_timedwait((*sem)->semid, abstime); _thr_cancel_leave(curthread); return (retval); } /* * The timeout argument is only supposed to * be checked if the thread would have blocked. */ _pthread_testcancel(); do { while ((val = (*sem)->count) > 0) { if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1)) return (0); } if (abstime == NULL) { errno = EINVAL; return (-1); } clock_gettime(CLOCK_REALTIME, &ts); TIMESPEC_SUB(&ts2, abstime, &ts); atomic_add_int(&(*sem)->nwaiters, 1); THR_CLEANUP_PUSH(curthread, sem_cancel_handler, sem); _thr_cancel_enter(curthread); - retval = _thr_umtx_wait_uint(&(*sem)->count, 0, &ts2); + retval = _thr_umtx_wait_uint((uint32_t*)&(*sem)->count, 0, &ts2, 0); _thr_cancel_leave(curthread); THR_CLEANUP_POP(curthread, 0); atomic_add_int(&(*sem)->nwaiters, -1); } while (retval == 0); errno = retval; return (-1); } /* * sem_post() is required to be safe to call from within * signal handlers, these code should work as that. */ int _sem_post(sem_t *sem) { int retval = 0; if (sem_check_validity(sem) != 0) return (-1); if ((*sem)->syssem != 0) return (ksem_post((*sem)->semid)); atomic_add_rel_int(&(*sem)->count, 1); if ((*sem)->nwaiters) { - retval = _thr_umtx_wake(&(*sem)->count, 1); + retval = _thr_umtx_wake(&(*sem)->count, 1, 0); if (retval != 0) retval = -1; } return (retval); } diff --git a/lib/libthr/thread/thr_sig.c b/lib/libthr/thread/thr_sig.c index fe1555a4dedd..9615b06febb9 100644 --- a/lib/libthr/thread/thr_sig.c +++ b/lib/libthr/thread/thr_sig.c @@ -1,396 +1,396 @@ /* * 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. * * $FreeBSD$ */ #include "namespace.h" #include #include #include #include #include #include #include #include #include #include "un-namespace.h" #include "thr_private.h" /* #define DEBUG_SIGNAL */ #ifdef DEBUG_SIGNAL #define DBG_MSG stdout_debug #else #define DBG_MSG(x...) #endif extern int __pause(void); int ___pause(void); int _raise(int); int __sigtimedwait(const sigset_t *set, siginfo_t *info, const struct timespec * timeout); int _sigtimedwait(const sigset_t *set, siginfo_t *info, const struct timespec * timeout); int __sigwaitinfo(const sigset_t *set, siginfo_t *info); int _sigwaitinfo(const sigset_t *set, siginfo_t *info); int __sigwait(const sigset_t *set, int *sig); int _sigwait(const sigset_t *set, int *sig); int __sigsuspend(const sigset_t *sigmask); static void sigcancel_handler(int sig __unused, siginfo_t *info __unused, ucontext_t *ucp __unused) { struct pthread *curthread = _get_curthread(); if (curthread->cancel_defer && curthread->cancel_pending) thr_wake(curthread->tid); curthread->in_sigcancel_handler++; _thr_ast(curthread); curthread->in_sigcancel_handler--; } void _thr_ast(struct pthread *curthread) { if (!THR_IN_CRITICAL(curthread)) { _thr_testcancel(curthread); if (__predict_false((curthread->flags & (THR_FLAGS_NEED_SUSPEND | THR_FLAGS_SUSPENDED)) == THR_FLAGS_NEED_SUSPEND)) _thr_suspend_check(curthread); } } void _thr_suspend_check(struct pthread *curthread) { - long cycle; + uint32_t cycle; int err; if (curthread->force_exit) return; err = errno; /* * Blocks SIGCANCEL which other threads must send. */ _thr_signal_block(curthread); /* * Increase critical_count, here we don't use THR_LOCK/UNLOCK * because we are leaf code, we don't want to recursively call * ourself. */ curthread->critical_count++; THR_UMUTEX_LOCK(curthread, &(curthread)->lock); while ((curthread->flags & (THR_FLAGS_NEED_SUSPEND | THR_FLAGS_SUSPENDED)) == THR_FLAGS_NEED_SUSPEND) { curthread->cycle++; cycle = curthread->cycle; /* Wake the thread suspending us. */ - _thr_umtx_wake(&curthread->cycle, INT_MAX); + _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(&curthread->cycle, cycle, NULL); + _thr_umtx_wait_uint(&curthread->cycle, cycle, NULL, 0); THR_UMUTEX_LOCK(curthread, &(curthread)->lock); curthread->flags &= ~THR_FLAGS_SUSPENDED; } THR_UMUTEX_UNLOCK(curthread, &(curthread)->lock); curthread->critical_count--; /* * Unblocks SIGCANCEL, it is possible a new SIGCANCEL is ready and * a new signal frame will nest us, this seems a problem because * stack will grow and overflow, but because kernel will automatically * mask the SIGCANCEL when delivering the signal, so we at most only * have one nesting signal frame, this should be fine. */ _thr_signal_unblock(curthread); errno = err; } void _thr_signal_init(void) { struct sigaction act; /* Install cancel handler. */ SIGEMPTYSET(act.sa_mask); act.sa_flags = SA_SIGINFO | SA_RESTART; act.sa_sigaction = (__siginfohandler_t *)&sigcancel_handler; __sys_sigaction(SIGCANCEL, &act, NULL); } void _thr_signal_deinit(void) { } __weak_reference(___pause, pause); int ___pause(void) { struct pthread *curthread = _get_curthread(); int ret; _thr_cancel_enter(curthread); ret = __pause(); _thr_cancel_leave(curthread); return ret; } __weak_reference(_raise, raise); int _raise(int sig) { int ret; if (!_thr_isthreaded()) ret = kill(getpid(), sig); else ret = _thr_send_sig(_get_curthread(), sig); return (ret); } __weak_reference(_sigaction, sigaction); int _sigaction(int sig, const struct sigaction * act, struct sigaction * oact) { /* Check if the signal number is out of range: */ if (sig < 1 || sig > _SIG_MAXSIG || sig == SIGCANCEL) { /* Return an invalid argument: */ errno = EINVAL; return (-1); } return __sys_sigaction(sig, act, oact); } __weak_reference(_sigprocmask, sigprocmask); int _sigprocmask(int how, const sigset_t *set, sigset_t *oset) { const sigset_t *p = set; sigset_t newset; if (how != SIG_UNBLOCK) { if (set != NULL) { newset = *set; SIGDELSET(newset, SIGCANCEL); p = &newset; } } return (__sys_sigprocmask(how, p, oset)); } __weak_reference(_pthread_sigmask, pthread_sigmask); int _pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) { if (_sigprocmask(how, set, oset)) return (errno); return (0); } __weak_reference(__sigsuspend, sigsuspend); int _sigsuspend(const sigset_t * set) { sigset_t newset; const sigset_t *pset; int ret; if (SIGISMEMBER(*set, SIGCANCEL)) { newset = *set; SIGDELSET(newset, SIGCANCEL); pset = &newset; } else pset = set; ret = __sys_sigsuspend(pset); return (ret); } int __sigsuspend(const sigset_t * set) { struct pthread *curthread = _get_curthread(); sigset_t newset; const sigset_t *pset; int ret; if (SIGISMEMBER(*set, SIGCANCEL)) { newset = *set; SIGDELSET(newset, SIGCANCEL); pset = &newset; } else pset = set; _thr_cancel_enter(curthread); ret = __sys_sigsuspend(pset); _thr_cancel_leave(curthread); return (ret); } __weak_reference(__sigwait, sigwait); __weak_reference(__sigtimedwait, sigtimedwait); __weak_reference(__sigwaitinfo, sigwaitinfo); int _sigtimedwait(const sigset_t *set, siginfo_t *info, const struct timespec * timeout) { sigset_t newset; const sigset_t *pset; int ret; if (SIGISMEMBER(*set, SIGCANCEL)) { newset = *set; SIGDELSET(newset, SIGCANCEL); pset = &newset; } else pset = set; ret = __sys_sigtimedwait(pset, info, timeout); return (ret); } int __sigtimedwait(const sigset_t *set, siginfo_t *info, const struct timespec * timeout) { struct pthread *curthread = _get_curthread(); sigset_t newset; const sigset_t *pset; int ret; if (SIGISMEMBER(*set, SIGCANCEL)) { newset = *set; SIGDELSET(newset, SIGCANCEL); pset = &newset; } else pset = set; _thr_cancel_enter(curthread); ret = __sys_sigtimedwait(pset, info, timeout); _thr_cancel_leave(curthread); return (ret); } int _sigwaitinfo(const sigset_t *set, siginfo_t *info) { sigset_t newset; const sigset_t *pset; int ret; if (SIGISMEMBER(*set, SIGCANCEL)) { newset = *set; SIGDELSET(newset, SIGCANCEL); pset = &newset; } else pset = set; ret = __sys_sigwaitinfo(pset, info); return (ret); } int __sigwaitinfo(const sigset_t *set, siginfo_t *info) { struct pthread *curthread = _get_curthread(); sigset_t newset; const sigset_t *pset; int ret; if (SIGISMEMBER(*set, SIGCANCEL)) { newset = *set; SIGDELSET(newset, SIGCANCEL); pset = &newset; } else pset = set; _thr_cancel_enter(curthread); ret = __sys_sigwaitinfo(pset, info); _thr_cancel_leave(curthread); return (ret); } int _sigwait(const sigset_t *set, int *sig) { sigset_t newset; const sigset_t *pset; int ret; if (SIGISMEMBER(*set, SIGCANCEL)) { newset = *set; SIGDELSET(newset, SIGCANCEL); pset = &newset; } else pset = set; ret = __sys_sigwait(pset, sig); return (ret); } int __sigwait(const sigset_t *set, int *sig) { struct pthread *curthread = _get_curthread(); sigset_t newset; const sigset_t *pset; int ret; if (SIGISMEMBER(*set, SIGCANCEL)) { newset = *set; SIGDELSET(newset, SIGCANCEL); pset = &newset; } else pset = set; _thr_cancel_enter(curthread); ret = __sys_sigwait(pset, sig); _thr_cancel_leave(curthread); return (ret); } diff --git a/lib/libthr/thread/thr_suspend_np.c b/lib/libthr/thread/thr_suspend_np.c index 3e48656b44b5..7d4a2e231bbd 100644 --- a/lib/libthr/thread/thr_suspend_np.c +++ b/lib/libthr/thread/thr_suspend_np.c @@ -1,142 +1,142 @@ /* * 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. * * $FreeBSD$ */ #include "namespace.h" #include #include #include #include "un-namespace.h" #include "thr_private.h" static int suspend_common(struct pthread *, struct pthread *, int); __weak_reference(_pthread_suspend_np, pthread_suspend_np); __weak_reference(_pthread_suspend_all_np, pthread_suspend_all_np); /* Suspend a thread: */ int _pthread_suspend_np(pthread_t thread) { struct pthread *curthread = _get_curthread(); int ret; /* Suspending the current thread doesn't make sense. */ if (thread == _get_curthread()) ret = EDEADLK; /* Add a reference to the thread: */ else if ((ret = _thr_ref_add(curthread, thread, /*include dead*/0)) == 0) { /* Lock the threads scheduling queue: */ THR_THREAD_LOCK(curthread, thread); suspend_common(curthread, thread, 1); /* Unlock the threads scheduling queue: */ THR_THREAD_UNLOCK(curthread, thread); /* Don't forget to remove the reference: */ _thr_ref_delete(curthread, thread); } return (ret); } void _pthread_suspend_all_np(void) { struct pthread *curthread = _get_curthread(); struct pthread *thread; int ret; THREAD_LIST_LOCK(curthread); TAILQ_FOREACH(thread, &_thread_list, tle) { if (thread != curthread) { THR_THREAD_LOCK(curthread, thread); if (thread->state != PS_DEAD && !(thread->flags & THR_FLAGS_SUSPENDED)) thread->flags |= THR_FLAGS_NEED_SUSPEND; THR_THREAD_UNLOCK(curthread, thread); } } thr_kill(-1, SIGCANCEL); restart: TAILQ_FOREACH(thread, &_thread_list, tle) { if (thread != curthread) { /* First try to suspend the thread without waiting */ THR_THREAD_LOCK(curthread, thread); ret = suspend_common(curthread, thread, 0); if (ret == 0) { /* Can not suspend, try to wait */ thread->refcount++; THREAD_LIST_UNLOCK(curthread); suspend_common(curthread, thread, 1); THR_THREAD_UNLOCK(curthread, thread); THREAD_LIST_LOCK(curthread); _thr_ref_delete_unlocked(curthread, thread); /* * Because we were blocked, things may have * been changed, we have to restart the * process. */ goto restart; } THR_THREAD_UNLOCK(curthread, thread); } } THREAD_LIST_UNLOCK(curthread); } static int suspend_common(struct pthread *curthread, struct pthread *thread, int waitok) { long tmp; while (thread->state != PS_DEAD && !(thread->flags & THR_FLAGS_SUSPENDED)) { thread->flags |= THR_FLAGS_NEED_SUSPEND; tmp = thread->cycle; THR_THREAD_UNLOCK(curthread, thread); _thr_send_sig(thread, SIGCANCEL); if (waitok) { - _thr_umtx_wait(&thread->cycle, tmp, NULL); + _thr_umtx_wait_uint(&thread->cycle, tmp, NULL, 0); THR_THREAD_LOCK(curthread, thread); } else { THR_THREAD_LOCK(curthread, thread); return (0); } } return (1); } diff --git a/lib/libthr/thread/thr_umtx.c b/lib/libthr/thread/thr_umtx.c index ff8c60e2a9b6..3efe8847a1a8 100644 --- a/lib/libthr/thread/thr_umtx.c +++ b/lib/libthr/thread/thr_umtx.c @@ -1,165 +1,166 @@ /* * 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. * * $FreeBSD$ * */ #include "thr_private.h" #include "thr_umtx.h" #ifndef HAS__UMTX_OP_ERR int _umtx_op_err(void *obj, int op, u_long val, void *uaddr, void *uaddr2) { if (_umtx_op(obj, op, val, uaddr, uaddr2) == -1) return (errno); return (0); } #endif void _thr_umutex_init(struct umutex *mtx) { static struct umutex default_mtx = DEFAULT_UMUTEX; *mtx = default_mtx; } int __thr_umutex_lock(struct umutex *mtx) { return _umtx_op_err(mtx, UMTX_OP_MUTEX_LOCK, 0, 0, 0); } int __thr_umutex_timedlock(struct umutex *mtx, const struct timespec *timeout) { if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 && timeout->tv_nsec <= 0))) return (ETIMEDOUT); return _umtx_op_err(mtx, UMTX_OP_MUTEX_LOCK, 0, 0, __DECONST(void *, timeout)); } int __thr_umutex_unlock(struct umutex *mtx) { return _umtx_op_err(mtx, UMTX_OP_MUTEX_UNLOCK, 0, 0, 0); } int __thr_umutex_trylock(struct umutex *mtx) { return _umtx_op_err(mtx, UMTX_OP_MUTEX_TRYLOCK, 0, 0, 0); } int __thr_umutex_set_ceiling(struct umutex *mtx, uint32_t ceiling, uint32_t *oldceiling) { return _umtx_op_err(mtx, UMTX_OP_SET_CEILING, ceiling, oldceiling, 0); } int _thr_umtx_wait(volatile long *mtx, long id, const struct timespec *timeout) { if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 && timeout->tv_nsec <= 0))) return (ETIMEDOUT); return _umtx_op_err(__DEVOLATILE(void *, mtx), UMTX_OP_WAIT, id, 0, __DECONST(void*, timeout)); } int -_thr_umtx_wait_uint(volatile u_int *mtx, u_int id, const struct timespec *timeout) +_thr_umtx_wait_uint(volatile u_int *mtx, u_int id, const struct timespec *timeout, int shared) { if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 && timeout->tv_nsec <= 0))) return (ETIMEDOUT); - return _umtx_op_err(__DEVOLATILE(void *, mtx), UMTX_OP_WAIT_UINT, id, 0, - __DECONST(void*, timeout)); + return _umtx_op_err(__DEVOLATILE(void *, mtx), + shared ? UMTX_OP_WAIT_UINT : UMTX_OP_WAIT_UINT_PRIVATE, id, 0, + __DECONST(void*, timeout)); } int -_thr_umtx_wake(volatile void *mtx, int nr_wakeup) +_thr_umtx_wake(volatile void *mtx, int nr_wakeup, int shared) { - return _umtx_op_err(__DEVOLATILE(void *, mtx), UMTX_OP_WAKE, + return _umtx_op_err(__DEVOLATILE(void *, mtx), shared ? UMTX_OP_WAKE : UMTX_OP_WAKE_PRIVATE, nr_wakeup, 0, 0); } void _thr_ucond_init(struct ucond *cv) { bzero(cv, sizeof(struct ucond)); } int _thr_ucond_wait(struct ucond *cv, struct umutex *m, const struct timespec *timeout, int check_unparking) { if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 && timeout->tv_nsec <= 0))) { __thr_umutex_unlock(m); return (ETIMEDOUT); } return _umtx_op_err(cv, UMTX_OP_CV_WAIT, check_unparking ? UMTX_CHECK_UNPARKING : 0, m, __DECONST(void*, timeout)); } int _thr_ucond_signal(struct ucond *cv) { if (!cv->c_has_waiters) return (0); return _umtx_op_err(cv, UMTX_OP_CV_SIGNAL, 0, NULL, NULL); } int _thr_ucond_broadcast(struct ucond *cv) { if (!cv->c_has_waiters) return (0); return _umtx_op_err(cv, UMTX_OP_CV_BROADCAST, 0, NULL, NULL); } int __thr_rwlock_rdlock(struct urwlock *rwlock, int flags, struct timespec *tsp) { return _umtx_op_err(rwlock, UMTX_OP_RW_RDLOCK, flags, NULL, tsp); } int __thr_rwlock_wrlock(struct urwlock *rwlock, struct timespec *tsp) { return _umtx_op_err(rwlock, UMTX_OP_RW_WRLOCK, 0, NULL, tsp); } int __thr_rwlock_unlock(struct urwlock *rwlock) { return _umtx_op_err(rwlock, UMTX_OP_RW_UNLOCK, 0, NULL, NULL); } diff --git a/lib/libthr/thread/thr_umtx.h b/lib/libthr/thread/thr_umtx.h index 752e7b2fe4c9..0ef75a7e49e2 100644 --- a/lib/libthr/thread/thr_umtx.h +++ b/lib/libthr/thread/thr_umtx.h @@ -1,181 +1,181 @@ /*- * Copyright (c) 2005 David Xu * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _THR_FBSD_UMTX_H_ #define _THR_FBSD_UMTX_H_ #include #include #define DEFAULT_UMUTEX {0,0, {0,0},{0,0,0,0}} int __thr_umutex_lock(struct umutex *mtx) __hidden; int __thr_umutex_timedlock(struct umutex *mtx, const struct timespec *timeout) __hidden; int __thr_umutex_unlock(struct umutex *mtx) __hidden; int __thr_umutex_trylock(struct umutex *mtx) __hidden; int __thr_umutex_set_ceiling(struct umutex *mtx, uint32_t ceiling, uint32_t *oldceiling) __hidden; void _thr_umutex_init(struct umutex *mtx) __hidden; int _thr_umtx_wait(volatile long *mtx, long exp, const struct timespec *timeout) __hidden; int _thr_umtx_wait_uint(volatile u_int *mtx, u_int exp, - const struct timespec *timeout) __hidden; -int _thr_umtx_wake(volatile void *mtx, int count) __hidden; + const struct timespec *timeout, int shared) __hidden; +int _thr_umtx_wake(volatile void *mtx, int count, int shared) __hidden; int _thr_ucond_wait(struct ucond *cv, struct umutex *m, const struct timespec *timeout, int check_unpaking) __hidden; void _thr_ucond_init(struct ucond *cv) __hidden; int _thr_ucond_signal(struct ucond *cv) __hidden; int _thr_ucond_broadcast(struct ucond *cv) __hidden; int __thr_rwlock_rdlock(struct urwlock *rwlock, int flags, struct timespec *tsp) __hidden; int __thr_rwlock_wrlock(struct urwlock *rwlock, struct timespec *tsp) __hidden; int __thr_rwlock_unlock(struct urwlock *rwlock) __hidden; static inline int _thr_umutex_trylock(struct umutex *mtx, uint32_t id) { if (atomic_cmpset_acq_32(&mtx->m_owner, UMUTEX_UNOWNED, id)) return (0); if ((mtx->m_flags & UMUTEX_PRIO_PROTECT) == 0) return (EBUSY); return (__thr_umutex_trylock(mtx)); } static inline int _thr_umutex_trylock2(struct umutex *mtx, uint32_t id) { if (atomic_cmpset_acq_32(&mtx->m_owner, UMUTEX_UNOWNED, id)) return (0); return (EBUSY); } static inline int _thr_umutex_lock(struct umutex *mtx, uint32_t id) { if (atomic_cmpset_acq_32(&mtx->m_owner, UMUTEX_UNOWNED, id)) return (0); return (__thr_umutex_lock(mtx)); } static inline int _thr_umutex_timedlock(struct umutex *mtx, uint32_t id, const struct timespec *timeout) { if (atomic_cmpset_acq_32(&mtx->m_owner, UMUTEX_UNOWNED, id)) return (0); return (__thr_umutex_timedlock(mtx, timeout)); } static inline int _thr_umutex_unlock(struct umutex *mtx, uint32_t id) { if (atomic_cmpset_rel_32(&mtx->m_owner, id, UMUTEX_UNOWNED)) return (0); return (__thr_umutex_unlock(mtx)); } static inline int _thr_rwlock_tryrdlock(struct urwlock *rwlock, int flags) { int32_t state; int32_t wrflags; if (flags & URWLOCK_PREFER_READER || rwlock->rw_flags & URWLOCK_PREFER_READER) wrflags = URWLOCK_WRITE_OWNER; else wrflags = URWLOCK_WRITE_OWNER | URWLOCK_WRITE_WAITERS; state = rwlock->rw_state; while (!(state & wrflags)) { if (__predict_false(URWLOCK_READER_COUNT(state) == URWLOCK_MAX_READERS)) return (EAGAIN); if (atomic_cmpset_acq_32(&rwlock->rw_state, state, state + 1)) return (0); state = rwlock->rw_state; } return (EBUSY); } static inline int _thr_rwlock_trywrlock(struct urwlock *rwlock) { int32_t state; state = rwlock->rw_state; while (!(state & URWLOCK_WRITE_OWNER) && URWLOCK_READER_COUNT(state) == 0) { if (atomic_cmpset_acq_32(&rwlock->rw_state, state, state | URWLOCK_WRITE_OWNER)) return (0); state = rwlock->rw_state; } return (EBUSY); } static inline int _thr_rwlock_rdlock(struct urwlock *rwlock, int flags, struct timespec *tsp) { if (_thr_rwlock_tryrdlock(rwlock, flags) == 0) return (0); return (__thr_rwlock_rdlock(rwlock, flags, tsp)); } static inline int _thr_rwlock_wrlock(struct urwlock *rwlock, struct timespec *tsp) { if (_thr_rwlock_trywrlock(rwlock) == 0) return (0); return (__thr_rwlock_wrlock(rwlock, tsp)); } static inline int _thr_rwlock_unlock(struct urwlock *rwlock) { int32_t state; state = rwlock->rw_state; if (state & URWLOCK_WRITE_OWNER) { if (atomic_cmpset_rel_32(&rwlock->rw_state, URWLOCK_WRITE_OWNER, 0)) return (0); } else { for (;;) { if (__predict_false(URWLOCK_READER_COUNT(state) == 0)) return (EPERM); if (!((state & URWLOCK_WRITE_WAITERS) && URWLOCK_READER_COUNT(state) == 1)) { if (atomic_cmpset_rel_32(&rwlock->rw_state, state, state-1)) return (0); state = rwlock->rw_state; } else { break; } } } return (__thr_rwlock_unlock(rwlock)); } #endif