Index: head/lib/libkse/support/thr_support.c =================================================================== --- head/lib/libkse/support/thr_support.c (revision 116976) +++ head/lib/libkse/support/thr_support.c (revision 116977) @@ -1,58 +1,61 @@ /*- * Copyright 2003 Alexander Kabaev. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include "thr_private.h" __strong_reference(clock_gettime, _thr_clock_gettime); __strong_reference(kse_exit, _thr_kse_exit); __strong_reference(kse_wakeup, _thr_kse_wakeup); __strong_reference(kse_create, _thr_kse_create); __strong_reference(kse_thr_interrupt, _thr_kse_thr_interrupt); __strong_reference(kse_release, _thr_kse_release); __strong_reference(sigaction, _thr_sigaction); __strong_reference(sigprocmask, _thr_sigprocmask); __strong_reference(sigemptyset, _thr_sigemptyset); __strong_reference(sigaddset, _thr_sigaddset); __strong_reference(sigfillset, _thr_sigfillset); __strong_reference(sigismember, _thr_sigismember); __strong_reference(sigdelset, _thr_sigdelset); __strong_reference(memset, _thr_memset); __strong_reference(memcpy, _thr_memcpy); __strong_reference(strcpy, _thr_strcpy); __strong_reference(strlen, _thr_strlen); __strong_reference(bzero, _thr_bzero); +__strong_reference(bcopy, _thr_bcopy); __strong_reference(__sys_write, _thr__sys_write); +__strong_reference(__sys_sigtimedwait, _thr__sys_sigtimedwait); + Index: head/lib/libkse/thread/thr_info.c =================================================================== --- head/lib/libkse/thread/thr_info.c (revision 116976) +++ head/lib/libkse/thread/thr_info.c (revision 116977) @@ -1,205 +1,220 @@ /* * 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. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include "thr_private.h" #ifndef NELEMENTS #define NELEMENTS(arr) (sizeof(arr) / sizeof(arr[0])) #endif static void dump_thread(int fd, pthread_t pthread, int long_version); __weak_reference(_pthread_set_name_np, pthread_set_name_np); struct s_thread_info { enum pthread_state state; char *name; }; /* Static variables: */ static const struct s_thread_info thread_info[] = { {PS_RUNNING , "Running"}, {PS_LOCKWAIT , "Waiting on an internal lock"}, {PS_MUTEX_WAIT , "Waiting on a mutex"}, {PS_COND_WAIT , "Waiting on a condition variable"}, {PS_SLEEP_WAIT , "Sleeping"}, {PS_SIGSUSPEND , "Suspended, waiting for a signal"}, {PS_SIGWAIT , "Waiting for a signal"}, {PS_JOIN , "Waiting to join"}, {PS_SUSPENDED , "Suspended"}, {PS_DEAD , "Dead"}, {PS_DEADLOCK , "Deadlocked"}, {PS_STATE_MAX , "Not a real state!"} }; void _thread_dump_info(void) { char s[512], tmpfile[128]; pthread_t pthread; int fd, i; for (i = 0; i < 100000; i++) { - snprintf(tmpfile, sizeof(tmpfile), "/tmp/uthread.dump.%u.%i", + snprintf(tmpfile, sizeof(tmpfile), "/tmp/pthread.dump.%u.%i", getpid(), i); /* Open the dump file for append and create it if necessary: */ if ((fd = __sys_open(tmpfile, O_RDWR | O_CREAT | O_EXCL, 0666)) < 0) { /* Can't open the dump file. */ if (errno == EEXIST) continue; /* * We only need to continue in case of * EEXIT error. Most other error * codes means that we will fail all * the times. */ return; } else { break; } } if (i==100000) { /* all 100000 possibilities are in use :( */ return; } else { /* Dump the active threads. */ strcpy(s, "\n\n========\nACTIVE THREADS\n\n"); __sys_write(fd, s, strlen(s)); /* Enter a loop to report each thread in the global list: */ TAILQ_FOREACH(pthread, &_thread_list, tle) { if (pthread->state != PS_DEAD) dump_thread(fd, pthread, /*long_verson*/ 1); } /* * Dump the ready threads. * XXX - We can't easily do this because the run queues * are per-KSEG. */ strcpy(s, "\n\n========\nREADY THREADS - unimplemented\n\n"); __sys_write(fd, s, strlen(s)); /* * Dump the waiting threads. * XXX - We can't easily do this because the wait queues * are per-KSEG. */ strcpy(s, "\n\n========\nWAITING THREADS - unimplemented\n\n"); __sys_write(fd, s, strlen(s)); /* Close the dump file. */ __sys_close(fd); } } static void dump_thread(int fd, pthread_t pthread, int long_version) { struct pthread *curthread = _get_curthread(); char s[512]; int i; /* Find the state: */ for (i = 0; i < NELEMENTS(thread_info) - 1; i++) if (thread_info[i].state == pthread->state) break; /* Output a record for the thread: */ snprintf(s, sizeof(s), "--------------------\n" "Thread %p (%s) prio %3d, blocked %s, state %s [%s:%d]\n", pthread, (pthread->name == NULL) ? "" : pthread->name, pthread->active_priority, (pthread->blocked != 0) ? "yes" : "no", thread_info[i].name, pthread->fname, pthread->lineno); __sys_write(fd, s, strlen(s)); if (long_version != 0) { /* Check if this is the running thread: */ if (pthread == curthread) { /* Output a record for the running thread: */ strcpy(s, "This is the running thread\n"); __sys_write(fd, s, strlen(s)); } /* Check if this is the initial thread: */ if (pthread == _thr_initial) { /* Output a record for the initial thread: */ strcpy(s, "This is the initial thread\n"); __sys_write(fd, s, strlen(s)); } + /* Check if this is the signal daemon thread: */ + if (pthread == _thr_sig_daemon) { + /* Output a record for the signal thread: */ + strcpy(s, "This is the signal daemon thread\n"); + __sys_write(fd, s, strlen(s)); + } /* Process according to thread state: */ switch (pthread->state) { case PS_SIGWAIT: snprintf(s, sizeof(s), "sigmask (hi)"); + __sys_write(fd, s, strlen(s)); + for (i = _SIG_WORDS - 1; i >= 0; i--) { + snprintf(s, sizeof(s), "%08x\n", + pthread->oldsigmask.__bits[i]); + __sys_write(fd, s, strlen(s)); + } + snprintf(s, sizeof(s), "(lo)\n"); + __sys_write(fd, s, strlen(s)); + snprintf(s, sizeof(s), "waitset (hi)"); __sys_write(fd, s, strlen(s)); for (i = _SIG_WORDS - 1; i >= 0; i--) { snprintf(s, sizeof(s), "%08x\n", pthread->sigmask.__bits[i]); __sys_write(fd, s, strlen(s)); } snprintf(s, sizeof(s), "(lo)\n"); __sys_write(fd, s, strlen(s)); break; /* * Trap other states that are not explicitly * coded to dump information: */ default: /* Nothing to do here. */ break; } } } /* Set the thread name for debug: */ void _pthread_set_name_np(pthread_t thread, char *name) { /* Check if the caller has specified a valid thread: */ if (thread != NULL && thread->magic == THR_MAGIC) { if (thread->name != NULL) { /* Free space for previous name. */ free(thread->name); } thread->name = strdup(name); } } Index: head/lib/libkse/thread/thr_init.c =================================================================== --- head/lib/libkse/thread/thr_init.c (revision 116976) +++ head/lib/libkse/thread/thr_init.c (revision 116977) @@ -1,530 +1,495 @@ /* * 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. * * $FreeBSD$ */ /* Allocate space for global thread variables here: */ #define GLOBAL_PTHREAD_PRIVATE #include "namespace.h" #include #include +#include #include #include #include #include #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" #include "ksd.h" 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); static void init_private(void); static void init_main_thread(struct pthread *thread); /* * All weak references used within libc should be in this table. * This is so that static libraries will work. */ static void *references[] = { &_accept, &_bind, &_close, &_connect, &_dup, &_dup2, &_execve, &_fcntl, &_flock, &_flockfile, &_fstat, &_fstatfs, &_fsync, &_funlockfile, &_getdirentries, &_getlogin, &_getpeername, &_getsockname, &_getsockopt, &_ioctl, &_kevent, &_listen, &_nanosleep, &_open, &_pthread_getspecific, &_pthread_key_create, &_pthread_key_delete, &_pthread_mutex_destroy, &_pthread_mutex_init, &_pthread_mutex_lock, &_pthread_mutex_trylock, &_pthread_mutex_unlock, &_pthread_mutexattr_init, &_pthread_mutexattr_destroy, &_pthread_mutexattr_settype, &_pthread_once, &_pthread_setspecific, &_read, &_readv, &_recvfrom, &_recvmsg, &_select, &_sendmsg, &_sendto, &_setsockopt, &_sigaction, &_sigprocmask, &_sigsuspend, &_socket, &_socketpair, &_thread_init_hack, &_wait4, &_write, &_writev }; /* * These are needed when linking statically. All references within * libgcc (and in the future libc) to these routines are weak, but * if they are not (strongly) referenced by the application or other * libraries, then the actual functions will not be loaded. */ static void *libgcc_references[] = { &_pthread_once, &_pthread_key_create, &_pthread_key_delete, &_pthread_getspecific, &_pthread_setspecific, &_pthread_mutex_init, &_pthread_mutex_destroy, &_pthread_mutex_lock, &_pthread_mutex_trylock, &_pthread_mutex_unlock }; #define DUAL_ENTRY(entry) \ (pthread_func_t)entry, (pthread_func_t)entry static pthread_func_t jmp_table[][2] = { {DUAL_ENTRY(_pthread_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 */ {(pthread_func_t)__pthread_cond_wait, (pthread_func_t)_pthread_cond_wait}, /* PJT_COND_WAIT */ {DUAL_ENTRY(_pthread_getspecific)}, /* PJT_GETSPECIFIC */ {DUAL_ENTRY(_pthread_key_create)}, /* PJT_KEY_CREATE */ {DUAL_ENTRY(_pthread_key_delete)}, /* PJT_KEY_DELETE*/ {DUAL_ENTRY(_pthread_main_np)}, /* PJT_MAIN_NP */ {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_mutexattr_destroy)}, /* PJT_MUTEXATTR_DESTROY */ {DUAL_ENTRY(_pthread_mutexattr_init)}, /* PJT_MUTEXATTR_INIT */ {DUAL_ENTRY(_pthread_mutexattr_settype)}, /* PJT_MUTEXATTR_SETTYPE */ {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_setspecific)}, /* PJT_SETSPECIFIC */ {DUAL_ENTRY(_pthread_sigmask)} /* PJT_SIGMASK */ }; static int init_once = 0; /* * Threaded process initialization. * * This is only called under two conditions: * * 1) Some thread routines have detected that the library hasn't yet * been initialized (_thr_initial == NULL && curthread == NULL), or * * 2) An explicit call to reinitialize after a fork (indicated * by curthread != NULL) */ void _libpthread_init(struct pthread *curthread) { int fd; /* Check if this function has already been called: */ if ((_thr_initial != NULL) && (curthread == NULL)) /* Only initialize the threaded application once. */ return; /* * Make gcc quiescent about {,libgcc_}references not being * referenced: */ if ((references[0] == NULL) || (libgcc_references[0] == NULL)) PANIC("Failed loading mandatory references in _thread_init"); /* * Check the size of the jump table to make sure it is preset * with the correct number of entries. */ if (sizeof(jmp_table) != (sizeof(pthread_func_t) * PJT_MAX * 2)) PANIC("Thread jump table not properly initialized"); memcpy(__thr_jtable, jmp_table, sizeof(jmp_table)); /* * Check for the special case of this process running as * or in place of init as pid = 1: */ if ((_thr_pid = getpid()) == 1) { /* * Setup a new session for this process which is * assumed to be running as root. */ if (setsid() == -1) PANIC("Can't set session ID"); if (revoke(_PATH_CONSOLE) != 0) PANIC("Can't revoke console"); if ((fd = __sys_open(_PATH_CONSOLE, O_RDWR)) < 0) PANIC("Can't open console"); if (setlogin("root") == -1) PANIC("Can't set login to root"); if (__sys_ioctl(fd, TIOCSCTTY, (char *) NULL) == -1) PANIC("Can't set controlling terminal"); } /* Initialize pthread private data. */ init_private(); _kse_init(); /* Initialize the initial kse and kseg. */ _kse_initial = _kse_alloc(NULL); if (_kse_initial == NULL) PANIC("Can't allocate initial kse."); _kse_initial->k_kseg = _kseg_alloc(NULL); if (_kse_initial->k_kseg == NULL) PANIC("Can't allocate initial kseg."); _kse_initial->k_schedq = &_kse_initial->k_kseg->kg_schedq; TAILQ_INSERT_TAIL(&_kse_initial->k_kseg->kg_kseq, _kse_initial, k_kgqe); _kse_initial->k_kseg->kg_ksecount = 1; /* Set the initial thread. */ if (curthread == NULL) { /* Create and initialize the initial thread. */ curthread = _thr_alloc(NULL); if (curthread == NULL) PANIC("Can't allocate initial thread"); _thr_initial = curthread; init_main_thread(curthread); } else { /* * The initial thread is the current thread. It is * assumed that the current thread is already initialized * because it is left over from a fork(). */ _thr_initial = curthread; } _kse_initial->k_kseg->kg_threadcount = 0; _thr_initial->kse = _kse_initial; _thr_initial->kseg = _kse_initial->k_kseg; _thr_initial->active = 1; /* * Add the thread to the thread list and to the KSEG's thread * queue. */ THR_LIST_ADD(_thr_initial); KSEG_THRQ_ADD(_kse_initial->k_kseg, _thr_initial); /* Setup the KSE/thread specific data for the current KSE/thread. */ if (_ksd_setprivate(&_thr_initial->kse->k_ksd) != 0) PANIC("Can't set initial KSE specific data"); _set_curkse(_thr_initial->kse); _thr_initial->kse->k_curthread = _thr_initial; _thr_initial->kse->k_flags |= KF_INITIALIZED; _kse_initial->k_curthread = _thr_initial; - + _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) { void *p; int i; /* Zero the initial thread structure. */ p = thread->alloc_addr; memset(thread, 0, sizeof(struct pthread)); thread->alloc_addr = p; /* Setup the thread attributes. */ 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((void *)_usrstack - THR_STACK_INITIAL - _thr_guard_default, _thr_guard_default, 0, MAP_ANON, -1, 0) == MAP_FAILED) PANIC("Cannot allocate red zone for initial thread"); /* * Mark the stack as an application supplied stack so that it * isn't deallocated. * * XXX - I'm not sure it would hurt anything to deallocate * the main thread stack because deallocation doesn't * actually free() it; it just puts it in the free * stack queue for later reuse. */ thread->attr.stackaddr_attr = (void *)_usrstack - THR_STACK_INITIAL; thread->attr.stacksize_attr = THR_STACK_INITIAL; thread->attr.guardsize_attr = _thr_guard_default; thread->attr.flags |= THR_STACK_USER; /* * Write a magic value to the thread structure * to help identify valid ones: */ thread->magic = THR_MAGIC; thread->slice_usec = -1; thread->cancelflags = PTHREAD_CANCEL_ENABLE | PTHREAD_CANCEL_DEFERRED; thread->name = strdup("initial thread"); /* Initialize the thread for signals: */ - sigemptyset(&thread->sigmask); + SIGEMPTYSET(thread->sigmask); /* * Set up the thread mailbox. The threads saved context * is also in the mailbox. */ thread->tmbx.tm_udata = thread; - thread->tmbx.tm_context.uc_sigmask = thread->sigmask; thread->tmbx.tm_context.uc_stack.ss_size = thread->attr.stacksize_attr; thread->tmbx.tm_context.uc_stack.ss_sp = thread->attr.stackaddr_attr; /* Default the priority of the initial thread: */ thread->base_priority = THR_DEFAULT_PRIORITY; thread->active_priority = THR_DEFAULT_PRIORITY; thread->inherited_priority = 0; /* Initialize the mutex queue: */ TAILQ_INIT(&thread->mutexq); /* Initialize thread locking. */ if (_lock_init(&thread->lock, LCK_ADAPTIVE, _thr_lock_wait, _thr_lock_wakeup) != 0) PANIC("Cannot initialize initial thread lock"); for (i = 0; i < MAX_THR_LOCKLEVEL; i++) { _lockuser_init(&thread->lockusers[i], (void *)thread); _LCK_SET_PRIVATE2(&thread->lockusers[i], (void *)thread); } /* Initialize hooks in the thread structure: */ thread->specific = NULL; thread->cleanup = NULL; thread->flags = 0; thread->continuation = NULL; thread->state = PS_RUNNING; thread->uniqueid = 0; } static void init_private(void) { struct clockinfo clockinfo; - struct sigaction act; size_t len; int mib[2]; - int i; /* * Avoid reinitializing some things if they don't need to be, * e.g. after a fork(). */ if (init_once == 0) { /* Find the stack top */ mib[0] = CTL_KERN; mib[1] = KERN_USRSTACK; len = sizeof (_usrstack); if (sysctl(mib, 2, &_usrstack, &len, NULL, 0) == -1) PANIC("Cannot get kern.usrstack from sysctl"); /* * Create a red zone below the main stack. All other * stacks are constrained to a maximum size by the * parameters passed to mmap(), but this stack is only * limited by resource limits, so this stack needs an * explicitly mapped red zone to protect the thread stack * that is just beyond. */ if (mmap((void *)_usrstack - THR_STACK_INITIAL - _thr_guard_default, _thr_guard_default, 0, MAP_ANON, -1, 0) == MAP_FAILED) PANIC("Cannot allocate red zone for initial thread"); /* Get the kernel clockrate: */ mib[0] = CTL_KERN; mib[1] = KERN_CLOCKRATE; len = sizeof (struct clockinfo); if (sysctl(mib, 2, &clockinfo, &len, NULL, 0) == 0) _clock_res_usec = clockinfo.tick; else _clock_res_usec = CLOCK_RES_USEC; _thr_page_size = getpagesize(); _thr_guard_default = _thr_page_size; - - /* Enter a loop to get the existing signal status: */ - for (i = 1; i < NSIG; i++) { - /* Check for signals which cannot be trapped: */ - if (i == SIGKILL || i == SIGSTOP) { - } - - /* Get the signal handler details: */ - else if (__sys_sigaction(i, NULL, - &_thread_sigact[i - 1]) != 0) { - /* - * Abort this process if signal - * initialisation fails: - */ - PANIC("Cannot read signal handler info"); - } - } - /* - * Install the signal handler for SIGINFO. It isn't - * really needed, but it is nice to have for debugging - * purposes. - */ - if (__sys_sigaction(SIGINFO, &act, NULL) != 0) { - /* - * Abort this process if signal initialisation fails: - */ - PANIC("Cannot initialize signal handler"); - } - _thread_sigact[SIGINFO - 1].sa_flags = SA_SIGINFO | SA_RESTART; - init_once = 1; /* Don't do this again. */ } else { /* * Destroy the locks before creating them. We don't * know what state they are in so it is better to just * recreate them. */ _lock_destroy(&_thread_signal_lock); _lock_destroy(&_mutex_static_lock); _lock_destroy(&_rwlock_static_lock); _lock_destroy(&_keytable_lock); } /* Initialize everything else. */ TAILQ_INIT(&_thread_list); TAILQ_INIT(&_thread_gc_list); - /* Enter a loop to get the existing signal status: */ - /* Initialize the SIG_DFL dummy handler count. */ bzero(_thread_dfl_count, sizeof(_thread_dfl_count)); /* * Initialize the lock for temporary installation of signal * handlers (to support sigwait() semantics) and for the * process signal mask and pending signal sets. */ if (_lock_init(&_thread_signal_lock, LCK_ADAPTIVE, _kse_lock_wait, _kse_lock_wakeup) != 0) PANIC("Cannot initialize _thread_signal_lock"); if (_lock_init(&_mutex_static_lock, LCK_ADAPTIVE, _thr_lock_wait, _thr_lock_wakeup) != 0) PANIC("Cannot initialize mutex static init lock"); if (_lock_init(&_rwlock_static_lock, LCK_ADAPTIVE, _thr_lock_wait, _thr_lock_wakeup) != 0) PANIC("Cannot initialize rwlock static init lock"); if (_lock_init(&_keytable_lock, LCK_ADAPTIVE, _thr_lock_wait, _thr_lock_wakeup) != 0) PANIC("Cannot initialize thread specific keytable lock"); _thr_spinlock_init(); /* Clear pending signals and get the process signal mask. */ - sigemptyset(&_thr_proc_sigpending); - __sys_sigprocmask(SIG_SETMASK, NULL, &_thr_proc_sigmask); + SIGEMPTYSET(_thr_proc_sigpending); /* * _thread_list_lock and _kse_count are initialized * by _kse_init() */ } Index: head/lib/libkse/thread/thr_kern.c =================================================================== --- head/lib/libkse/thread/thr_kern.c (revision 116976) +++ head/lib/libkse/thread/thr_kern.c (revision 116977) @@ -1,2210 +1,2252 @@ /* * Copyright (C) 2003 Daniel M. Eischen * Copyright (C) 2002 Jonathon Mini * 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 #include #include #include #include +#include #include #include #include #include #include #include #include #include #include "atomic_ops.h" #include "thr_private.h" #include "libc_private.h" #include "ksd.h" /*#define DEBUG_THREAD_KERN */ #ifdef DEBUG_THREAD_KERN #define DBG_MSG stdout_debug #else #define DBG_MSG(x...) #endif /* * Define a high water mark for the maximum number of threads that * will be cached. Once this level is reached, any extra threads * will be free()'d. * * XXX - It doesn't make sense to worry about the maximum number of * KSEs that we can cache because the system will limit us to * something *much* less than the maximum number of threads * that we can have. Disregarding KSEs in their own group, * the maximum number of KSEs is the number of processors in * the system. */ #define MAX_CACHED_THREADS 100 #define KSE_STACKSIZE 16384 #define KSE_SET_MBOX(kse, thrd) \ (kse)->k_mbx.km_curthread = &(thrd)->tmbx #define KSE_SET_EXITED(kse) (kse)->k_flags |= KF_EXITED /* * Macros for manipulating the run queues. The priority queue * routines use the thread's pqe link and also handle the setting * and clearing of the thread's THR_FLAGS_IN_RUNQ flag. */ #define KSE_RUNQ_INSERT_HEAD(kse, thrd) \ _pq_insert_head(&(kse)->k_schedq->sq_runq, thrd) #define KSE_RUNQ_INSERT_TAIL(kse, thrd) \ _pq_insert_tail(&(kse)->k_schedq->sq_runq, thrd) #define KSE_RUNQ_REMOVE(kse, thrd) \ _pq_remove(&(kse)->k_schedq->sq_runq, thrd) #define KSE_RUNQ_FIRST(kse) _pq_first(&(kse)->k_schedq->sq_runq) #define KSE_RUNQ_THREADS(kse) ((kse)->k_schedq->sq_runq.pq_threads) /* * We've got to keep track of everything that is allocated, not only * to have a speedy free list, but also so they can be deallocated * after a fork(). */ static TAILQ_HEAD(, kse) active_kseq; static TAILQ_HEAD(, kse) free_kseq; static TAILQ_HEAD(, kse_group) free_kse_groupq; static TAILQ_HEAD(, kse_group) active_kse_groupq; static TAILQ_HEAD(, kse_group) gc_ksegq; static struct lock kse_lock; /* also used for kseg queue */ static int free_kse_count = 0; static int free_kseg_count = 0; static TAILQ_HEAD(, pthread) free_threadq; static struct lock thread_lock; static int free_thread_count = 0; static int inited = 0; static int active_threads = 1; static int active_kse_count = 0; static int active_kseg_count = 0; static u_int64_t next_uniqueid = 1; #ifdef DEBUG_THREAD_KERN static void dump_queues(struct kse *curkse); #endif static void kse_check_completed(struct kse *kse); static void kse_check_waitq(struct kse *kse); -static void kse_check_signals(struct kse *kse); static void kse_fini(struct kse *curkse); static void kse_reinit(struct kse *kse); static void kse_sched_multi(struct kse *curkse); #ifdef NOT_YET static void kse_sched_single(struct kse *curkse); #endif static void kse_switchout_thread(struct kse *kse, struct pthread *thread); static void kse_wait(struct kse *kse, struct pthread *td_wait); static void kse_free_unlocked(struct kse *kse); static void kseg_free_unlocked(struct kse_group *kseg); static void kseg_init(struct kse_group *kseg); static void kseg_reinit(struct kse_group *kseg); static void kse_waitq_insert(struct pthread *thread); static void kse_wakeup_multi(struct kse *curkse); static void kse_wakeup_one(struct pthread *thread); static void thr_cleanup(struct kse *kse, struct pthread *curthread); static void thr_link(struct pthread *thread); -static void thr_resume_wrapper(int unused_1, siginfo_t *unused_2, - ucontext_t *ucp); +static void thr_resume_wrapper(int sig, siginfo_t *, ucontext_t *); static void thr_resume_check(struct pthread *curthread, ucontext_t *ucp, struct pthread_sigframe *psf); static int thr_timedout(struct pthread *thread, struct timespec *curtime); static void thr_unlink(struct pthread *thread); + /* * This is called after a fork(). * No locks need to be taken here since we are guaranteed to be * single threaded. + * + * XXX + * POSIX says for threaded process, fork() function is used + * only to run new programs, and the effects of calling functions + * that require certain resources between the call to fork() and + * the call to an exec function are undefined. + * + * Here it is not safe to reinitialize the library after fork(). + * Because memory management may be corrupted, further calling + * malloc()/free() may cause undefined behavior. */ void _kse_single_thread(struct pthread *curthread) { +#ifdef NOTYET struct kse *kse; struct kse_group *kseg; struct pthread *thread; kse_critical_t crit; int i; + /* * Disable upcalls and clear the threaded flag. * XXX - I don't think we need to disable upcalls after a fork(). * but it doesn't hurt. */ crit = _kse_critical_enter(); __isthreaded = 0; active_threads = 1; + _thr_signal_deinit(); /* * Enter a loop to remove and free all threads other than * the running thread from the active thread list: */ while ((thread = TAILQ_FIRST(&_thread_list)) != NULL) { THR_GCLIST_REMOVE(thread); /* * Remove this thread from the list (the current * thread will be removed but re-added by libpthread * initialization. */ TAILQ_REMOVE(&_thread_list, thread, tle); /* Make sure this isn't the running thread: */ if (thread != curthread) { _thr_stack_free(&thread->attr); if (thread->specific != NULL) free(thread->specific); for (i = 0; i < MAX_THR_LOCKLEVEL; i++) { _lockuser_destroy(&thread->lockusers[i]); } _lock_destroy(&thread->lock); free(thread); } } TAILQ_INIT(&curthread->mutexq); /* initialize mutex queue */ curthread->joiner = NULL; /* no joining threads yet */ curthread->refcount = 0; - sigemptyset(&curthread->sigpend); /* clear pending signals */ + SIGEMPTYSET(curthread->sigpend); /* clear pending signals */ if (curthread->specific != NULL) { free(curthread->specific); curthread->specific = NULL; curthread->specific_data_count = 0; } /* Free the free KSEs: */ while ((kse = TAILQ_FIRST(&free_kseq)) != NULL) { TAILQ_REMOVE(&free_kseq, kse, k_qe); for (i = 0; i < MAX_KSE_LOCKLEVEL; i++) { _lockuser_destroy(&kse->k_lockusers[i]); } _lock_destroy(&kse->k_lock); _ksd_destroy(&kse->k_ksd); if (kse->k_stack.ss_sp != NULL) free(kse->k_stack.ss_sp); free(kse); } free_kse_count = 0; /* Free the active KSEs: */ while ((kse = TAILQ_FIRST(&active_kseq)) != NULL) { TAILQ_REMOVE(&active_kseq, kse, k_qe); for (i = 0; i < MAX_KSE_LOCKLEVEL; i++) { _lockuser_destroy(&kse->k_lockusers[i]); } _lock_destroy(&kse->k_lock); if (kse->k_stack.ss_sp != NULL) free(kse->k_stack.ss_sp); free(kse); } active_kse_count = 0; /* Free the free KSEGs: */ while ((kseg = TAILQ_FIRST(&free_kse_groupq)) != NULL) { TAILQ_REMOVE(&free_kse_groupq, kseg, kg_qe); _lock_destroy(&kseg->kg_lock); _pq_free(&kseg->kg_schedq.sq_runq); free(kseg); } free_kseg_count = 0; /* Free the active KSEGs: */ while ((kseg = TAILQ_FIRST(&active_kse_groupq)) != NULL) { TAILQ_REMOVE(&active_kse_groupq, kseg, kg_qe); _lock_destroy(&kseg->kg_lock); _pq_free(&kseg->kg_schedq.sq_runq); free(kseg); } active_kseg_count = 0; /* Free the free threads. */ while ((thread = TAILQ_FIRST(&free_threadq)) != NULL) { TAILQ_REMOVE(&free_threadq, thread, tle); if (thread->specific != NULL) free(thread->specific); for (i = 0; i < MAX_THR_LOCKLEVEL; i++) { _lockuser_destroy(&thread->lockusers[i]); } _lock_destroy(&thread->lock); free(thread); } free_thread_count = 0; /* Free the to-be-gc'd threads. */ while ((thread = TAILQ_FIRST(&_thread_gc_list)) != NULL) { TAILQ_REMOVE(&_thread_gc_list, thread, gcle); for (i = 0; i < MAX_THR_LOCKLEVEL; i++) { _lockuser_destroy(&thread->lockusers[i]); } _lock_destroy(&thread->lock); free(thread); } TAILQ_INIT(&gc_ksegq); _gc_count = 0; if (inited != 0) { /* * Destroy these locks; they'll be recreated to assure they * are in the unlocked state. */ _lock_destroy(&kse_lock); _lock_destroy(&thread_lock); _lock_destroy(&_thread_list_lock); inited = 0; } /* * After a fork(), the leftover thread goes back to being * scope process. */ curthread->attr.flags &= ~PTHREAD_SCOPE_SYSTEM; curthread->attr.flags |= PTHREAD_SCOPE_PROCESS; /* * After a fork, we are still operating on the thread's original * stack. Don't clear the THR_FLAGS_USER from the thread's * attribute flags. */ /* Initialize the threads library. */ curthread->kse = NULL; curthread->kseg = NULL; _kse_initial = NULL; _libpthread_init(curthread); +#else + _ksd_readandclear_tmbx(); + __isthreaded = 0; + active_threads = 0; + _thr_signal_deinit(); +#endif } /* * This is used to initialize housekeeping and to initialize the * KSD for the KSE. */ void _kse_init(void) { if (inited == 0) { TAILQ_INIT(&active_kseq); TAILQ_INIT(&active_kse_groupq); TAILQ_INIT(&free_kseq); TAILQ_INIT(&free_kse_groupq); TAILQ_INIT(&free_threadq); TAILQ_INIT(&gc_ksegq); if (_lock_init(&kse_lock, LCK_ADAPTIVE, _kse_lock_wait, _kse_lock_wakeup) != 0) PANIC("Unable to initialize free KSE queue lock"); if (_lock_init(&thread_lock, LCK_ADAPTIVE, _kse_lock_wait, _kse_lock_wakeup) != 0) PANIC("Unable to initialize free thread queue lock"); if (_lock_init(&_thread_list_lock, LCK_ADAPTIVE, _kse_lock_wait, _kse_lock_wakeup) != 0) PANIC("Unable to initialize thread list lock"); active_kse_count = 0; active_kseg_count = 0; _gc_count = 0; inited = 1; } } int _kse_isthreaded(void) { return (__isthreaded != 0); } /* * This is called when the first thread (other than the initial * thread) is created. */ int _kse_setthreaded(int threaded) { if ((threaded != 0) && (__isthreaded == 0)) { /* * Locking functions in libc are required when there are * threads other than the initial thread. */ __isthreaded = 1; /* * Tell the kernel to create a KSE for the initial thread * and enable upcalls in it. */ + _thr_signal_init(); _kse_initial->k_flags |= KF_STARTED; if (kse_create(&_kse_initial->k_mbx, 0) != 0) { _kse_initial->k_flags &= ~KF_STARTED; __isthreaded = 0; /* may abort() */ - DBG_MSG("kse_create failed\n"); + PANIC("kse_create() failed\n"); return (-1); } KSE_SET_MBOX(_kse_initial, _thr_initial); + _thr_start_sig_daemon(); _thr_setmaxconcurrency(); } return (0); } /* * Lock wait and wakeup handlers for KSE locks. These are only used by * KSEs, and should never be used by threads. KSE locks include the * KSE group lock (used for locking the scheduling queue) and the * kse_lock defined above. * * When a KSE lock attempt blocks, the entire KSE blocks allowing another * KSE to run. For the most part, it doesn't make much sense to try and * schedule another thread because you need to lock the scheduling queue * in order to do that. And since the KSE lock is used to lock the scheduling * queue, you would just end up blocking again. */ void _kse_lock_wait(struct lock *lock, struct lockuser *lu) { struct kse *curkse = (struct kse *)_LCK_GET_PRIVATE(lu); struct timespec ts; int saved_flags; if (curkse->k_mbx.km_curthread != NULL) PANIC("kse_lock_wait does not disable upcall.\n"); /* * Enter a loop to wait until we get the lock. */ ts.tv_sec = 0; ts.tv_nsec = 1000000; /* 1 sec */ while (!_LCK_GRANTED(lu)) { /* * Yield the kse and wait to be notified when the lock * is granted. */ saved_flags = curkse->k_mbx.km_flags; curkse->k_mbx.km_flags |= KMF_NOUPCALL | KMF_NOCOMPLETED; kse_release(&ts); curkse->k_mbx.km_flags = saved_flags; } } void _kse_lock_wakeup(struct lock *lock, struct lockuser *lu) { struct kse *curkse; struct kse *kse; struct kse_mailbox *mbx; curkse = _get_curkse(); kse = (struct kse *)_LCK_GET_PRIVATE(lu); if (kse == curkse) PANIC("KSE trying to wake itself up in lock"); else { mbx = &kse->k_mbx; _lock_grant(lock, lu); /* * Notify the owning kse that it has the lock. * It is safe to pass invalid address to kse_wakeup * even if the mailbox is not in kernel at all, * and waking up a wrong kse is also harmless. */ kse_wakeup(mbx); } } /* * Thread wait and wakeup handlers for thread locks. These are only used * by threads, never by KSEs. Thread locks include the per-thread lock * (defined in its structure), and condition variable and mutex locks. */ void _thr_lock_wait(struct lock *lock, struct lockuser *lu) { struct pthread *curthread = (struct pthread *)lu->lu_private; do { THR_SCHED_LOCK(curthread, curthread); THR_SET_STATE(curthread, PS_LOCKWAIT); THR_SCHED_UNLOCK(curthread, curthread); _thr_sched_switch(curthread); } while (!_LCK_GRANTED(lu)); } void _thr_lock_wakeup(struct lock *lock, struct lockuser *lu) { struct pthread *thread; struct pthread *curthread; curthread = _get_curthread(); thread = (struct pthread *)_LCK_GET_PRIVATE(lu); THR_SCHED_LOCK(curthread, thread); _lock_grant(lock, lu); _thr_setrunnable_unlocked(thread); THR_SCHED_UNLOCK(curthread, thread); } kse_critical_t _kse_critical_enter(void) { kse_critical_t crit; crit = _ksd_readandclear_tmbx(); return (crit); } void _kse_critical_leave(kse_critical_t crit) { struct pthread *curthread; _ksd_set_tmbx(crit); if ((crit != NULL) && ((curthread = _get_curthread()) != NULL)) THR_YIELD_CHECK(curthread); } int _kse_in_critical(void) { return (_ksd_get_tmbx() == NULL); } void _thr_critical_enter(struct pthread *thread) { thread->critical_count++; } void _thr_critical_leave(struct pthread *thread) { thread->critical_count--; THR_YIELD_CHECK(thread); } void _thr_sched_switch(struct pthread *curthread) { struct kse *curkse; (void)_kse_critical_enter(); curkse = _get_curkse(); KSE_SCHED_LOCK(curkse, curkse->k_kseg); _thr_sched_switch_unlocked(curthread); } /* * XXX - We may need to take the scheduling lock before calling * this, or perhaps take the lock within here before * doing anything else. */ void _thr_sched_switch_unlocked(struct pthread *curthread) { struct pthread *td; struct pthread_sigframe psf; struct kse *curkse; int ret; volatile int uts_once; volatile int resume_once = 0; + ucontext_t uc; /* We're in the scheduler, 5 by 5: */ curkse = _get_curkse(); curthread->need_switchout = 1; /* The thread yielded on its own. */ curthread->critical_yield = 0; /* No need to yield anymore. */ curthread->slice_usec = -1; /* Restart the time slice. */ /* Thread can unlock the scheduler lock. */ curthread->lock_switch = 1; /* * The signal frame is allocated off the stack because * a thread can be interrupted by other signals while * it is running down pending signals. */ - sigemptyset(&psf.psf_sigset); + psf.psf_valid = 0; curthread->curframe = &psf; /* * Enter the scheduler if any one of the following is true: * * o The current thread is dead; it's stack needs to be * cleaned up and it can't be done while operating on * it. + * o The current thread has signals pending, should + * let scheduler install signal trampoline for us. * o There are no runnable threads. * o The next thread to run won't unlock the scheduler * lock. A side note: the current thread may be run * instead of the next thread in the run queue, but * we don't bother checking for that. */ if ((curthread->state == PS_DEAD) || (((td = KSE_RUNQ_FIRST(curkse)) == NULL) && (curthread->state != PS_RUNNING)) || - ((td != NULL) && (td->lock_switch == 0))) + ((td != NULL) && (td->lock_switch == 0))) { + curkse->k_switch = 1; _thread_enter_uts(&curthread->tmbx, &curkse->k_mbx); + } else { uts_once = 0; THR_GETCONTEXT(&curthread->tmbx.tm_context); if (uts_once == 0) { uts_once = 1; /* Switchout the current thread. */ kse_switchout_thread(curkse, curthread); /* Choose another thread to run. */ td = KSE_RUNQ_FIRST(curkse); KSE_RUNQ_REMOVE(curkse, td); curkse->k_curthread = td; /* * Make sure the current thread's kse points to * this kse. */ td->kse = curkse; /* * Reset accounting. */ td->tmbx.tm_uticks = 0; td->tmbx.tm_sticks = 0; /* * Reset the time slice if this thread is running * for the first time or running again after using * its full time slice allocation. */ if (td->slice_usec == -1) td->slice_usec = 0; /* Mark the thread active. */ td->active = 1; /* Remove the frame reference. */ td->curframe = NULL; /* * Continue the thread at its current frame: */ ret = _thread_switch(&td->tmbx, NULL); /* This point should not be reached. */ if (ret != 0) PANIC("Bad return from _thread_switch"); PANIC("Thread has returned from _thread_switch"); } } + if (psf.psf_valid) { + /* + * It is ugly we must increase critical count, because we + * have a frame saved, we must backout state in psf + * before we can process signals. + */ + curthread->critical_count++; + } + if (curthread->lock_switch != 0) { /* * Unlock the scheduling queue and leave the * critical region. */ /* Don't trust this after a switch! */ curkse = _get_curkse(); curthread->lock_switch = 0; KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); _kse_critical_leave(&curthread->tmbx); } /* * This thread is being resumed; check for cancellations. */ - if ((resume_once == 0) && (!THR_IN_CRITICAL(curthread))) { - resume_once = 1; - thr_resume_check(curthread, &curthread->tmbx.tm_context, &psf); + if ((psf.psf_valid || curthread->check_pending)) { + resume_once = 0; + THR_GETCONTEXT(&uc); + if (resume_once == 0) { + resume_once = 1; + curthread->check_pending = 0; + thr_resume_check(curthread, &uc, &psf); + } } - THR_ACTIVATE_LAST_LOCK(curthread); } /* * This is the scheduler for a KSE which runs a scope system thread. * The multi-thread KSE scheduler should also work for a single threaded * KSE, but we use a separate scheduler so that it can be fine-tuned * to be more efficient (and perhaps not need a separate stack for * the KSE, allowing it to use the thread's stack). * * XXX - This probably needs some work. */ #ifdef NOT_YET static void kse_sched_single(struct kse *curkse) { struct pthread *curthread = curkse->k_curthread; struct pthread *td_wait; struct timespec ts; int level; if (curthread->active == 0) { if (curthread->state != PS_RUNNING) { /* Check to see if the thread has timed out. */ KSE_GET_TOD(curkse, &ts); if (thr_timedout(curthread, &ts) != 0) { curthread->timeout = 1; curthread->state = PS_RUNNING; } } } /* This thread no longer needs to yield the CPU: */ curthread->critical_yield = 0; curthread->need_switchout = 0; /* * Lock the scheduling queue. * * There is no scheduling queue for single threaded KSEs, * but we need a lock for protection regardless. */ KSE_SCHED_LOCK(curkse, curkse->k_kseg); /* * This has to do the job of kse_switchout_thread(), only * for a single threaded KSE/KSEG. */ switch (curthread->state) { case PS_DEAD: /* Unlock the scheduling queue and exit the KSE and thread. */ thr_cleaup(curkse, curthread); KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); break; case PS_COND_WAIT: case PS_SLEEP_WAIT: /* Only insert threads that can timeout: */ if (curthread->wakeup_time.tv_sec != -1) { /* Insert into the waiting queue: */ KSE_WAITQ_INSERT(curkse, curthread); } break; case PS_LOCKWAIT: level = curthread->locklevel - 1; if (!_LCK_GRANTED(&curthread->lockusers[level])) KSE_WAITQ_INSERT(curkse, curthread); else THR_SET_STATE(curthread, PS_RUNNING); break; case PS_JOIN: case PS_MUTEX_WAIT: case PS_RUNNING: case PS_SIGSUSPEND: case PS_SIGWAIT: case PS_SUSPENDED: case PS_DEADLOCK: default: /* * These states don't timeout and don't need * to be in the waiting queue. */ break; } while (curthread->state != PS_RUNNING) { curthread->active = 0; td_wait = KSE_WAITQ_FIRST(curkse); kse_wait(curkse, td_wait); if (td_wait != NULL) { KSE_GET_TOD(curkse, &ts); if (thr_timedout(curthread, &ts)) { /* Indicate the thread timedout: */ td_wait->timeout = 1; /* Make the thread runnable. */ THR_SET_STATE(td_wait, PS_RUNNING); KSE_WAITQ_REMOVE(curkse, td_wait); } } - KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); - kse_check_signals(curkse); - KSE_SCHED_LOCK(curkse, curkse->k_kseg); } /* Remove the frame reference. */ curthread->curframe = NULL; /* Unlock the scheduling queue. */ KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); /* * Continue the thread at its current frame: */ DBG_MSG("Continuing bound thread %p\n", curthread); _thread_switch(&curthread->tmbx, &curkse->k_mbx.km_curthread); PANIC("Thread has returned from _thread_switch"); } #endif #ifdef DEBUG_THREAD_KERN static void dump_queues(struct kse *curkse) { struct pthread *thread; DBG_MSG("Threads in waiting queue:\n"); TAILQ_FOREACH(thread, &curkse->k_kseg->kg_schedq.sq_waitq, pqe) { DBG_MSG(" thread %p, state %d, blocked %d\n", thread, thread->state, thread->blocked); } } #endif /* * This is the scheduler for a KSE which runs multiple threads. */ static void kse_sched_multi(struct kse *curkse) { struct pthread *curthread, *td_wait; struct pthread_sigframe *curframe; int ret; THR_ASSERT(curkse->k_mbx.km_curthread == NULL, "Mailbox not null in kse_sched_multi"); /* Check for first time initialization: */ if ((curkse->k_flags & KF_INITIALIZED) == 0) { /* Setup this KSEs specific data. */ _ksd_setprivate(&curkse->k_ksd); _set_curkse(curkse); /* Set this before grabbing the context. */ curkse->k_flags |= KF_INITIALIZED; } /* This may have returned from a kse_release(). */ if (KSE_WAITING(curkse)) { DBG_MSG("Entered upcall when KSE is waiting."); KSE_CLEAR_WAIT(curkse); } - /* Lock the scheduling lock. */ - curthread = curkse->k_curthread; - if ((curthread == NULL) || (curthread->need_switchout == 0)) { - /* This is an upcall; take the scheduler lock. */ + /*If this is an upcall; take the scheduler lock. */ + if (curkse->k_switch == 0) KSE_SCHED_LOCK(curkse, curkse->k_kseg); - } + curkse->k_switch = 0; + curthread = curkse->k_curthread; + if (KSE_IS_IDLE(curkse)) { KSE_CLEAR_IDLE(curkse); curkse->k_kseg->kg_idle_kses--; } /* * If the current thread was completed in another KSE, then * it will be in the run queue. Don't mark it as being blocked. */ if ((curthread != NULL) && ((curthread->flags & THR_FLAGS_IN_RUNQ) == 0) && (curthread->need_switchout == 0)) { /* * Assume the current thread is blocked; when the * completed threads are checked and if the current * thread is among the completed, the blocked flag * will be cleared. */ curthread->blocked = 1; } /* Check for any unblocked threads in the kernel. */ kse_check_completed(curkse); /* * Check for threads that have timed-out. */ kse_check_waitq(curkse); /* * Switchout the current thread, if necessary, as the last step * so that it is inserted into the run queue (if it's runnable) * _after_ any other threads that were added to it above. */ if (curthread == NULL) ; /* Nothing to do here. */ else if ((curthread->need_switchout == 0) && (curthread->blocked == 0) && (THR_IN_CRITICAL(curthread))) { /* * Resume the thread and tell it to yield when * it leaves the critical region. */ curthread->critical_yield = 1; curthread->active = 1; if ((curthread->flags & THR_FLAGS_IN_RUNQ) != 0) KSE_RUNQ_REMOVE(curkse, curthread); curkse->k_curthread = curthread; curthread->kse = curkse; DBG_MSG("Continuing thread %p in critical region\n", curthread); kse_wakeup_multi(curkse); KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); ret = _thread_switch(&curthread->tmbx, &curkse->k_mbx.km_curthread); if (ret != 0) PANIC("Can't resume thread in critical region\n"); } else if ((curthread->flags & THR_FLAGS_IN_RUNQ) == 0) kse_switchout_thread(curkse, curthread); curkse->k_curthread = NULL; kse_wakeup_multi(curkse); - /* This has to be done without the scheduling lock held. */ - KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); - kse_check_signals(curkse); - KSE_SCHED_LOCK(curkse, curkse->k_kseg); - #ifdef DEBUG_THREAD_KERN dump_queues(curkse); #endif /* Check if there are no threads ready to run: */ while (((curthread = KSE_RUNQ_FIRST(curkse)) == NULL) && (curkse->k_kseg->kg_threadcount != 0)) { /* * Wait for a thread to become active or until there are * no more threads. */ td_wait = KSE_WAITQ_FIRST(curkse); kse_wait(curkse, td_wait); kse_check_completed(curkse); kse_check_waitq(curkse); - KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); - kse_check_signals(curkse); - KSE_SCHED_LOCK(curkse, curkse->k_kseg); } /* Check for no more threads: */ if (curkse->k_kseg->kg_threadcount == 0) { /* * Normally this shouldn't return, but it will if there * are other KSEs running that create new threads that * are assigned to this KSE[G]. For instance, if a scope * system thread were to create a scope process thread * and this kse[g] is the initial kse[g], then that newly * created thread would be assigned to us (the initial * kse[g]). */ KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); kse_fini(curkse); /* never returns */ } THR_ASSERT(curthread != NULL, "Return from kse_wait/fini without thread."); THR_ASSERT(curthread->state != PS_DEAD, "Trying to resume dead thread!"); KSE_RUNQ_REMOVE(curkse, curthread); /* * Make the selected thread the current thread. */ curkse->k_curthread = curthread; /* * Make sure the current thread's kse points to this kse. */ curthread->kse = curkse; /* * Reset accounting. */ curthread->tmbx.tm_uticks = 0; curthread->tmbx.tm_sticks = 0; /* * Reset the time slice if this thread is running for the first * time or running again after using its full time slice allocation. */ if (curthread->slice_usec == -1) curthread->slice_usec = 0; /* Mark the thread active. */ curthread->active = 1; /* Remove the frame reference. */ curframe = curthread->curframe; curthread->curframe = NULL; kse_wakeup_multi(curkse); /* * The thread's current signal frame will only be NULL if it * is being resumed after being blocked in the kernel. In * this case, and if the thread needs to run down pending * signals or needs a cancellation check, we need to add a * signal frame to the thread's context. */ #ifdef NOT_YET - if ((curframe == NULL) && ((curthread->have_signals != 0) || + if ((((curframe == NULL) && (curthread->check_pending != 0)) || (((curthread->cancelflags & THR_AT_CANCEL_POINT) == 0) && - ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0)))) + ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0))) && + !THR_IN_CRITICAL(curthread)) signalcontext(&curthread->tmbx.tm_context, 0, (__sighandler_t *)thr_resume_wrapper); #else - if ((curframe == NULL) && (curthread->have_signals != 0)) + if ((curframe == NULL) && (curthread->check_pending != 0) && + !THR_IN_CRITICAL(curthread)) { + curthread->check_pending = 0; signalcontext(&curthread->tmbx.tm_context, 0, (__sighandler_t *)thr_resume_wrapper); + } #endif /* * Continue the thread at its current frame: */ if (curthread->lock_switch != 0) { /* * This thread came from a scheduler switch; it will * unlock the scheduler lock and set the mailbox. */ ret = _thread_switch(&curthread->tmbx, NULL); } else { /* This thread won't unlock the scheduler lock. */ KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); ret = _thread_switch(&curthread->tmbx, &curkse->k_mbx.km_curthread); } if (ret != 0) PANIC("Thread has returned from _thread_switch"); /* This point should not be reached. */ PANIC("Thread has returned from _thread_switch"); } static void -kse_check_signals(struct kse *curkse) +thr_resume_wrapper(int sig, siginfo_t *siginfo, ucontext_t *ucp) { - sigset_t sigset; - int i; - - /* Deliver posted signals. */ - for (i = 0; i < _SIG_WORDS; i++) { - atomic_swap_int(&curkse->k_mbx.km_sigscaught.__bits[i], - 0, &sigset.__bits[i]); - } - if (SIGNOTEMPTY(sigset)) { - /* - * Dispatch each signal. - * - * XXX - There is no siginfo for any of these. - * I think there should be, especially for - * signals from other processes (si_pid, si_uid). - */ - for (i = 1; i < NSIG; i++) { - if (sigismember(&sigset, i) != 0) { - DBG_MSG("Dispatching signal %d\n", i); - _thr_sig_dispatch(curkse, i, - NULL /* no siginfo */); - } - } - sigemptyset(&sigset); - __sys_sigprocmask(SIG_SETMASK, &sigset, NULL); - } -} - -static void -thr_resume_wrapper(int unused_1, siginfo_t *unused_2, ucontext_t *ucp) -{ struct pthread *curthread = _get_curthread(); + struct kse *curkse; + int ret; + DBG_MSG(">>> sig wrapper\n"); + if (curthread->lock_switch) + PANIC("thr_resume_wrapper, lock_switch != 0\n"); thr_resume_check(curthread, ucp, NULL); + _kse_critical_enter(); + curkse = _get_curkse(); + curthread->tmbx.tm_context = *ucp; + ret = _thread_switch(&curthread->tmbx, &curkse->k_mbx.km_curthread); + if (ret != 0) + PANIC("thr_resume_wrapper: thread has returned " + "from _thread_switch"); + /* THR_SETCONTEXT(ucp); */ /* not work, why ? */ } static void thr_resume_check(struct pthread *curthread, ucontext_t *ucp, struct pthread_sigframe *psf) { - /* Check signals before cancellations. */ - while (curthread->have_signals != 0) { - /* Clear the pending flag. */ - curthread->have_signals = 0; + _thr_sig_rundown(curthread, ucp, psf); - /* - * It's perfectly valid, though not portable, for - * signal handlers to munge their interrupted context - * and expect to return to it. Ensure we use the - * correct context when running down signals. - */ - _thr_sig_rundown(curthread, ucp, psf); - } - #ifdef NOT_YET if (((curthread->cancelflags & THR_AT_CANCEL_POINT) == 0) && ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0)) pthread_testcancel(); #endif } /* * Clean up a thread. This must be called with the thread's KSE * scheduling lock held. The thread must be a thread from the * KSE's group. */ static void thr_cleanup(struct kse *curkse, struct pthread *thread) { struct pthread *joiner; int sys_scope; if ((joiner = thread->joiner) != NULL) { /* Joinee scheduler lock held; joiner won't leave. */ if (joiner->kseg == curkse->k_kseg) { if (joiner->join_status.thread == thread) { joiner->join_status.thread = NULL; joiner->join_status.ret = thread->ret; _thr_setrunnable_unlocked(joiner); } } else { KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); /* The joiner may have removed itself and exited. */ if (_thr_ref_add(thread, joiner, 0) == 0) { KSE_SCHED_LOCK(curkse, joiner->kseg); if (joiner->join_status.thread == thread) { joiner->join_status.thread = NULL; joiner->join_status.ret = thread->ret; _thr_setrunnable_unlocked(joiner); } KSE_SCHED_UNLOCK(curkse, joiner->kseg); _thr_ref_delete(thread, joiner); } KSE_SCHED_LOCK(curkse, curkse->k_kseg); } thread->attr.flags |= PTHREAD_DETACHED; } if (!(sys_scope = (thread->attr.flags & PTHREAD_SCOPE_SYSTEM))) { /* * Remove the thread from the KSEG's list of threads. */ KSEG_THRQ_REMOVE(thread->kseg, thread); /* * Migrate the thread to the main KSE so that this * KSE and KSEG can be cleaned when their last thread * exits. */ thread->kseg = _kse_initial->k_kseg; thread->kse = _kse_initial; } thread->flags |= THR_FLAGS_GC_SAFE; /* * We can't hold the thread list lock while holding the * scheduler lock. */ KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); DBG_MSG("Adding thread %p to GC list\n", thread); KSE_LOCK_ACQUIRE(curkse, &_thread_list_lock); THR_GCLIST_ADD(thread); /* Use thread_list_lock */ active_threads--; - if (active_threads == 0) { + if (active_threads == 1) { KSE_LOCK_RELEASE(curkse, &_thread_list_lock); exit(0); } KSE_LOCK_RELEASE(curkse, &_thread_list_lock); if (sys_scope) { /* * System scope thread is single thread group, * when thread is exited, its kse and ksegrp should * be recycled as well. */ kse_exit(); PANIC("kse_exit() failed for system scope thread"); } KSE_SCHED_LOCK(curkse, curkse->k_kseg); } void _thr_gc(struct pthread *curthread) { struct pthread *td, *td_next; kse_critical_t crit; TAILQ_HEAD(, pthread) worklist; TAILQ_INIT(&worklist); crit = _kse_critical_enter(); KSE_LOCK_ACQUIRE(curthread->kse, &_thread_list_lock); /* Check the threads waiting for GC. */ for (td = TAILQ_FIRST(&_thread_gc_list); td != NULL; td = td_next) { td_next = TAILQ_NEXT(td, gcle); if ((td->flags & THR_FLAGS_GC_SAFE) == 0) continue; else if (((td->attr.flags & PTHREAD_SCOPE_SYSTEM) != 0) && ((td->kse->k_mbx.km_flags & KMF_DONE) == 0)) { /* * The thread and KSE are operating on the same * stack. Wait for the KSE to exit before freeing * the thread's stack as well as everything else. */ continue; } /* * Remove the thread from the GC list. If the thread * isn't yet detached, it will get added back to the * GC list at a later time. */ THR_GCLIST_REMOVE(td); DBG_MSG("Freeing thread %p stack\n", td); /* * We can free the thread stack since it's no longer * in use. */ _thr_stack_free(&td->attr); if (((td->attr.flags & PTHREAD_DETACHED) != 0) && (td->refcount == 0)) { /* * The thread has detached and is no longer * referenced. It is safe to remove all * remnants of the thread. */ THR_LIST_REMOVE(td); TAILQ_INSERT_HEAD(&worklist, td, gcle); } } KSE_LOCK_RELEASE(curthread->kse, &_thread_list_lock); _kse_critical_leave(crit); while ((td = TAILQ_FIRST(&worklist)) != NULL) { TAILQ_REMOVE(&worklist, td, gcle); if ((td->attr.flags & PTHREAD_SCOPE_SYSTEM) != 0) { crit = _kse_critical_enter(); KSE_LOCK_ACQUIRE(curthread->kse, &kse_lock); kse_free_unlocked(td->kse); kseg_free_unlocked(td->kseg); KSE_LOCK_RELEASE(curthread->kse, &kse_lock); _kse_critical_leave(crit); } - DBG_MSG("Freeing thread %p\n", td); - _thr_free(curthread, td); + /* + * XXX we don't free initial thread, because there might + * have some code referencing initial thread. + */ + if (td != _thr_initial) { + DBG_MSG("Freeing thread %p\n", td); + _thr_free(curthread, td); + } else + DBG_MSG("Initial thread won't be freed\n"); } /* XXX free kse and ksegrp list should be looked as well */ } /* * Only new threads that are running or suspended may be scheduled. */ int _thr_schedule_add(struct pthread *curthread, struct pthread *newthread) { - struct kse *curkse; kse_critical_t crit; int ret; /* Add the new thread. */ thr_link(newthread); /* * If this is the first time creating a thread, make sure * the mailbox is set for the current thread. */ if ((newthread->attr.flags & PTHREAD_SCOPE_SYSTEM) != 0) { #ifdef NOT_YET /* We use the thread's stack as the KSE's stack. */ new_thread->kse->k_mbx.km_stack.ss_sp = new_thread->attr.stackaddr_attr; new_thread->kse->k_mbx.km_stack.ss_size = new_thread->attr.stacksize_attr; #endif /* * No need to lock the scheduling queue since the * KSE/KSEG pair have not yet been started. */ KSEG_THRQ_ADD(newthread->kseg, newthread); if (newthread->state == PS_RUNNING) THR_RUNQ_INSERT_TAIL(newthread); newthread->kse->k_curthread = NULL; newthread->kse->k_mbx.km_flags = 0; newthread->kse->k_mbx.km_func = (kse_func_t *)kse_sched_multi; newthread->kse->k_mbx.km_quantum = 0; /* * This thread needs a new KSE and KSEG. */ - crit = _kse_critical_enter(); - curkse = _get_curkse(); - _ksd_setprivate(&newthread->kse->k_ksd); - newthread->kse->k_flags |= KF_INITIALIZED|KF_STARTED; + newthread->kse->k_flags &= ~KF_INITIALIZED; + newthread->kse->k_flags |= KF_STARTED; ret = kse_create(&newthread->kse->k_mbx, 1); if (ret != 0) ret = errno; - _ksd_setprivate(&curkse->k_ksd); - _kse_critical_leave(crit); } else { /* * Lock the KSE and add the new thread to its list of * assigned threads. If the new thread is runnable, also * add it to the KSE's run queue. */ + crit = _kse_critical_enter(); KSE_SCHED_LOCK(curthread->kse, newthread->kseg); KSEG_THRQ_ADD(newthread->kseg, newthread); if (newthread->state == PS_RUNNING) THR_RUNQ_INSERT_TAIL(newthread); if ((newthread->kse->k_flags & KF_STARTED) == 0) { /* * This KSE hasn't been started yet. Start it * outside of holding the lock. */ newthread->kse->k_flags |= KF_STARTED; newthread->kse->k_mbx.km_func = (kse_func_t *)kse_sched_multi; newthread->kse->k_mbx.km_flags = 0; kse_create(&newthread->kse->k_mbx, 0); } else if ((newthread->state == PS_RUNNING) && KSE_IS_IDLE(newthread->kse)) { /* * The thread is being scheduled on another KSEG. */ kse_wakeup_one(newthread); } KSE_SCHED_UNLOCK(curthread->kse, newthread->kseg); + _kse_critical_leave(crit); ret = 0; } if (ret != 0) thr_unlink(newthread); return (ret); } void kse_waitq_insert(struct pthread *thread) { struct pthread *td; if (thread->wakeup_time.tv_sec == -1) TAILQ_INSERT_TAIL(&thread->kse->k_schedq->sq_waitq, thread, pqe); else { td = TAILQ_FIRST(&thread->kse->k_schedq->sq_waitq); while ((td != NULL) && (td->wakeup_time.tv_sec != -1) && ((td->wakeup_time.tv_sec < thread->wakeup_time.tv_sec) || ((td->wakeup_time.tv_sec == thread->wakeup_time.tv_sec) && (td->wakeup_time.tv_nsec <= thread->wakeup_time.tv_nsec)))) td = TAILQ_NEXT(td, pqe); if (td == NULL) TAILQ_INSERT_TAIL(&thread->kse->k_schedq->sq_waitq, thread, pqe); else TAILQ_INSERT_BEFORE(td, thread, pqe); } thread->flags |= THR_FLAGS_IN_WAITQ; } /* * This must be called with the scheduling lock held. */ static void kse_check_completed(struct kse *kse) { struct pthread *thread; struct kse_thr_mailbox *completed; + int sig; if ((completed = kse->k_mbx.km_completed) != NULL) { kse->k_mbx.km_completed = NULL; while (completed != NULL) { thread = completed->tm_udata; DBG_MSG("Found completed thread %p, name %s\n", thread, (thread->name == NULL) ? "none" : thread->name); thread->blocked = 0; if (thread != kse->k_curthread) { if ((thread->flags & THR_FLAGS_SUSPENDED) != 0) THR_SET_STATE(thread, PS_SUSPENDED); else KSE_RUNQ_INSERT_TAIL(kse, thread); if ((thread->kse != kse) && (thread->kse->k_curthread == thread)) { thread->kse->k_curthread = NULL; thread->active = 0; } } + if ((sig = thread->tmbx.tm_syncsig.si_signo) != 0) { + if (SIGISMEMBER(thread->sigmask, sig)) + SIGADDSET(thread->sigpend, sig); + else + _thr_sig_add(thread, sig, &thread->tmbx.tm_syncsig); + thread->tmbx.tm_syncsig.si_signo = 0; + } completed = completed->tm_next; } } } /* * This must be called with the scheduling lock held. */ static void kse_check_waitq(struct kse *kse) { struct pthread *pthread; struct timespec ts; KSE_GET_TOD(kse, &ts); /* * Wake up threads that have timedout. This has to be * done before adding the current thread to the run queue * so that a CPU intensive thread doesn't get preference * over waiting threads. */ while (((pthread = KSE_WAITQ_FIRST(kse)) != NULL) && thr_timedout(pthread, &ts)) { /* Remove the thread from the wait queue: */ KSE_WAITQ_REMOVE(kse, pthread); DBG_MSG("Found timedout thread %p in waitq\n", pthread); /* Indicate the thread timedout: */ pthread->timeout = 1; /* Add the thread to the priority queue: */ if ((pthread->flags & THR_FLAGS_SUSPENDED) != 0) THR_SET_STATE(pthread, PS_SUSPENDED); else { THR_SET_STATE(pthread, PS_RUNNING); KSE_RUNQ_INSERT_TAIL(kse, pthread); } } } static int thr_timedout(struct pthread *thread, struct timespec *curtime) { if (thread->wakeup_time.tv_sec < 0) return (0); else if (thread->wakeup_time.tv_sec > curtime->tv_sec) return (0); else if ((thread->wakeup_time.tv_sec == curtime->tv_sec) && (thread->wakeup_time.tv_nsec > curtime->tv_nsec)) return (0); else return (1); } /* * This must be called with the scheduling lock held. * * Each thread has a time slice, a wakeup time (used when it wants * to wait for a specified amount of time), a run state, and an * active flag. * * When a thread gets run by the scheduler, the active flag is * set to non-zero (1). When a thread performs an explicit yield * or schedules a state change, it enters the scheduler and the * active flag is cleared. When the active flag is still seen * set in the scheduler, that means that the thread is blocked in * the kernel (because it is cleared before entering the scheduler * in all other instances). * * The wakeup time is only set for those states that can timeout. * It is set to (-1, -1) for all other instances. * * The thread's run state, aside from being useful when debugging, * is used to place the thread in an appropriate queue. There * are 2 basic queues: * * o run queue - queue ordered by priority for all threads * that are runnable * o waiting queue - queue sorted by wakeup time for all threads * that are not otherwise runnable (not blocked * in kernel, not waiting for locks) * * The thread's time slice is used for round-robin scheduling * (the default scheduling policy). While a SCHED_RR thread * is runnable it's time slice accumulates. When it reaches * the time slice interval, it gets reset and added to the end * of the queue of threads at its priority. When a thread no * longer becomes runnable (blocks in kernel, waits, etc), its * time slice is reset. * * The job of kse_switchout_thread() is to handle all of the above. */ static void kse_switchout_thread(struct kse *kse, struct pthread *thread) { int level; int i; + int restart; + siginfo_t siginfo; /* * Place the currently running thread into the * appropriate queue(s). */ DBG_MSG("Switching out thread %p, state %d\n", thread, thread->state); THR_DEACTIVATE_LAST_LOCK(thread); if (thread->blocked != 0) { thread->active = 0; thread->need_switchout = 0; /* This thread must have blocked in the kernel. */ /* thread->slice_usec = -1;*/ /* restart timeslice */ - /* - * XXX - Check for pending signals for this thread to - * see if we need to interrupt it in the kernel. - */ - /* if (thread->check_pending != 0) */ if ((thread->slice_usec != -1) && (thread->attr.sched_policy != SCHED_FIFO)) thread->slice_usec += (thread->tmbx.tm_uticks + thread->tmbx.tm_sticks) * _clock_res_usec; + /* + * Check for pending signals for this thread to + * see if we need to interrupt it in the kernel. + */ + if (thread->check_pending != 0) { + for (i = 1; i <= _SIG_MAXSIG; ++i) { + if (SIGISMEMBER(thread->sigpend, i) && + !SIGISMEMBER(thread->sigmask, i)) { + restart = _thread_sigact[1 - 1].sa_flags & SA_RESTART; + kse_thr_interrupt(&thread->tmbx, + restart ? -2 : -1); + break; + } + } + } } else { switch (thread->state) { case PS_DEAD: /* * The scheduler is operating on a different * stack. It is safe to do garbage collecting * here. */ thread->active = 0; thread->need_switchout = 0; thr_cleanup(kse, thread); return; break; case PS_RUNNING: if ((thread->flags & THR_FLAGS_SUSPENDED) != 0) THR_SET_STATE(thread, PS_SUSPENDED); break; case PS_COND_WAIT: case PS_SLEEP_WAIT: /* Insert into the waiting queue: */ KSE_WAITQ_INSERT(kse, thread); break; case PS_LOCKWAIT: /* * This state doesn't timeout. */ thread->wakeup_time.tv_sec = -1; thread->wakeup_time.tv_nsec = -1; level = thread->locklevel - 1; if (!_LCK_GRANTED(&thread->lockusers[level])) KSE_WAITQ_INSERT(kse, thread); else THR_SET_STATE(thread, PS_RUNNING); break; + case PS_SIGWAIT: + KSE_WAITQ_INSERT(kse, thread); + break; case PS_JOIN: case PS_MUTEX_WAIT: case PS_SIGSUSPEND: - case PS_SIGWAIT: case PS_SUSPENDED: case PS_DEADLOCK: default: /* * These states don't timeout. */ thread->wakeup_time.tv_sec = -1; thread->wakeup_time.tv_nsec = -1; /* Insert into the waiting queue: */ KSE_WAITQ_INSERT(kse, thread); break; } if (thread->state != PS_RUNNING) { /* Restart the time slice: */ thread->slice_usec = -1; } else { if (thread->need_switchout != 0) /* * The thread yielded on its own; * restart the timeslice. */ thread->slice_usec = -1; else if ((thread->slice_usec != -1) && (thread->attr.sched_policy != SCHED_FIFO)) { thread->slice_usec += (thread->tmbx.tm_uticks + thread->tmbx.tm_sticks) * _clock_res_usec; /* Check for time quantum exceeded: */ if (thread->slice_usec > TIMESLICE_USEC) thread->slice_usec = -1; } if (thread->slice_usec == -1) { /* * The thread exceeded its time quantum or * it yielded the CPU; place it at the tail * of the queue for its priority. */ KSE_RUNQ_INSERT_TAIL(kse, thread); } else { /* * The thread hasn't exceeded its interval * Place it at the head of the queue for its * priority. */ KSE_RUNQ_INSERT_HEAD(kse, thread); } } } thread->active = 0; thread->need_switchout = 0; if (thread->check_pending != 0) { /* Install pending signals into the frame. */ thread->check_pending = 0; - for (i = 0; i < _SIG_MAXSIG; i++) { - if (sigismember(&thread->sigpend, i) && - !sigismember(&thread->tmbx.tm_context.uc_sigmask, i)) + KSE_LOCK_ACQUIRE(kse, &_thread_signal_lock); + for (i = 1; i <= _SIG_MAXSIG; i++) { + if (SIGISMEMBER(thread->sigmask, i)) + continue; + if (SIGISMEMBER(thread->sigpend, i)) _thr_sig_add(thread, i, &thread->siginfo[i]); + else if (SIGISMEMBER(_thr_proc_sigpending, i) && + _thr_getprocsig_unlocked(i, &siginfo)) { + _thr_sig_add(thread, i, &siginfo); + } } + KSE_LOCK_RELEASE(kse, &_thread_signal_lock); } } /* * This function waits for the smallest timeout value of any waiting * thread, or until it receives a message from another KSE. * * This must be called with the scheduling lock held. */ static void kse_wait(struct kse *kse, struct pthread *td_wait) { struct timespec ts, ts_sleep; int saved_flags; KSE_GET_TOD(kse, &ts); if ((td_wait == NULL) || (td_wait->wakeup_time.tv_sec < 0)) { /* Limit sleep to no more than 1 minute. */ ts_sleep.tv_sec = 60; ts_sleep.tv_nsec = 0; } else { TIMESPEC_SUB(&ts_sleep, &td_wait->wakeup_time, &ts); if (ts_sleep.tv_sec > 60) { ts_sleep.tv_sec = 60; ts_sleep.tv_nsec = 0; } } /* Don't sleep for negative times. */ if ((ts_sleep.tv_sec >= 0) && (ts_sleep.tv_nsec >= 0)) { KSE_SET_IDLE(kse); kse->k_kseg->kg_idle_kses++; KSE_SCHED_UNLOCK(kse, kse->k_kseg); saved_flags = kse->k_mbx.km_flags; kse->k_mbx.km_flags |= KMF_NOUPCALL; kse_release(&ts_sleep); kse->k_mbx.km_flags = saved_flags; KSE_SCHED_LOCK(kse, kse->k_kseg); if (KSE_IS_IDLE(kse)) { KSE_CLEAR_IDLE(kse); kse->k_kseg->kg_idle_kses--; } } } /* * Avoid calling this kse_exit() so as not to confuse it with the * system call of the same name. */ static void kse_fini(struct kse *kse) { /* struct kse_group *free_kseg = NULL; */ struct timespec ts; /* * Check to see if this is one of the main kses. */ if (kse->k_kseg != _kse_initial->k_kseg) { PANIC("shouldn't get here"); /* This is for supporting thread groups. */ #ifdef NOT_YET /* Remove this KSE from the KSEG's list of KSEs. */ KSE_SCHED_LOCK(kse, kse->k_kseg); TAILQ_REMOVE(&kse->k_kseg->kg_kseq, kse, k_kgqe); kse->k_kseg->kg_ksecount--; if (TAILQ_EMPTY(&kse->k_kseg->kg_kseq)) free_kseg = kse->k_kseg; KSE_SCHED_UNLOCK(kse, kse->k_kseg); /* * Add this KSE to the list of free KSEs along with * the KSEG if is now orphaned. */ KSE_LOCK_ACQUIRE(kse, &kse_lock); if (free_kseg != NULL) kseg_free_unlocked(free_kseg); kse_free_unlocked(kse); KSE_LOCK_RELEASE(kse, &kse_lock); kse_exit(); /* Never returns. */ PANIC("kse_exit()"); #endif } else { #ifdef NOT_YET /* * In future, we might allow program to kill * kse in initial group. */ if (kse != _kse_initial) { KSE_SCHED_LOCK(kse, kse->k_kseg); TAILQ_REMOVE(&kse->k_kseg->kg_kseq, kse, k_kgqe); kse->k_kseg->kg_ksecount--; KSE_SCHED_UNLOCK(kse, kse->k_kseg); KSE_LOCK_ACQUIRE(kse, &kse_lock); kse_free_unlocked(kse); KSE_LOCK_RELEASE(kse, &kse_lock); kse_exit(); /* Never returns. */ PANIC("kse_exit() failed for initial kseg"); } #endif KSE_SCHED_LOCK(kse, kse->k_kseg); KSE_SET_IDLE(kse); kse->k_kseg->kg_idle_kses++; KSE_SCHED_UNLOCK(kse, kse->k_kseg); ts.tv_sec = 120; ts.tv_nsec = 0; kse->k_mbx.km_flags = 0; kse_release(&ts); /* Never reach */ } } void _thr_set_timeout(const struct timespec *timeout) { struct pthread *curthread = _get_curthread(); struct timespec ts; /* Reset the timeout flag for the running thread: */ curthread->timeout = 0; /* Check if the thread is to wait forever: */ if (timeout == NULL) { /* * Set the wakeup time to something that can be recognised as * different to an actual time of day: */ curthread->wakeup_time.tv_sec = -1; curthread->wakeup_time.tv_nsec = -1; } /* Check if no waiting is required: */ else if ((timeout->tv_sec == 0) && (timeout->tv_nsec == 0)) { /* Set the wake up time to 'immediately': */ curthread->wakeup_time.tv_sec = 0; curthread->wakeup_time.tv_nsec = 0; } else { /* Calculate the time for the current thread to wakeup: */ KSE_GET_TOD(curthread->kse, &ts); TIMESPEC_ADD(&curthread->wakeup_time, &ts, timeout); } } void _thr_panic_exit(char *file, int line, char *msg) { char buf[256]; snprintf(buf, sizeof(buf), "(%s:%d) %s\n", file, line, msg); __sys_write(2, buf, strlen(buf)); abort(); } void _thr_setrunnable(struct pthread *curthread, struct pthread *thread) { kse_critical_t crit; crit = _kse_critical_enter(); KSE_SCHED_LOCK(curthread->kse, thread->kseg); _thr_setrunnable_unlocked(thread); KSE_SCHED_UNLOCK(curthread->kse, thread->kseg); _kse_critical_leave(crit); } void _thr_setrunnable_unlocked(struct pthread *thread) { if ((thread->kseg->kg_flags & KGF_SINGLE_THREAD) != 0) { /* No silly queues for these threads. */ if ((thread->flags & THR_FLAGS_SUSPENDED) != 0) THR_SET_STATE(thread, PS_SUSPENDED); else THR_SET_STATE(thread, PS_RUNNING); } else if (thread->state != PS_RUNNING) { if ((thread->flags & THR_FLAGS_IN_WAITQ) != 0) KSE_WAITQ_REMOVE(thread->kse, thread); if ((thread->flags & THR_FLAGS_SUSPENDED) != 0) THR_SET_STATE(thread, PS_SUSPENDED); else { THR_SET_STATE(thread, PS_RUNNING); if ((thread->blocked == 0) && (thread->active == 0) && (thread->flags & THR_FLAGS_IN_RUNQ) == 0) THR_RUNQ_INSERT_TAIL(thread); } } /* * XXX - Threads are not yet assigned to specific KSEs; they are * assigned to the KSEG. So the fact that a thread's KSE is * waiting doesn't necessarily mean that it will be the KSE * that runs the thread after the lock is granted. But we * don't know if the other KSEs within the same KSEG are * also in a waiting state or not so we err on the side of * caution and wakeup the thread's last known KSE. We * ensure that the threads KSE doesn't change while it's * scheduling lock is held so it is safe to reference it * (the KSE). If the KSE wakes up and doesn't find any more * work it will again go back to waiting so no harm is done. */ kse_wakeup_one(thread); } static void kse_wakeup_one(struct pthread *thread) { struct kse *ke; if (KSE_IS_IDLE(thread->kse)) { KSE_CLEAR_IDLE(thread->kse); thread->kseg->kg_idle_kses--; KSE_WAKEUP(thread->kse); } else { TAILQ_FOREACH(ke, &thread->kseg->kg_kseq, k_kgqe) { if (KSE_IS_IDLE(ke)) { KSE_CLEAR_IDLE(ke); ke->k_kseg->kg_idle_kses--; KSE_WAKEUP(ke); return; } } } } static void kse_wakeup_multi(struct kse *curkse) { struct kse *ke; int tmp; if ((tmp = KSE_RUNQ_THREADS(curkse)) && curkse->k_kseg->kg_idle_kses) { TAILQ_FOREACH(ke, &curkse->k_kseg->kg_kseq, k_kgqe) { if (KSE_IS_IDLE(ke)) { KSE_CLEAR_IDLE(ke); ke->k_kseg->kg_idle_kses--; KSE_WAKEUP(ke); if (--tmp == 0) break; } } } } struct pthread * _get_curthread(void) { return (_ksd_curthread()); } /* This assumes the caller has disabled upcalls. */ struct kse * _get_curkse(void) { return (_ksd_curkse()); } void _set_curkse(struct kse *kse) { _ksd_setprivate(&kse->k_ksd); } /* * Allocate a new KSEG. * * We allow the current thread to be NULL in the case that this * is the first time a KSEG is being created (library initialization). * In this case, we don't need to (and can't) take any locks. */ struct kse_group * _kseg_alloc(struct pthread *curthread) { struct kse_group *kseg = NULL; kse_critical_t crit; if ((curthread != NULL) && (free_kseg_count > 0)) { /* Use the kse lock for the kseg queue. */ crit = _kse_critical_enter(); KSE_LOCK_ACQUIRE(curthread->kse, &kse_lock); if ((kseg = TAILQ_FIRST(&free_kse_groupq)) != NULL) { TAILQ_REMOVE(&free_kse_groupq, kseg, kg_qe); free_kseg_count--; active_kseg_count++; TAILQ_INSERT_TAIL(&active_kse_groupq, kseg, kg_qe); } KSE_LOCK_RELEASE(curthread->kse, &kse_lock); _kse_critical_leave(crit); if (kseg) kseg_reinit(kseg); } /* * If requested, attempt to allocate a new KSE group only if the * KSE allocation was successful and a KSE group wasn't found in * the free list. */ if ((kseg == NULL) && ((kseg = (struct kse_group *)malloc(sizeof(*kseg))) != NULL)) { if (_pq_alloc(&kseg->kg_schedq.sq_runq, THR_MIN_PRIORITY, THR_LAST_PRIORITY) != 0) { free(kseg); kseg = NULL; } else { kseg_init(kseg); /* Add the KSEG to the list of active KSEGs. */ if (curthread != NULL) { crit = _kse_critical_enter(); KSE_LOCK_ACQUIRE(curthread->kse, &kse_lock); active_kseg_count++; TAILQ_INSERT_TAIL(&active_kse_groupq, kseg, kg_qe); KSE_LOCK_RELEASE(curthread->kse, &kse_lock); _kse_critical_leave(crit); } else { active_kseg_count++; TAILQ_INSERT_TAIL(&active_kse_groupq, kseg, kg_qe); } } } return (kseg); } /* * This must be called with the kse lock held and when there are * no more threads that reference it. */ static void kseg_free_unlocked(struct kse_group *kseg) { TAILQ_REMOVE(&active_kse_groupq, kseg, kg_qe); TAILQ_INSERT_HEAD(&free_kse_groupq, kseg, kg_qe); free_kseg_count++; active_kseg_count--; } void _kseg_free(struct kse_group *kseg) { struct kse *curkse; kse_critical_t crit; crit = _kse_critical_enter(); curkse = _get_curkse(); KSE_LOCK_ACQUIRE(curkse, &kse_lock); kseg_free_unlocked(kseg); KSE_LOCK_RELEASE(curkse, &kse_lock); _kse_critical_leave(crit); } /* * Allocate a new KSE. * * We allow the current thread to be NULL in the case that this * is the first time a KSE is being created (library initialization). * In this case, we don't need to (and can't) take any locks. */ struct kse * _kse_alloc(struct pthread *curthread) { struct kse *kse = NULL; kse_critical_t crit; int need_ksd = 0; int i; if ((curthread != NULL) && (free_kse_count > 0)) { crit = _kse_critical_enter(); KSE_LOCK_ACQUIRE(curthread->kse, &kse_lock); /* Search for a finished KSE. */ kse = TAILQ_FIRST(&free_kseq); while ((kse != NULL) && ((kse->k_mbx.km_flags & KMF_DONE) == 0)) { kse = TAILQ_NEXT(kse, k_qe); } if (kse != NULL) { DBG_MSG("found an unused kse.\n"); TAILQ_REMOVE(&free_kseq, kse, k_qe); free_kse_count--; TAILQ_INSERT_TAIL(&active_kseq, kse, k_qe); active_kse_count++; } KSE_LOCK_RELEASE(curthread->kse, &kse_lock); _kse_critical_leave(crit); if (kse != NULL) kse_reinit(kse); } if ((kse == NULL) && ((kse = (struct kse *)malloc(sizeof(*kse))) != NULL)) { bzero(kse, sizeof(*kse)); /* Initialize the lockusers. */ for (i = 0; i < MAX_KSE_LOCKLEVEL; i++) { _lockuser_init(&kse->k_lockusers[i], (void *)kse); _LCK_SET_PRIVATE2(&kse->k_lockusers[i], NULL); } /* _lock_init(kse->k_lock, ...) */ /* We had to malloc a kse; mark it as needing a new ID.*/ need_ksd = 1; /* * Create the KSE context. * * XXX - For now this is done here in the allocation. * In the future, we may want to have it done * outside the allocation so that scope system * threads (one thread per KSE) are not required * to have a stack for an unneeded kse upcall. */ kse->k_mbx.km_func = (kse_func_t *)kse_sched_multi; kse->k_mbx.km_stack.ss_sp = (char *)malloc(KSE_STACKSIZE); kse->k_mbx.km_stack.ss_size = KSE_STACKSIZE; kse->k_mbx.km_udata = (void *)kse; kse->k_mbx.km_quantum = 20000; /* * We need to keep a copy of the stack in case it * doesn't get used; a KSE running a scope system * thread will use that thread's stack. */ kse->k_stack.ss_sp = kse->k_mbx.km_stack.ss_sp; kse->k_stack.ss_size = kse->k_mbx.km_stack.ss_size; if (kse->k_mbx.km_stack.ss_sp == NULL) { for (i = 0; i < MAX_KSE_LOCKLEVEL; i++) { _lockuser_destroy(&kse->k_lockusers[i]); } /* _lock_destroy(&kse->k_lock); */ free(kse); kse = NULL; } } if ((kse != NULL) && (need_ksd != 0)) { /* This KSE needs initialization. */ if (curthread != NULL) { crit = _kse_critical_enter(); KSE_LOCK_ACQUIRE(curthread->kse, &kse_lock); } /* Initialize KSD inside of the lock. */ if (_ksd_create(&kse->k_ksd, (void *)kse, sizeof(*kse)) != 0) { if (curthread != NULL) { KSE_LOCK_RELEASE(curthread->kse, &kse_lock); _kse_critical_leave(crit); } free(kse->k_mbx.km_stack.ss_sp); for (i = 0; i < MAX_KSE_LOCKLEVEL; i++) { _lockuser_destroy(&kse->k_lockusers[i]); } free(kse); + if (curthread != NULL) { + KSE_LOCK_RELEASE(curthread->kse, &kse_lock); + _kse_critical_leave(crit); + } return (NULL); } kse->k_flags = 0; TAILQ_INSERT_TAIL(&active_kseq, kse, k_qe); active_kse_count++; if (curthread != NULL) { KSE_LOCK_RELEASE(curthread->kse, &kse_lock); _kse_critical_leave(crit); } } return (kse); } static void kse_reinit(struct kse *kse) { /* * XXX - For now every kse has its stack. * In the future, we may want to have it done * outside the allocation so that scope system * threads (one thread per KSE) are not required * to have a stack for an unneeded kse upcall. */ kse->k_mbx.km_flags = 0; kse->k_curthread = 0; kse->k_kseg = 0; kse->k_schedq = 0; kse->k_locklevel = 0; - sigemptyset(&kse->k_sigmask); + SIGEMPTYSET(kse->k_sigmask); bzero(&kse->k_sigq, sizeof(kse->k_sigq)); kse->k_check_sigq = 0; kse->k_flags = 0; kse->k_waiting = 0; kse->k_idle = 0; kse->k_error = 0; kse->k_cpu = 0; kse->k_done = 0; + kse->k_switch = 0; } void kse_free_unlocked(struct kse *kse) { TAILQ_REMOVE(&active_kseq, kse, k_qe); active_kse_count--; kse->k_kseg = NULL; kse->k_mbx.km_quantum = 20000; kse->k_flags = 0; TAILQ_INSERT_HEAD(&free_kseq, kse, k_qe); free_kse_count++; } void _kse_free(struct pthread *curthread, struct kse *kse) { kse_critical_t crit; if (curthread == NULL) kse_free_unlocked(kse); else { crit = _kse_critical_enter(); KSE_LOCK_ACQUIRE(curthread->kse, &kse_lock); kse_free_unlocked(kse); KSE_LOCK_RELEASE(curthread->kse, &kse_lock); _kse_critical_leave(crit); } } static void kseg_init(struct kse_group *kseg) { kseg_reinit(kseg); _lock_init(&kseg->kg_lock, LCK_ADAPTIVE, _kse_lock_wait, _kse_lock_wakeup); } static void kseg_reinit(struct kse_group *kseg) { TAILQ_INIT(&kseg->kg_kseq); TAILQ_INIT(&kseg->kg_threadq); TAILQ_INIT(&kseg->kg_schedq.sq_waitq); kseg->kg_threadcount = 0; kseg->kg_ksecount = 0; kseg->kg_idle_kses = 0; kseg->kg_flags = 0; } struct pthread * _thr_alloc(struct pthread *curthread) { kse_critical_t crit; void *p; struct pthread *thread = NULL; if (curthread != NULL) { if (GC_NEEDED()) _thr_gc(curthread); if (free_thread_count > 0) { crit = _kse_critical_enter(); KSE_LOCK_ACQUIRE(curthread->kse, &thread_lock); if ((thread = TAILQ_FIRST(&free_threadq)) != NULL) { TAILQ_REMOVE(&free_threadq, thread, tle); free_thread_count--; } KSE_LOCK_RELEASE(curthread->kse, &thread_lock); _kse_critical_leave(crit); } } if (thread == NULL) { p = malloc(sizeof(struct pthread) + THR_ALIGNBYTES); if (p != NULL) { thread = (struct pthread *)THR_ALIGN(p); thread->alloc_addr = p; } } return (thread); } void _thr_free(struct pthread *curthread, struct pthread *thread) { kse_critical_t crit; int i; DBG_MSG("Freeing thread %p\n", thread); if ((curthread == NULL) || (free_thread_count >= MAX_CACHED_THREADS)) { for (i = 0; i < MAX_THR_LOCKLEVEL; i++) { _lockuser_destroy(&thread->lockusers[i]); } _lock_destroy(&thread->lock); free(thread->alloc_addr); } else { crit = _kse_critical_enter(); KSE_LOCK_ACQUIRE(curthread->kse, &thread_lock); TAILQ_INSERT_TAIL(&free_threadq, thread, tle); free_thread_count++; KSE_LOCK_RELEASE(curthread->kse, &thread_lock); _kse_critical_leave(crit); } } /* * Add an active thread: * * o Assign the thread a unique id (which GDB uses to track * threads. * o Add the thread to the list of all threads and increment * number of active threads. */ static void thr_link(struct pthread *thread) { kse_critical_t crit; struct kse *curkse; + struct pthread *curthread; crit = _kse_critical_enter(); curkse = _get_curkse(); - + curthread = _get_curthread(); + thread->sigmask = curthread->sigmask; KSE_LOCK_ACQUIRE(curkse, &_thread_list_lock); /* * Initialize the unique id (which GDB uses to track * threads), add the thread to the list of all threads, * and */ thread->uniqueid = next_uniqueid++; THR_LIST_ADD(thread); active_threads++; KSE_LOCK_RELEASE(curkse, &_thread_list_lock); _kse_critical_leave(crit); } /* * Remove an active thread. */ static void thr_unlink(struct pthread *thread) { kse_critical_t crit; struct kse *curkse; crit = _kse_critical_enter(); curkse = _get_curkse(); KSE_LOCK_ACQUIRE(curkse, &_thread_list_lock); THR_LIST_REMOVE(thread); active_threads--; KSE_LOCK_RELEASE(curkse, &_thread_list_lock); _kse_critical_leave(crit); } Index: head/lib/libkse/thread/thr_kill.c =================================================================== --- head/lib/libkse/thread/thr_kill.c (revision 116976) +++ head/lib/libkse/thread/thr_kill.c (revision 116977) @@ -1,66 +1,66 @@ /* * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by John Birrell. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include "thr_private.h" __weak_reference(_pthread_kill, pthread_kill); int _pthread_kill(pthread_t pthread, int sig) { struct pthread *curthread = _get_curthread(); int ret; /* Check for invalid signal numbers: */ - if (sig < 0 || sig >= NSIG) + if (sig < 0 || sig > _SIG_MAXSIG) /* Invalid signal: */ ret = EINVAL; /* * Ensure the thread is in the list of active threads, and the * signal is valid (signal 0 specifies error checking only) and * not being ignored: */ else if ((ret = _thr_ref_add(curthread, pthread, /*include dead*/0)) == 0) { if ((sig > 0) && (_thread_sigact[sig - 1].sa_handler != SIG_IGN)) _thr_sig_send(pthread, sig); _thr_ref_delete(curthread, pthread); } /* Return the completion status: */ return (ret); } Index: head/lib/libkse/thread/thr_private.h =================================================================== --- head/lib/libkse/thread/thr_private.h (revision 116976) +++ head/lib/libkse/thread/thr_private.h (revision 116977) @@ -1,1231 +1,1236 @@ /* * 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. * * Private thread definitions for the uthread kernel. * * $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 "lock.h" #include "pthread_md.h" /* * Evaluate the storage class specifier. */ #ifdef GLOBAL_PTHREAD_PRIVATE #define SCLASS #define SCLASS_PRESET(x...) = x #else #define SCLASS extern #define SCLASS_PRESET(x...) #endif /* * Kernel fatal error handler macro. */ #define PANIC(string) _thr_exit(__FILE__,__LINE__,string) /* Output debug messages like this: */ #define stdout_debug(args...) _thread_printf(STDOUT_FILENO, ##args) #define stderr_debug(args...) _thread_printf(STDOUT_FILENO, ##args) #define DBG_MUTEX 0x0001 #define DBG_SIG 0x0002 #define THR_ASSERT(cond, msg) do { \ if (!(cond)) \ PANIC(msg); \ } while (0) /* * State change macro without scheduling queue change: */ #define THR_SET_STATE(thrd, newstate) do { \ (thrd)->state = newstate; \ (thrd)->fname = __FILE__; \ (thrd)->lineno = __LINE__; \ } while (0) /* * Define the signals to be used for scheduling. */ #define _ITIMER_SCHED_TIMER ITIMER_PROF #define _SCHED_SIGNAL SIGPROF #define TIMESPEC_ADD(dst, src, val) \ do { \ (dst)->tv_sec = (src)->tv_sec + (val)->tv_sec; \ (dst)->tv_nsec = (src)->tv_nsec + (val)->tv_nsec; \ if ((dst)->tv_nsec > 1000000000) { \ (dst)->tv_sec++; \ (dst)->tv_nsec -= 1000000000; \ } \ } while (0) #define TIMESPEC_SUB(dst, src, val) \ do { \ (dst)->tv_sec = (src)->tv_sec - (val)->tv_sec; \ (dst)->tv_nsec = (src)->tv_nsec - (val)->tv_nsec; \ if ((dst)->tv_nsec < 0) { \ (dst)->tv_sec--; \ (dst)->tv_nsec += 1000000000; \ } \ } while (0) /* * Priority queues. * * XXX It'd be nice if these were contained in uthread_priority_queue.[ch]. */ typedef struct pq_list { TAILQ_HEAD(, pthread) pl_head; /* list of threads at this priority */ TAILQ_ENTRY(pq_list) pl_link; /* link for queue of priority lists */ int pl_prio; /* the priority of this list */ int pl_queued; /* is this in the priority queue */ } pq_list_t; typedef struct pq_queue { TAILQ_HEAD(, pq_list) pq_queue; /* queue of priority lists */ pq_list_t *pq_lists; /* array of all priority lists */ int pq_size; /* number of priority lists */ #define PQF_ACTIVE 0x0001 int pq_flags; int pq_threads; } pq_queue_t; /* * Each KSEG has a scheduling queue. For now, threads that exist in their * own KSEG (system scope) will get a full priority queue. In the future * this can be optimized for the single thread per KSEG case. */ struct sched_queue { pq_queue_t sq_runq; TAILQ_HEAD(, pthread) sq_waitq; /* waiting in userland */ }; /* Used to maintain pending and active signals: */ struct sigstatus { siginfo_t *info; /* arg 2 to signal handler */ int pending; /* Is this a pending signal? */ int blocked; /* * This signal has occured and hasn't * yet been handled; ignore subsequent * signals until the handler is done. */ int signo; }; typedef struct kse_thr_mailbox *kse_critical_t; struct kse_group; -#define MAX_KSE_LOCKLEVEL 3 +#define MAX_KSE_LOCKLEVEL 5 struct kse { struct kse_mailbox k_mbx; /* kernel kse mailbox */ /* -- location and order specific items for gdb -- */ struct pthread *k_curthread; /* current thread */ struct kse_group *k_kseg; /* parent KSEG */ struct sched_queue *k_schedq; /* scheduling queue */ /* -- end of location and order specific items -- */ TAILQ_ENTRY(kse) k_qe; /* KSE list link entry */ TAILQ_ENTRY(kse) k_kgqe; /* KSEG's KSE list entry */ struct ksd k_ksd; /* KSE specific data */ /* * Items that are only modified by the kse, or that otherwise * don't need to be locked when accessed */ struct lock k_lock; struct lockuser k_lockusers[MAX_KSE_LOCKLEVEL]; int k_locklevel; sigset_t k_sigmask; - struct sigstatus k_sigq[NSIG]; + struct sigstatus k_sigq[_SIG_MAXSIG]; stack_t k_stack; int k_check_sigq; int k_flags; #define KF_STARTED 0x0001 /* kernel kse created */ #define KF_INITIALIZED 0x0002 /* initialized on 1st upcall */ int k_waiting; int k_idle; /* kse is idle */ int k_error; /* syscall errno in critical */ int k_cpu; /* CPU ID when bound */ int k_done; /* this KSE is done */ + int k_switch; /* thread switch in UTS */ }; /* * Each KSE group contains one or more KSEs in which threads can run. * At least for now, there is one scheduling queue per KSE group; KSEs * within the same KSE group compete for threads from the same scheduling * queue. A scope system thread has one KSE in one KSE group; the group * does not use its scheduling queue. */ struct kse_group { TAILQ_HEAD(, kse) kg_kseq; /* list of KSEs in group */ TAILQ_HEAD(, pthread) kg_threadq; /* list of threads in group */ TAILQ_ENTRY(kse_group) kg_qe; /* link entry */ struct sched_queue kg_schedq; /* scheduling queue */ struct lock kg_lock; int kg_threadcount; /* # of assigned threads */ int kg_ksecount; /* # of assigned KSEs */ int kg_idle_kses; int kg_flags; #define KGF_SINGLE_THREAD 0x0001 /* scope system kse group */ #define KGF_SCHEDQ_INITED 0x0002 /* has an initialized schedq */ }; /* * Add/remove threads from a KSE's scheduling queue. * For now the scheduling queue is hung off the KSEG. */ #define KSEG_THRQ_ADD(kseg, thr) \ do { \ TAILQ_INSERT_TAIL(&(kseg)->kg_threadq, thr, kle);\ (kseg)->kg_threadcount++; \ } while (0) #define KSEG_THRQ_REMOVE(kseg, thr) \ do { \ TAILQ_REMOVE(&(kseg)->kg_threadq, thr, kle); \ (kseg)->kg_threadcount--; \ } while (0) /* * Lock acquire and release for KSEs. */ #define KSE_LOCK_ACQUIRE(kse, lck) \ do { \ if ((kse)->k_locklevel >= MAX_KSE_LOCKLEVEL) \ PANIC("Exceeded maximum lock level"); \ else { \ (kse)->k_locklevel++; \ _lock_acquire((lck), \ &(kse)->k_lockusers[(kse)->k_locklevel - 1], 0); \ } \ } while (0) #define KSE_LOCK_RELEASE(kse, lck) \ do { \ if ((kse)->k_locklevel > 0) { \ _lock_release((lck), \ &(kse)->k_lockusers[(kse)->k_locklevel - 1]); \ (kse)->k_locklevel--; \ } \ } while (0) /* * Lock our own KSEG. */ #define KSE_LOCK(curkse) \ KSE_LOCK_ACQUIRE(curkse, &(curkse)->k_kseg->kg_lock) #define KSE_UNLOCK(curkse) \ KSE_LOCK_RELEASE(curkse, &(curkse)->k_kseg->kg_lock) /* * Lock a potentially different KSEG. */ #define KSE_SCHED_LOCK(curkse, kseg) \ KSE_LOCK_ACQUIRE(curkse, &(kseg)->kg_lock) #define KSE_SCHED_UNLOCK(curkse, kseg) \ KSE_LOCK_RELEASE(curkse, &(kseg)->kg_lock) /* * Waiting queue manipulation macros (using pqe link): */ #define KSE_WAITQ_REMOVE(kse, thrd) \ do { \ if (((thrd)->flags & THR_FLAGS_IN_WAITQ) != 0) { \ TAILQ_REMOVE(&(kse)->k_schedq->sq_waitq, thrd, pqe); \ (thrd)->flags &= ~THR_FLAGS_IN_WAITQ; \ } \ } while (0) #define KSE_WAITQ_INSERT(kse, thrd) kse_waitq_insert(thrd) #define KSE_WAITQ_FIRST(kse) TAILQ_FIRST(&(kse)->k_schedq->sq_waitq) #define KSE_SET_WAIT(kse) atomic_store_rel_int(&(kse)->k_waiting, 1) #define KSE_CLEAR_WAIT(kse) atomic_store_rel_int(&(kse)->k_waiting, 0) #define KSE_WAITING(kse) (kse)->k_waiting != 0 #define KSE_WAKEUP(kse) kse_wakeup(&(kse)->k_mbx) #define KSE_SET_IDLE(kse) ((kse)->k_idle = 1) #define KSE_CLEAR_IDLE(kse) ((kse)->k_idle = 0) #define KSE_IS_IDLE(kse) ((kse)->k_idle != 0) /* * TailQ initialization values. */ #define TAILQ_INITIALIZER { NULL, NULL } /* * lock initialization values. */ #define LCK_INITIALIZER { NULL, NULL, LCK_DEFAULT } struct pthread_mutex { /* * Lock for accesses to this structure. */ struct lock m_lock; enum pthread_mutextype m_type; int m_protocol; TAILQ_HEAD(mutex_head, pthread) m_queue; struct pthread *m_owner; long m_flags; int m_count; int m_refcount; /* * Used for priority inheritence and protection. * * m_prio - For priority inheritence, the highest active * priority (threads locking the mutex inherit * this priority). For priority protection, the * ceiling priority of this mutex. * m_saved_prio - mutex owners inherited priority before * taking the mutex, restored when the owner * unlocks the mutex. */ int m_prio; int m_saved_prio; /* * Link for list of all mutexes a thread currently owns. */ TAILQ_ENTRY(pthread_mutex) m_qe; }; /* * Flags for mutexes. */ #define MUTEX_FLAGS_PRIVATE 0x01 #define MUTEX_FLAGS_INITED 0x02 #define MUTEX_FLAGS_BUSY 0x04 /* * Static mutex initialization values. */ #define PTHREAD_MUTEX_STATIC_INITIALIZER \ { LCK_INITIALIZER, PTHREAD_MUTEX_DEFAULT, PTHREAD_PRIO_NONE, \ TAILQ_INITIALIZER, NULL, MUTEX_FLAGS_PRIVATE, 0, 0, 0, 0, \ TAILQ_INITIALIZER } struct pthread_mutex_attr { enum pthread_mutextype m_type; int m_protocol; int m_ceiling; long m_flags; }; #define PTHREAD_MUTEXATTR_STATIC_INITIALIZER \ { PTHREAD_MUTEX_DEFAULT, PTHREAD_PRIO_NONE, 0, MUTEX_FLAGS_PRIVATE } /* * Condition variable definitions. */ enum pthread_cond_type { COND_TYPE_FAST, COND_TYPE_MAX }; struct pthread_cond { /* * Lock for accesses to this structure. */ struct lock c_lock; enum pthread_cond_type c_type; TAILQ_HEAD(cond_head, pthread) c_queue; struct pthread_mutex *c_mutex; long c_flags; long c_seqno; }; struct pthread_cond_attr { enum pthread_cond_type c_type; long c_flags; }; /* * Flags for condition variables. */ #define COND_FLAGS_PRIVATE 0x01 #define COND_FLAGS_INITED 0x02 #define COND_FLAGS_BUSY 0x04 /* * Static cond initialization values. */ #define PTHREAD_COND_STATIC_INITIALIZER \ { LCK_INITIALIZER, COND_TYPE_FAST, TAILQ_INITIALIZER, \ NULL, NULL, 0, 0 } /* * Semaphore definitions. */ struct sem { #define SEM_MAGIC ((u_int32_t) 0x09fa4012) u_int32_t magic; pthread_mutex_t lock; pthread_cond_t gtzero; u_int32_t count; u_int32_t nwaiters; }; /* * Cleanup definitions. */ struct pthread_cleanup { struct pthread_cleanup *next; void (*routine) (); void *routine_arg; }; struct pthread_attr { int sched_policy; int sched_inherit; int sched_interval; int prio; int suspend; #define THR_STACK_USER 0x100 /* 0xFF reserved for */ int flags; void *arg_attr; void (*cleanup_attr) (); void *stackaddr_attr; size_t stacksize_attr; size_t guardsize_attr; }; /* * Thread creation state attributes. */ #define THR_CREATE_RUNNING 0 #define THR_CREATE_SUSPENDED 1 /* * Miscellaneous definitions. */ #define THR_STACK_DEFAULT 65536 /* * 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 0x100000 /* * Define the different priority ranges. All applications have thread * priorities constrained within 0-31. The threads library raises the * priority when delivering signals in order to ensure that signal * delivery happens (from the POSIX spec) "as soon as possible". * In the future, the threads library will also be able to map specific * threads into real-time (cooperating) processes or kernel threads. * The RT and SIGNAL priorities will be used internally and added to * thread base priorities so that the scheduling queue can handle both * normal and RT priority threads with and without signal handling. * * The approach taken is that, within each class, signal delivery * always has priority over thread execution. */ #define THR_DEFAULT_PRIORITY 15 #define THR_MIN_PRIORITY 0 #define THR_MAX_PRIORITY 31 /* 0x1F */ #define THR_SIGNAL_PRIORITY 32 /* 0x20 */ #define THR_RT_PRIORITY 64 /* 0x40 */ #define THR_FIRST_PRIORITY THR_MIN_PRIORITY #define THR_LAST_PRIORITY \ (THR_MAX_PRIORITY + THR_SIGNAL_PRIORITY + THR_RT_PRIORITY) #define THR_BASE_PRIORITY(prio) ((prio) & THR_MAX_PRIORITY) /* * Clock resolution in microseconds. */ #define CLOCK_RES_USEC 10000 /* * Time slice period in microseconds. */ #define TIMESLICE_USEC 20000 /* * XXX - Define a thread-safe macro to get the current time of day * which is updated at regular intervals by something. * * For now, we just make the system call to get the time. */ #define KSE_GET_TOD(curkse, tsp) \ do { \ *tsp = (curkse)->k_mbx.km_timeofday; \ if ((tsp)->tv_sec == 0) \ clock_gettime(CLOCK_REALTIME, tsp); \ } while (0) struct pthread_rwlockattr { int pshared; }; struct pthread_rwlock { pthread_mutex_t lock; /* monitor lock */ int state; /* 0 = idle >0 = # of readers -1 = writer */ pthread_cond_t read_signal; pthread_cond_t write_signal; int blocked_writers; }; /* * Thread states. */ enum pthread_state { PS_RUNNING, PS_LOCKWAIT, PS_MUTEX_WAIT, PS_COND_WAIT, PS_SLEEP_WAIT, PS_SIGSUSPEND, PS_SIGWAIT, PS_JOIN, PS_SUSPENDED, PS_DEAD, PS_DEADLOCK, PS_STATE_MAX }; union pthread_wait_data { pthread_mutex_t mutex; pthread_cond_t cond; - const sigset_t *sigwait; /* Waiting on a signal in sigwait */ struct lock *lock; + siginfo_t *sigwaitinfo; /* used to save siginfo for sigwaitinfo() */ }; /* * Define a continuation routine that can be used to perform a * transfer of control: */ typedef void (*thread_continuation_t) (void *); /* * This stores a thread's state prior to running a signal handler. * It is used when a signal is delivered to a thread blocked in * userland. If the signal handler returns normally, the thread's * state is restored from here. */ struct pthread_sigframe { + int psf_valid; int psf_flags; int psf_interrupted; int psf_signo; enum pthread_state psf_state; union pthread_wait_data psf_wait_data; struct timespec psf_wakeup_time; sigset_t psf_sigset; sigset_t psf_sigmask; int psf_seqno; }; struct join_status { struct pthread *thread; void *ret; int error; }; struct pthread_specific_elem { const void *data; int seqno; }; -#define MAX_THR_LOCKLEVEL 3 +#define MAX_THR_LOCKLEVEL 5 /* * Thread structure. */ struct pthread { /* * Thread mailbox is first so it cal be aligned properly. */ struct kse_thr_mailbox tmbx; void *alloc_addr; /* real address (unaligned) */ /* * Magic value to help recognize a valid thread structure * from an invalid one: */ #define THR_MAGIC ((u_int32_t) 0xd09ba115) u_int32_t magic; char *name; u_int64_t uniqueid; /* for gdb */ /* Queue entry for list of all threads: */ TAILQ_ENTRY(pthread) tle; /* link for all threads in process */ TAILQ_ENTRY(pthread) kle; /* link for all threads in KSE/KSEG */ /* Queue entry for GC lists: */ TAILQ_ENTRY(pthread) gcle; /* * Lock for accesses to this thread structure. */ struct lock lock; struct lockuser lockusers[MAX_THR_LOCKLEVEL]; int locklevel; kse_critical_t critical[MAX_KSE_LOCKLEVEL]; struct kse *kse; struct kse_group *kseg; /* * Thread start routine, argument, stack pointer and thread * attributes. */ void *(*start_routine)(void *); void *arg; struct pthread_attr attr; int active; /* thread running */ int blocked; /* thread blocked in kernel */ int need_switchout; int need_wakeup; /* * Used for tracking delivery of signal handlers. */ struct pthread_sigframe *curframe; - siginfo_t siginfo[NSIG]; + siginfo_t siginfo[_SIG_MAXSIG]; /* * Cancelability flags - the lower 2 bits are used by cancel * definitions in pthread.h */ #define THR_AT_CANCEL_POINT 0x0004 #define THR_CANCELLING 0x0008 #define THR_CANCEL_NEEDED 0x0010 int cancelflags; thread_continuation_t continuation; /* * The thread's base and pending signal masks. The active * signal mask is stored in the thread's context (in mailbox). */ + sigset_t oldsigmask; sigset_t sigmask; sigset_t sigpend; - int sigmask_seqno; int check_pending; - int have_signals; int refcount; /* Thread state: */ enum pthread_state state; volatile int lock_switch; /* * Number of microseconds accumulated by this thread when * time slicing is active. */ long slice_usec; /* * Time to wake up thread. This is used for sleeping threads and * for any operation which may time out (such as select). */ struct timespec wakeup_time; /* TRUE if operation has timed out. */ int timeout; /* * Error variable used instead of errno. The function __error() * returns a pointer to this. */ int error; /* * The joiner is the thread that is joining to this thread. The * join status keeps track of a join operation to another thread. */ struct pthread *joiner; struct join_status join_status; /* * The current thread can belong to only one scheduling queue at * a time (ready or waiting queue). It can also belong to: * * o A queue of threads waiting for a mutex * o A queue of threads waiting for a condition variable * * It is possible for a thread to belong to more than one of the * above queues if it is handling a signal. A thread may only * enter a mutex or condition variable queue when it is not * being called from a signal handler. If a thread is a member * of one of these queues when a signal handler is invoked, it * must be removed from the queue before invoking the handler * and then added back to the queue after return from the handler. * * Use pqe for the scheduling queue link (both ready and waiting), * sqe for synchronization (mutex, condition variable, and join) * queue links, and qe for all other links. */ TAILQ_ENTRY(pthread) pqe; /* priority, wait queues link */ TAILQ_ENTRY(pthread) sqe; /* synchronization queue link */ /* Wait data. */ union pthread_wait_data data; /* * Set to TRUE if a blocking operation was * interrupted by a signal: */ int interrupted; /* Signal number when in state PS_SIGWAIT: */ int signo; /* * Set to non-zero when this thread has entered a critical * region. We allow for recursive entries into critical regions. */ int critical_count; /* * Set to TRUE if this thread should yield after leaving a * critical region to check for signals, messages, etc. */ int critical_yield; int sflags; #define THR_FLAGS_IN_SYNCQ 0x0001 /* Miscellaneous flags; only set with scheduling lock held. */ int flags; #define THR_FLAGS_PRIVATE 0x0001 #define THR_FLAGS_IN_WAITQ 0x0002 /* in waiting queue using pqe link */ #define THR_FLAGS_IN_RUNQ 0x0004 /* in run queue using pqe link */ #define THR_FLAGS_EXITING 0x0008 /* thread is exiting */ #define THR_FLAGS_SUSPENDED 0x0010 /* thread is suspended */ #define THR_FLAGS_GC_SAFE 0x0020 /* thread safe for cleaning */ #define THR_FLAGS_IN_TDLIST 0x0040 /* thread in all thread list */ #define THR_FLAGS_IN_GCLIST 0x0080 /* thread in gc list */ /* * Base priority is the user setable and retrievable priority * of the thread. It is only affected by explicit calls to * set thread priority and upon thread creation via a thread * attribute or default priority. */ char base_priority; /* * Inherited priority is the priority a thread inherits by * taking a priority inheritence or protection mutex. It * is not affected by base priority changes. Inherited * priority defaults to and remains 0 until a mutex is taken * that is being waited on by any other thread whose priority * is non-zero. */ char inherited_priority; /* * Active priority is always the maximum of the threads base * priority and inherited priority. When there is a change * in either the base or inherited priority, the active * priority must be recalculated. */ char active_priority; /* Number of priority ceiling or protection mutexes owned. */ int priority_mutex_count; /* * Queue of currently owned mutexes. */ TAILQ_HEAD(, pthread_mutex) mutexq; void *ret; struct pthread_specific_elem *specific; int specific_data_count; /* * Current locks bitmap for rtld. */ int rtld_bits; /* Cleanup handlers Link List */ struct pthread_cleanup *cleanup; char *fname; /* Ptr to source file name */ int lineno; /* Source line number. */ }; /* * Critical regions can also be detected by looking at the threads * current lock level. Ensure these macros increment and decrement * the lock levels such that locks can not be held with a lock level * of 0. */ #define THR_IN_CRITICAL(thrd) \ (((thrd)->locklevel > 0) || \ ((thrd)->critical_count > 0)) #define THR_YIELD_CHECK(thrd) \ do { \ if (((thrd)->critical_yield != 0) && \ !(THR_IN_CRITICAL(thrd))) \ _thr_sched_switch(thrd); \ else if (((thrd)->check_pending != 0) && \ !(THR_IN_CRITICAL(thrd))) \ _thr_sig_check_pending(thrd); \ } while (0) #define THR_LOCK_ACQUIRE(thrd, lck) \ do { \ if ((thrd)->locklevel >= MAX_THR_LOCKLEVEL) \ PANIC("Exceeded maximum lock level"); \ else { \ THR_DEACTIVATE_LAST_LOCK(thrd); \ (thrd)->locklevel++; \ _lock_acquire((lck), \ &(thrd)->lockusers[(thrd)->locklevel - 1], \ (thrd)->active_priority); \ } \ } while (0) #define THR_LOCK_RELEASE(thrd, lck) \ do { \ if ((thrd)->locklevel > 0) { \ _lock_release((lck), \ &(thrd)->lockusers[(thrd)->locklevel - 1]); \ (thrd)->locklevel--; \ THR_ACTIVATE_LAST_LOCK(thrd); \ if ((thrd)->locklevel == 0) \ THR_YIELD_CHECK(thrd); \ } \ } while (0) #define THR_ACTIVATE_LAST_LOCK(thrd) \ do { \ if ((thrd)->locklevel > 0) \ _lockuser_setactive( \ &(thrd)->lockusers[(thrd)->locklevel - 1], 1); \ } while (0) #define THR_DEACTIVATE_LAST_LOCK(thrd) \ do { \ if ((thrd)->locklevel > 0) \ _lockuser_setactive( \ &(thrd)->lockusers[(thrd)->locklevel - 1], 0); \ } while (0) /* * For now, threads will have their own lock separate from their * KSE scheduling lock. */ #define THR_LOCK(thr) THR_LOCK_ACQUIRE(thr, &(thr)->lock) #define THR_UNLOCK(thr) THR_LOCK_RELEASE(thr, &(thr)->lock) #define THR_THREAD_LOCK(curthrd, thr) THR_LOCK_ACQUIRE(curthrd, &(thr)->lock) #define THR_THREAD_UNLOCK(curthrd, thr) THR_LOCK_RELEASE(curthrd, &(thr)->lock) /* * Priority queue manipulation macros (using pqe link). We use * the thread's kseg link instead of the kse link because a thread * does not (currently) have a statically assigned kse. */ #define THR_RUNQ_INSERT_HEAD(thrd) \ _pq_insert_head(&(thrd)->kseg->kg_schedq.sq_runq, thrd) #define THR_RUNQ_INSERT_TAIL(thrd) \ _pq_insert_tail(&(thrd)->kseg->kg_schedq.sq_runq, thrd) #define THR_RUNQ_REMOVE(thrd) \ _pq_remove(&(thrd)->kseg->kg_schedq.sq_runq, thrd) #define THR_RUNQ_FIRST() \ _pq_first(&(thrd)->kseg->kg_schedq.sq_runq) /* * Macros to insert/remove threads to the all thread list and * the gc list. */ #define THR_LIST_ADD(thrd) do { \ if (((thrd)->flags & THR_FLAGS_IN_TDLIST) == 0) { \ TAILQ_INSERT_HEAD(&_thread_list, thrd, tle); \ (thrd)->flags |= THR_FLAGS_IN_TDLIST; \ } \ } while (0) #define THR_LIST_REMOVE(thrd) do { \ if (((thrd)->flags & THR_FLAGS_IN_TDLIST) != 0) { \ TAILQ_REMOVE(&_thread_list, thrd, tle); \ (thrd)->flags &= ~THR_FLAGS_IN_TDLIST; \ } \ } while (0) #define THR_GCLIST_ADD(thrd) do { \ if (((thrd)->flags & THR_FLAGS_IN_GCLIST) == 0) { \ TAILQ_INSERT_HEAD(&_thread_gc_list, thrd, gcle);\ (thrd)->flags |= THR_FLAGS_IN_GCLIST; \ _gc_count++; \ } \ } while (0) #define THR_GCLIST_REMOVE(thrd) do { \ if (((thrd)->flags & THR_FLAGS_IN_GCLIST) != 0) { \ TAILQ_REMOVE(&_thread_gc_list, thrd, gcle); \ (thrd)->flags &= ~THR_FLAGS_IN_GCLIST; \ _gc_count--; \ } \ } while (0) #define GC_NEEDED() (atomic_load_acq_int(&_gc_count) >= 5) /* * Locking the scheduling queue for another thread uses that thread's * KSEG lock. */ #define THR_SCHED_LOCK(curthr, thr) do { \ (curthr)->critical[(curthr)->locklevel] = _kse_critical_enter(); \ (curthr)->locklevel++; \ KSE_SCHED_LOCK((curthr)->kse, (thr)->kseg); \ } while (0) #define THR_SCHED_UNLOCK(curthr, thr) do { \ KSE_SCHED_UNLOCK((curthr)->kse, (thr)->kseg); \ (curthr)->locklevel--; \ _kse_critical_leave((curthr)->critical[(curthr)->locklevel]); \ } while (0) /* Take the scheduling lock with the intent to call the scheduler. */ #define THR_LOCK_SWITCH(curthr) do { \ (void)_kse_critical_enter(); \ KSE_SCHED_LOCK((curthr)->kse, (curthr)->kseg); \ } while (0) #define THR_CRITICAL_ENTER(thr) (thr)->critical_count++ #define THR_CRITICAL_LEAVE(thr) do { \ (thr)->critical_count--; \ if (((thr)->critical_yield != 0) && \ ((thr)->critical_count == 0)) { \ (thr)->critical_yield = 0; \ _thr_sched_switch(thr); \ } \ } while (0) #define THR_IS_ACTIVE(thrd) \ ((thrd)->kse != NULL) && ((thrd)->kse->k_curthread == (thrd)) #define THR_IN_SYNCQ(thrd) (((thrd)->sflags & THR_FLAGS_IN_SYNCQ) != 0) #define THR_IS_SUSPENDED(thrd) \ (((thrd)->state == PS_SUSPENDED) || \ (((thrd)->flags & THR_FLAGS_SUSPENDED) != 0)) #define THR_IS_EXITING(thrd) (((thrd)->flags & THR_FLAGS_EXITING) != 0) /* * Global variables for the pthread kernel. */ SCLASS void *_usrstack SCLASS_PRESET(NULL); SCLASS struct kse *_kse_initial SCLASS_PRESET(NULL); SCLASS struct pthread *_thr_initial SCLASS_PRESET(NULL); /* List of all threads: */ SCLASS TAILQ_HEAD(, pthread) _thread_list SCLASS_PRESET(TAILQ_HEAD_INITIALIZER(_thread_list)); /* List of threads needing GC: */ SCLASS TAILQ_HEAD(, pthread) _thread_gc_list SCLASS_PRESET(TAILQ_HEAD_INITIALIZER(_thread_gc_list)); /* Default thread attributes: */ SCLASS struct pthread_attr _pthread_attr_default SCLASS_PRESET({ SCHED_RR, 0, TIMESLICE_USEC, THR_DEFAULT_PRIORITY, THR_CREATE_RUNNING, PTHREAD_CREATE_JOINABLE, NULL, NULL, NULL, THR_STACK_DEFAULT }); /* Default mutex attributes: */ SCLASS struct pthread_mutex_attr _pthread_mutexattr_default SCLASS_PRESET({PTHREAD_MUTEX_DEFAULT, PTHREAD_PRIO_NONE, 0, 0 }); /* Default condition variable attributes: */ SCLASS struct pthread_cond_attr _pthread_condattr_default SCLASS_PRESET({COND_TYPE_FAST, 0}); /* Clock resolution in usec. */ SCLASS int _clock_res_usec SCLASS_PRESET(CLOCK_RES_USEC); /* Array of signal actions for this process: */ -SCLASS struct sigaction _thread_sigact[NSIG]; +SCLASS struct sigaction _thread_sigact[_SIG_MAXSIG]; /* * Array of counts of dummy handlers for SIG_DFL signals. This is used to * assure that there is always a dummy signal handler installed while there * is a thread sigwait()ing on the corresponding signal. */ -SCLASS int _thread_dfl_count[NSIG]; +SCLASS int _thread_dfl_count[_SIG_MAXSIG]; /* * Lock for above count of dummy handlers and for the process signal * mask and pending signal sets. */ SCLASS struct lock _thread_signal_lock; /* Pending signals and mask for this process: */ SCLASS sigset_t _thr_proc_sigpending; -SCLASS sigset_t _thr_proc_sigmask SCLASS_PRESET({{0, 0, 0, 0}}); -SCLASS siginfo_t _thr_proc_siginfo[NSIG]; +SCLASS siginfo_t _thr_proc_siginfo[_SIG_MAXSIG]; SCLASS pid_t _thr_pid SCLASS_PRESET(0); /* Garbage collector lock. */ SCLASS struct lock _gc_lock; SCLASS int _gc_check SCLASS_PRESET(0); SCLASS int _gc_count SCLASS_PRESET(0); SCLASS struct lock _mutex_static_lock; SCLASS struct lock _rwlock_static_lock; SCLASS struct lock _keytable_lock; SCLASS struct lock _thread_list_lock; SCLASS int _thr_guard_default; SCLASS int _thr_page_size; - +SCLASS pthread_t _thr_sig_daemon; SCLASS int _thr_debug_flags SCLASS_PRESET(0); /* Undefine the storage class and preset specifiers: */ #undef SCLASS #undef SCLASS_PRESET /* * Function prototype definitions. */ __BEGIN_DECLS int _cond_reinit(pthread_cond_t *); void _cond_wait_backout(struct pthread *); struct pthread *_get_curthread(void); struct kse *_get_curkse(void); void _set_curkse(struct kse *); struct kse *_kse_alloc(struct pthread *); kse_critical_t _kse_critical_enter(void); void _kse_critical_leave(kse_critical_t); int _kse_in_critical(void); void _kse_free(struct pthread *, struct kse *); void _kse_init(); struct kse_group *_kseg_alloc(struct pthread *); void _kse_lock_wait(struct lock *, struct lockuser *lu); void _kse_lock_wakeup(struct lock *, struct lockuser *lu); void _kse_sig_check_pending(struct kse *); void _kse_single_thread(struct pthread *); void _kse_start(struct kse *); int _kse_setthreaded(int); int _kse_isthreaded(void); void _kseg_free(struct kse_group *); int _mutex_cv_lock(pthread_mutex_t *); int _mutex_cv_unlock(pthread_mutex_t *); void _mutex_lock_backout(struct pthread *); void _mutex_notify_priochange(struct pthread *, struct pthread *, int); int _mutex_reinit(struct pthread_mutex *); void _mutex_unlock_private(struct pthread *); void _libpthread_init(struct pthread *); int _pq_alloc(struct pq_queue *, int, int); void _pq_free(struct pq_queue *); int _pq_init(struct pq_queue *); void _pq_remove(struct pq_queue *pq, struct pthread *); void _pq_insert_head(struct pq_queue *pq, struct pthread *); void _pq_insert_tail(struct pq_queue *pq, struct pthread *); struct pthread *_pq_first(struct pq_queue *pq); void *_pthread_getspecific(pthread_key_t); int _pthread_key_create(pthread_key_t *, void (*) (void *)); int _pthread_key_delete(pthread_key_t); int _pthread_mutex_destroy(pthread_mutex_t *); int _pthread_mutex_init(pthread_mutex_t *, const pthread_mutexattr_t *); int _pthread_mutex_lock(pthread_mutex_t *); int _pthread_mutex_trylock(pthread_mutex_t *); int _pthread_mutex_unlock(pthread_mutex_t *); int _pthread_mutexattr_init(pthread_mutexattr_t *); int _pthread_mutexattr_destroy(pthread_mutexattr_t *); int _pthread_mutexattr_settype(pthread_mutexattr_t *, int); int _pthread_once(pthread_once_t *, void (*) (void)); int _pthread_rwlock_init(pthread_rwlock_t *, const pthread_rwlockattr_t *); int _pthread_rwlock_destroy (pthread_rwlock_t *); struct pthread *_pthread_self(void); int _pthread_setspecific(pthread_key_t, const void *); struct pthread *_thr_alloc(struct pthread *); int _thread_enter_uts(struct kse_thr_mailbox *, struct kse_mailbox *); int _thread_switch(struct kse_thr_mailbox *, struct kse_thr_mailbox **); void _thr_exit(char *, int, char *); void _thr_exit_cleanup(void); void _thr_lock_wait(struct lock *lock, struct lockuser *lu); void _thr_lock_wakeup(struct lock *lock, struct lockuser *lu); int _thr_ref_add(struct pthread *, struct pthread *, int); void _thr_ref_delete(struct pthread *, struct pthread *); int _thr_schedule_add(struct pthread *, struct pthread *); void _thr_schedule_remove(struct pthread *, struct pthread *); void _thr_setrunnable(struct pthread *curthread, struct pthread *thread); void _thr_setrunnable_unlocked(struct pthread *thread); void _thr_sig_add(struct pthread *, int, siginfo_t *); void _thr_sig_dispatch(struct kse *, int, siginfo_t *); int _thr_stack_alloc(struct pthread_attr *); void _thr_stack_free(struct pthread_attr *); void _thr_exit_cleanup(void); void _thr_free(struct pthread *, struct pthread *); void _thr_gc(struct pthread *); void _thr_panic_exit(char *, int, char *); void _thread_cleanupspecific(void); void _thread_dump_info(void); void _thread_printf(int, const char *, ...); -void _thr_sched_frame(struct pthread_sigframe *); void _thr_sched_switch(struct pthread *); void _thr_sched_switch_unlocked(struct pthread *); void _thr_set_timeout(const struct timespec *); void _thr_seterrno(struct pthread *, int); void _thr_sig_handler(int, siginfo_t *, ucontext_t *); void _thr_sig_check_pending(struct pthread *); void _thr_sig_rundown(struct pthread *, ucontext_t *, struct pthread_sigframe *); void _thr_sig_send(struct pthread *pthread, int sig); -void _thr_sig_wrapper(void); void _thr_sigframe_restore(struct pthread *thread, struct pthread_sigframe *psf); void _thr_spinlock_init(void); void _thr_enter_cancellation_point(struct pthread *); void _thr_leave_cancellation_point(struct pthread *); int _thr_setconcurrency(int new_level); int _thr_setmaxconcurrency(void); +int _thr_start_sig_daemon(void); +int _thr_getprocsig(int sig, siginfo_t *siginfo); +int _thr_getprocsig_unlocked(int sig, siginfo_t *siginfo); +void _thr_signal_init(void); +void _thr_signal_deinit(void); /* * Aliases for _pthread functions. Should be called instead of * originals if PLT replocation is unwanted at runtme. */ int _thr_cond_broadcast(pthread_cond_t *); int _thr_cond_signal(pthread_cond_t *); int _thr_cond_wait(pthread_cond_t *, pthread_mutex_t *); int _thr_mutex_lock(pthread_mutex_t *); int _thr_mutex_unlock(pthread_mutex_t *); int _thr_rwlock_rdlock (pthread_rwlock_t *); int _thr_rwlock_wrlock (pthread_rwlock_t *); int _thr_rwlock_unlock (pthread_rwlock_t *); /* XXX - Stuff that goes away when my sources get more up to date. */ /* #include */ #ifdef SYS_KSE_H int __sys_kse_create(struct kse_mailbox *, int); int __sys_kse_thr_wakeup(struct kse_mailbox *); int __sys_kse_exit(struct kse_mailbox *); int __sys_kse_release(struct kse_mailbox *); #endif /* #include */ #ifdef _SYS_AIO_H_ int __sys_aio_suspend(const struct aiocb * const[], int, const struct timespec *); #endif /* #include */ #ifdef _SYS_FCNTL_H_ int __sys_fcntl(int, int, ...); int __sys_open(const char *, int, ...); #endif /* #include */ #ifdef _SYS_IOCTL_H_ int __sys_ioctl(int, unsigned long, ...); #endif /* #inclde */ #ifdef _SCHED_H_ int __sys_sched_yield(void); #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 *); #endif /* #include */ #ifdef _SYS_SOCKET_H_ int __sys_sendfile(int, int, off_t, size_t, struct sf_hdtr *, off_t *, int); #endif /* #include */ #ifdef _SYS_UIO_H_ ssize_t __sys_readv(int, const struct iovec *, int); ssize_t __sys_writev(int, const struct iovec *, int); #endif /* #include */ #ifdef _TIME_H_ int __sys_nanosleep(const struct timespec *, struct timespec *); #endif /* #include */ #ifdef _UNISTD_H_ int __sys_close(int); int __sys_execve(const char *, char * const *, char * const *); int __sys_fork(void); int __sys_fsync(int); pid_t __sys_getpid(void); int __sys_select(int, fd_set *, fd_set *, fd_set *, struct timeval *); ssize_t __sys_read(int, void *, size_t); ssize_t __sys_write(int, const void *, size_t); void __sys_exit(int); +int __sys_sigwait(const sigset_t *, int *); +int __sys_sigtimedwait(sigset_t *, siginfo_t *, struct timespec *); #endif /* #include */ #ifdef _SYS_POLL_H_ int __sys_poll(struct pollfd *, unsigned, int); #endif /* #include */ #ifdef _SYS_MMAN_H_ int __sys_msync(void *, size_t, int); #endif #endif /* !_THR_PRIVATE_H */ Index: head/lib/libkse/thread/thr_sig.c =================================================================== --- head/lib/libkse/thread/thr_sig.c (revision 116976) +++ head/lib/libkse/thread/thr_sig.c (revision 116977) @@ -1,912 +1,1010 @@ /* * 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. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include "thr_private.h" #include "pthread_md.h" /* Prototypes: */ static void build_siginfo(siginfo_t *info, int signo); -/* static void thr_sig_add(struct pthread *pthread, int sig, siginfo_t *info); */ static void thr_sig_check_state(struct pthread *pthread, int sig); static struct pthread *thr_sig_find(struct kse *curkse, int sig, siginfo_t *info); static void handle_special_signals(struct kse *curkse, int sig); -static void thr_sigframe_add(struct pthread *thread, int sig, - siginfo_t *info); +static void thr_sigframe_add(struct pthread *thread); static void thr_sigframe_restore(struct pthread *thread, struct pthread_sigframe *psf); static void thr_sigframe_save(struct pthread *thread, struct pthread_sigframe *psf); -static void thr_sig_invoke_handler(struct pthread *, int sig, - siginfo_t *info, ucontext_t *ucp); /* #define DEBUG_SIGNAL */ #ifdef DEBUG_SIGNAL #define DBG_MSG stdout_debug #else #define DBG_MSG(x...) #endif /* * Signal setup and delivery. * * 1) Delivering signals to threads in the same KSE. * These signals are sent by upcall events and are set in the * km_sigscaught field of the KSE mailbox. Since these signals * are received while operating on the KSE stack, they can be * delivered either by using signalcontext() to add a stack frame * to the target thread's stack, or by adding them in the thread's * pending set and having the thread run them down after it * 2) Delivering signals to threads in other KSEs/KSEGs. * 3) Delivering signals to threads in critical regions. * 4) Delivering signals to threads after they change their signal masks. * * Methods of delivering signals. * * 1) Add a signal frame to the thread's saved context. * 2) Add the signal to the thread structure, mark the thread as * having signals to handle, and let the thread run them down * after it resumes from the KSE scheduler. * * Problem with 1). You can't do this to a running thread or a * thread in a critical region. * * Problem with 2). You can't do this to a thread that doesn't * yield in some way (explicitly enters the scheduler). A thread * blocked in the kernel or a CPU hungry thread will not see the * signal without entering the scheduler. * * The solution is to use both 1) and 2) to deliver signals: * * o Thread in critical region - use 2). When the thread * leaves the critical region it will check to see if it * has pending signals and run them down. * * o Thread enters scheduler explicitly - use 2). The thread * can check for pending signals after it returns from the * the scheduler. * * o Thread is running and not current thread - use 2). When the * thread hits a condition specified by one of the other bullets, * the signal will be delivered. * * o Thread is running and is current thread (e.g., the thread * has just changed its signal mask and now sees that it has * pending signals) - just run down the pending signals. * * o Thread is swapped out due to quantum expiration - use 1) * * o Thread is blocked in kernel - kse_thr_wakeup() and then * use 1) */ /* * Rules for selecting threads for signals received: * * 1) If the signal is a sychronous signal, it is delivered to * the generating (current thread). If the thread has the * signal masked, it is added to the threads pending signal * set until the thread unmasks it. * * 2) A thread in sigwait() where the signal is in the thread's * waitset. * * 3) A thread in sigsuspend() where the signal is not in the * thread's suspended signal mask. * * 4) Any thread (first found/easiest to deliver) that has the * signal unmasked. */ +static void * +sig_daemon(void *arg /* Unused */) +{ + int i; + kse_critical_t crit; + struct timespec ts; + sigset_t set; + struct kse *curkse; + struct pthread *curthread = _get_curthread(); + + DBG_MSG("signal daemon started"); + + curthread->name = strdup("signal thread"); + crit = _kse_critical_enter(); + curkse = _get_curkse(); + SIGFILLSET(set); + __sys_sigprocmask(SIG_SETMASK, &set, NULL); + __sys_sigpending(&set); + ts.tv_sec = 0; + ts.tv_nsec = 0; + while (1) { + KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock); + _thr_proc_sigpending = set; + KSE_LOCK_RELEASE(curkse, &_thread_signal_lock); + for (i = 1; i <= _SIG_MAXSIG; i++) { + if (SIGISMEMBER(set, i) != 0) + _thr_sig_dispatch(curkse, i, + NULL /* no siginfo */); + } + ts.tv_sec = 30; + ts.tv_nsec = 0; + curkse->k_mbx.km_flags = + KMF_NOUPCALL | KMF_NOCOMPLETED | KMF_WAITSIGEVENT; + kse_release(&ts); + curkse->k_mbx.km_flags = 0; + set = curkse->k_mbx.km_sigscaught; + } + return (0); +} + +/* Utility function to create signal daemon thread */ +int +_thr_start_sig_daemon(void) +{ + pthread_attr_t attr; + sigset_t sigset, oldset; + + SIGFILLSET(sigset); + pthread_sigmask(SIG_SETMASK, &sigset, &oldset); + pthread_attr_init(&attr); + pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); + /* sigmask will be inherited */ + if (pthread_create(&_thr_sig_daemon, &attr, sig_daemon, NULL)) + PANIC("can not create signal daemon thread!\n"); + pthread_attr_destroy(&attr); + pthread_sigmask(SIG_SETMASK, &oldset, NULL); + return (0); +} + /* * This signal handler only delivers asynchronous signals. * This must be called with upcalls disabled and without * holding any locks. */ void _thr_sig_dispatch(struct kse *curkse, int sig, siginfo_t *info) { struct pthread *thread; DBG_MSG(">>> _thr_sig_dispatch(%d)\n", sig); /* Some signals need special handling: */ handle_special_signals(curkse, sig); - DBG_MSG("dispatch sig:%d\n", sig); while ((thread = thr_sig_find(curkse, sig, info)) != NULL) { /* * Setup the target thread to receive the signal: */ DBG_MSG("Got signal %d, selecting thread %p\n", sig, thread); KSE_SCHED_LOCK(curkse, thread->kseg); if ((thread->state == PS_DEAD) || (thread->state == PS_DEADLOCK) || THR_IS_EXITING(thread) || THR_IS_SUSPENDED(thread)) { KSE_SCHED_UNLOCK(curkse, thread->kseg); _thr_ref_delete(NULL, thread); - } else if (sigismember(&thread->tmbx.tm_context.uc_sigmask, - sig)) { + } else if (SIGISMEMBER(thread->sigmask, sig)) { KSE_SCHED_UNLOCK(curkse, thread->kseg); _thr_ref_delete(NULL, thread); - } - else { + } else { _thr_sig_add(thread, sig, info); KSE_SCHED_UNLOCK(curkse, thread->kseg); _thr_ref_delete(NULL, thread); break; } } + DBG_MSG("<<< _thr_sig_dispatch\n"); } void _thr_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp) { __siginfohandler_t *sigfunc; struct kse *curkse; curkse = _get_curkse(); if ((curkse == NULL) || ((curkse->k_flags & KF_STARTED) == 0)) { /* Upcalls are not yet started; just call the handler. */ sigfunc = _thread_sigact[sig - 1].sa_sigaction; - ucp->uc_sigmask = _thr_proc_sigmask; if (((__sighandler_t *)sigfunc != SIG_DFL) && ((__sighandler_t *)sigfunc != SIG_IGN) && (sigfunc != (__siginfohandler_t *)_thr_sig_handler)) { if (((_thread_sigact[sig - 1].sa_flags & SA_SIGINFO) != 0) || (info == NULL)) (*(sigfunc))(sig, info, ucp); else (*(sigfunc))(sig, (siginfo_t*)(intptr_t)info->si_code, ucp); } } else { /* Nothing. */ DBG_MSG("Got signal %d\n", sig); - sigaddset(&curkse->k_mbx.km_sigscaught, sig); - ucp->uc_sigmask = _thr_proc_sigmask; + /* XXX Bound thread will fall into this... */ } } +/* Must be called with signal lock and schedule lock held in order */ static void thr_sig_invoke_handler(struct pthread *curthread, int sig, siginfo_t *info, ucontext_t *ucp) { void (*sigfunc)(int, siginfo_t *, void *); - sigset_t saved_mask; - int saved_seqno; + sigset_t sigmask; + int sa_flags; + struct sigaction act; + struct kse *curkse; - /* Invoke the signal handler without going through the scheduler: + /* + * Invoke the signal handler without going through the scheduler: */ DBG_MSG("Got signal %d, calling handler for current thread %p\n", sig, curthread); + if (!_kse_in_critical()) + PANIC("thr_sig_invoke_handler without in critical\n"); + curkse = _get_curkse(); /* - * Setup the threads signal mask. - * - * The mask is changed in the thread's active signal mask - * (in the context) and not in the base signal mask because - * a thread is allowed to change its signal mask within a - * signal handler. If it does, the signal mask restored - * after the handler should be the same as that set by the - * thread during the handler, not the original mask from - * before calling the handler. The thread could also - * modify the signal mask in the context and expect this - * mask to be used. - */ - THR_SCHED_LOCK(curthread, curthread); - saved_mask = curthread->tmbx.tm_context.uc_sigmask; - saved_seqno = curthread->sigmask_seqno; - SIGSETOR(curthread->tmbx.tm_context.uc_sigmask, - _thread_sigact[sig - 1].sa_mask); - sigaddset(&curthread->tmbx.tm_context.uc_sigmask, sig); - THR_SCHED_UNLOCK(curthread, curthread); - - /* * Check that a custom handler is installed and if * the signal is not blocked: */ sigfunc = _thread_sigact[sig - 1].sa_sigaction; - ucp->uc_sigmask = _thr_proc_sigmask; + sa_flags = _thread_sigact[sig - 1].sa_flags & SA_SIGINFO; + sigmask = curthread->sigmask; + SIGSETOR(curthread->sigmask, _thread_sigact[sig - 1].sa_mask); + if (!(sa_flags & (SA_NODEFER | SA_RESETHAND))) + SIGADDSET(curthread->sigmask, sig); + if ((sig != SIGILL) && (sa_flags & SA_RESETHAND)) { + if (_thread_dfl_count[sig - 1] == 0) { + act.sa_handler = SIG_DFL; + act.sa_flags = SA_RESTART; + SIGEMPTYSET(act.sa_mask); + __sys_sigaction(sig, &act, NULL); + __sys_sigaction(sig, NULL, &_thread_sigact[sig - 1]); + } + } + KSE_LOCK_RELEASE(curkse, &_thread_signal_lock); + KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); + _kse_critical_leave(&curthread->tmbx); + ucp->uc_sigmask = sigmask; + if (((__sighandler_t *)sigfunc != SIG_DFL) && ((__sighandler_t *)sigfunc != SIG_IGN)) { - if (((_thread_sigact[sig - 1].sa_flags & SA_SIGINFO) != 0) || - (info == NULL)) + if ((sa_flags & SA_SIGINFO) != 0 || info == NULL) (*(sigfunc))(sig, info, ucp); else (*(sigfunc))(sig, (siginfo_t*)(intptr_t)info->si_code, ucp); + } else { + /* XXX + * TODO: exit process if signal would kill it. + */ +#ifdef NOTYET + if (sigprop(sig) & SA_KILL) + kse_sigexit(sig); +#endif } - + _kse_critical_enter(); + /* Don't trust after critical leave/enter */ + curkse = _get_curkse(); + KSE_SCHED_LOCK(curkse, curkse->k_kseg); + KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock); /* * Restore the thread's signal mask. */ - if (saved_seqno == curthread->sigmask_seqno) - curthread->tmbx.tm_context.uc_sigmask = saved_mask; - else - curthread->tmbx.tm_context.uc_sigmask = curthread->sigmask; + curthread->sigmask = ucp->uc_sigmask; + + DBG_MSG("Got signal %d, handler returned %p\n", sig, curthread); } +int +_thr_getprocsig(int sig, siginfo_t *siginfo) +{ + kse_critical_t crit; + struct kse *curkse; + int ret; + + DBG_MSG(">>> _thr_getprocsig\n"); + + crit = _kse_critical_enter(); + curkse = _get_curkse(); + KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock); + ret = _thr_getprocsig_unlocked(sig, siginfo); + KSE_LOCK_RELEASE(curkse, &_thread_signal_lock); + _kse_critical_leave(crit); + + DBG_MSG("<<< _thr_getprocsig\n"); + return (ret); +} + +int +_thr_getprocsig_unlocked(int sig, siginfo_t *siginfo) +{ + sigset_t sigset; + struct timespec ts; + + /* try to retrieve signal from kernel */ + SIGEMPTYSET(sigset); + SIGADDSET(sigset, sig); + ts.tv_sec = 0; + ts.tv_nsec = 0; + if (__sys_sigtimedwait(&sigset, siginfo, &ts) > 0) { + SIGDELSET(_thr_proc_sigpending, sig); + return (sig); + } + return (0); +} + /* * Find a thread that can handle the signal. This must be called * with upcalls disabled. */ struct pthread * thr_sig_find(struct kse *curkse, int sig, siginfo_t *info) { - int handler_installed; struct pthread *pthread; struct pthread *suspended_thread, *signaled_thread; + siginfo_t si; DBG_MSG("Looking for thread to handle signal %d\n", sig); - handler_installed = (_thread_sigact[sig - 1].sa_handler != SIG_IGN) && - (_thread_sigact[sig - 1].sa_handler != SIG_DFL); - /* Check if the signal requires a dump of thread information: */ if (sig == SIGINFO) { /* Dump thread information to file: */ _thread_dump_info(); } /* * Enter a loop to look for threads that have the signal * unmasked. POSIX specifies that a thread in a sigwait * will get the signal over any other threads. Second * preference will be threads in in a sigsuspend. Third * preference will be the current thread. If none of the * above, then the signal is delivered to the first thread * that is found. Note that if a custom handler is not * installed, the signal only affects threads in sigwait. */ suspended_thread = NULL; signaled_thread = NULL; KSE_LOCK_ACQUIRE(curkse, &_thread_list_lock); TAILQ_FOREACH(pthread, &_thread_list, tle) { + if (pthread == _thr_sig_daemon) + continue; +#ifdef NOTYET + /* Signal delivering to bound thread is done by kernel */ + if (pthread->attr.flags & PTHREAD_SCOPE_SYSTEM) + continue; +#endif + /* Take the scheduling lock. */ KSE_SCHED_LOCK(curkse, pthread->kseg); - if ((pthread->state == PS_SIGWAIT) && - sigismember(pthread->data.sigwait, sig)) { + if ((pthread->state == PS_DEAD) || + (pthread->state == PS_DEADLOCK) || + THR_IS_EXITING(pthread) || + THR_IS_SUSPENDED(pthread) || + SIGISMEMBER(pthread->sigmask, sig)) { + ; /* Skip this thread. */ + } else if (pthread->state == PS_SIGWAIT) { /* - * Return the signal number and make the - * thread runnable. + * retrieve signal from kernel, if it is job control + * signal, and sigaction is SIG_DFL, then we will + * be stopped in kernel, we hold lock here, but that + * does not matter, because that's job control, and + * whole process should be stopped. */ - pthread->signo = sig; - _thr_setrunnable_unlocked(pthread); - + if (_thr_getprocsig(sig, &si)) { + DBG_MSG("Waking thread %p in sigwait" + " with signal %d\n", pthread, sig); + /* where to put siginfo ? */ + *(pthread->data.sigwaitinfo) = si; + pthread->sigmask = pthread->oldsigmask; + _thr_setrunnable_unlocked(pthread); + } KSE_SCHED_UNLOCK(curkse, pthread->kseg); - /* * POSIX doesn't doesn't specify which thread * will get the signal if there are multiple * waiters, so we give it to the first thread * we find. * * Do not attempt to deliver this signal * to other threads and do not add the signal * to the process pending set. */ KSE_LOCK_RELEASE(curkse, &_thread_list_lock); - DBG_MSG("Waking thread %p in sigwait with signal %d\n", - pthread, sig); return (NULL); - } - else if ((pthread->state == PS_DEAD) || - (pthread->state == PS_DEADLOCK) || - THR_IS_EXITING(pthread) || THR_IS_SUSPENDED(pthread)) - ; /* Skip this thread. */ - else if ((handler_installed != 0) && - !sigismember(&pthread->tmbx.tm_context.uc_sigmask, sig)) { + } else { if (pthread->state == PS_SIGSUSPEND) { if (suspended_thread == NULL) { suspended_thread = pthread; suspended_thread->refcount++; } } else if (signaled_thread == NULL) { signaled_thread = pthread; signaled_thread->refcount++; - } + } } KSE_SCHED_UNLOCK(curkse, pthread->kseg); } KSE_LOCK_RELEASE(curkse, &_thread_list_lock); - /* - * Only perform wakeups and signal delivery if there is a - * custom handler installed: - */ - if (handler_installed == 0) { - /* - * There is no handler installed; nothing to do here. - */ - } else if (suspended_thread == NULL && - signaled_thread == NULL) { - /* - * Add it to the set of signals pending - * on the process: - */ - KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock); - if (!sigismember(&_thr_proc_sigpending, sig)) { - sigaddset(&_thr_proc_sigpending, sig); - if (info == NULL) - build_siginfo(&_thr_proc_siginfo[sig], sig); - else - memcpy(&_thr_proc_siginfo[sig], info, - sizeof(*info)); - } - KSE_LOCK_RELEASE(curkse, &_thread_signal_lock); - } else { - /* - * We only deliver the signal to one thread; - * give preference to the suspended thread: - */ - if (suspended_thread != NULL) { - pthread = suspended_thread; + if (suspended_thread != NULL) { + pthread = suspended_thread; + if (signaled_thread) _thr_ref_delete(NULL, signaled_thread); - } else - pthread = signaled_thread; - return (pthread); + } else if (signaled_thread) { + pthread = signaled_thread; + } else { + pthread = NULL; } - return (NULL); + return (pthread); } static void build_siginfo(siginfo_t *info, int signo) { bzero(info, sizeof(*info)); info->si_signo = signo; info->si_pid = _thr_pid; } /* * This is called by a thread when it has pending signals to deliver. * It should only be called from the context of the thread. */ void _thr_sig_rundown(struct pthread *curthread, ucontext_t *ucp, struct pthread_sigframe *psf) { - struct pthread_sigframe psf_save; - sigset_t sigset; + siginfo_t siginfo; int i; + kse_critical_t crit; + struct kse *curkse; - THR_SCHED_LOCK(curthread, curthread); - memcpy(&sigset, &curthread->sigpend, sizeof(sigset)); - sigemptyset(&curthread->sigpend); - if (psf != NULL) { - memcpy(&psf_save, psf, sizeof(*psf)); - SIGSETOR(sigset, psf_save.psf_sigset); - sigemptyset(&psf->psf_sigset); - } - THR_SCHED_UNLOCK(curthread, curthread); - + DBG_MSG(">>> thr_sig_rundown %p\n", curthread); /* Check the threads previous state: */ - if ((psf != NULL) && (psf_save.psf_state != PS_RUNNING)) { + if ((psf != NULL) && (psf->psf_valid != 0)) { /* * Do a little cleanup handling for those threads in * queues before calling the signal handler. Signals * for these threads are temporarily blocked until * after cleanup handling. */ - switch (psf_save.psf_state) { + switch (psf->psf_state) { case PS_COND_WAIT: _cond_wait_backout(curthread); - psf_save.psf_state = PS_RUNNING; + psf->psf_state = PS_RUNNING; break; case PS_MUTEX_WAIT: _mutex_lock_backout(curthread); - psf_save.psf_state = PS_RUNNING; + psf->psf_state = PS_RUNNING; break; + case PS_RUNNING: + break; + default: + psf->psf_state = PS_RUNNING; break; } + /* XXX see comment in thr_sched_switch_unlocked */ + curthread->critical_count--; } + /* * Lower the priority before calling the handler in case * it never returns (longjmps back): */ + crit = _kse_critical_enter(); + curkse = _get_curkse(); + KSE_SCHED_LOCK(curkse, curkse->k_kseg); + KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock); curthread->active_priority &= ~THR_SIGNAL_PRIORITY; - for (i = 1; i < NSIG; i++) { - if (sigismember(&sigset, i) != 0) { - /* Call the handler: */ - thr_sig_invoke_handler(curthread, i, - &curthread->siginfo[i], ucp); + while (1) { + for (i = 1; i <= _SIG_MAXSIG; i++) { + if (SIGISMEMBER(curthread->sigmask, i)) + continue; + if (SIGISMEMBER(curthread->sigpend, i)) { + SIGDELSET(curthread->sigpend, i); + siginfo = curthread->siginfo[i]; + break; + } + if (SIGISMEMBER(_thr_proc_sigpending, i)) { + if (_thr_getprocsig_unlocked(i, &siginfo)) + break; + } } + if (i <= _SIG_MAXSIG) + thr_sig_invoke_handler(curthread, i, &siginfo, ucp); + else + break; } - THR_SCHED_LOCK(curthread, curthread); - if (psf != NULL) - thr_sigframe_restore(curthread, &psf_save); - /* Restore the signal mask. */ - curthread->tmbx.tm_context.uc_sigmask = curthread->sigmask; - THR_SCHED_UNLOCK(curthread, curthread); - _thr_sig_check_pending(curthread); + if (psf != NULL && psf->psf_valid != 0) + thr_sigframe_restore(curthread, psf); + curkse = _get_curkse(); + KSE_LOCK_RELEASE(curkse, &_thread_signal_lock); + KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); + _kse_critical_leave(&curthread->tmbx); + + DBG_MSG("<<< thr_sig_rundown %p\n", curthread); } /* * This checks pending signals for the current thread. It should be * called whenever a thread changes its signal mask. Note that this * is called from a thread (using its stack). * * XXX - We might want to just check to see if there are pending * signals for the thread here, but enter the UTS scheduler * to actually install the signal handler(s). */ void _thr_sig_check_pending(struct pthread *curthread) { - sigset_t sigset; - sigset_t pending_process; - sigset_t pending_thread; - kse_critical_t crit; - int i; + ucontext_t uc; + volatile int once; - curthread->check_pending = 0; + if (THR_IN_CRITICAL(curthread)) + return; - /* - * Check if there are pending signals for the running - * thread or process that aren't blocked: - */ - crit = _kse_critical_enter(); - KSE_LOCK_ACQUIRE(curthread->kse, &_thread_signal_lock); - sigset = _thr_proc_sigpending; - KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock); - _kse_critical_leave(crit); - - THR_SCHED_LOCK(curthread, curthread); - SIGSETOR(sigset, curthread->sigpend); - SIGSETNAND(sigset, curthread->tmbx.tm_context.uc_sigmask); - if (SIGNOTEMPTY(sigset)) { - ucontext_t uc; - volatile int once; - + once = 0; + THR_GETCONTEXT(&uc); + if (once == 0) { + once = 1; curthread->check_pending = 0; - THR_SCHED_UNLOCK(curthread, curthread); - - /* - * Split the pending signals into those that were - * pending on the process and those that were pending - * on the thread. - */ - sigfillset(&pending_process); - sigfillset(&pending_thread); - for (i = 1; i < NSIG; i++) { - if (sigismember(&sigset, i) != 0) { - if (sigismember(&curthread->sigpend, i) != 0) { - build_siginfo(&curthread->siginfo[i], i); - sigdelset(&pending_thread, i); - } else { - memcpy(&curthread->siginfo[i], - &_thr_proc_siginfo[i], - sizeof(siginfo_t)); - sigdelset(&pending_process, i); - } - } - } - /* - * Remove any process pending signals that were scheduled - * to be delivered from process' pending set. - */ - crit = _kse_critical_enter(); - KSE_LOCK_ACQUIRE(curthread->kse, &_thread_signal_lock); - SIGSETAND(_thr_proc_sigpending, pending_process); - KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock); - _kse_critical_leave(crit); - - /* - * Remove any thread pending signals that were scheduled - * to be delivered from thread's pending set. - */ - THR_SCHED_LOCK(curthread, curthread); - SIGSETAND(curthread->sigpend, pending_thread); - THR_SCHED_UNLOCK(curthread, curthread); - - once = 0; - THR_GETCONTEXT(&uc); - if (once == 0) { - once = 1; - for (i = 1; i < NSIG; i++) { - if (sigismember(&sigset, i) != 0) { - /* Call the handler: */ - thr_sig_invoke_handler(curthread, i, - &curthread->siginfo[i], &uc); - } - } - } + _thr_sig_rundown(curthread, &uc, NULL); } - else - THR_SCHED_UNLOCK(curthread, curthread); } /* * This must be called with upcalls disabled. */ static void handle_special_signals(struct kse *curkse, int sig) { switch (sig) { /* * POSIX says that pending SIGCONT signals are * discarded when one of these signals occurs. */ case SIGTSTP: case SIGTTIN: case SIGTTOU: KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock); - sigdelset(&_thr_proc_sigpending, SIGCONT); + SIGDELSET(_thr_proc_sigpending, SIGCONT); KSE_LOCK_RELEASE(curkse, &_thread_signal_lock); break; - + case SIGCONT: + KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock); + SIGDELSET(_thr_proc_sigpending, SIGTSTP); + SIGDELSET(_thr_proc_sigpending, SIGTTIN); + SIGDELSET(_thr_proc_sigpending, SIGTTOU); + KSE_LOCK_RELEASE(curkse, &_thread_signal_lock); default: break; } } /* * Perform thread specific actions in response to a signal. * This function is only called if there is a handler installed * for the signal, and if the target thread has the signal * unmasked. * * This must be called with the thread's scheduling lock held. */ void _thr_sig_add(struct pthread *pthread, int sig, siginfo_t *info) { int restart; int suppress_handler = 0; + int fromproc = 0; + struct pthread *curthread = _get_curthread(); + struct kse *curkse; + siginfo_t siginfo; - if (pthread->curframe == NULL) { - /* - * This thread is active. Just add it to the - * thread's pending set. - */ - sigaddset(&pthread->sigpend, sig); - pthread->check_pending = 1; - if (info == NULL) - build_siginfo(&pthread->siginfo[sig], sig); - else if (info != &pthread->siginfo[sig]) - memcpy(&pthread->siginfo[sig], info, - sizeof(*info)); - if ((pthread->blocked != 0) && !THR_IN_CRITICAL(pthread)) - kse_thr_interrupt(&pthread->tmbx /* XXX - restart?!?! */); - } - else { - restart = _thread_sigact[sig - 1].sa_flags & SA_RESTART; + DBG_MSG(">>> _thr_sig_add\n"); - /* Make sure this signal isn't still in the pending set: */ - sigdelset(&pthread->sigpend, sig); + curkse = _get_curkse(); + restart = _thread_sigact[sig - 1].sa_flags & SA_RESTART; + fromproc = (curthread == _thr_sig_daemon); + if (pthread->state == PS_DEAD || pthread->state == PS_DEADLOCK || + pthread->state == PS_STATE_MAX) + return; /* return false */ + +#ifdef NOTYET + if ((pthread->attrs.flags & PTHREAD_SCOPE_SYSTEM) != 0) { + if (!fromproc) + kse_thr_interrupt(&pthread->tmbx, 0, sig); + return; + } +#endif + + if (pthread->curframe == NULL || + SIGISMEMBER(pthread->sigmask, sig) || + THR_IN_CRITICAL(pthread)) { + /* thread is running or signal was being masked */ + if (!fromproc) { + SIGADDSET(pthread->sigpend, sig); + if (info == NULL) + build_siginfo(&pthread->siginfo[sig], sig); + else if (info != &pthread->siginfo[sig]) + memcpy(&pthread->siginfo[sig], info, + sizeof(*info)); + } else { + if (!_thr_getprocsig(sig, &pthread->siginfo[sig])) + return; + SIGADDSET(pthread->sigpend, sig); + } + if (!SIGISMEMBER(pthread->sigmask, sig)) { + pthread->check_pending = 1; + if (pthread->blocked != 0 && !THR_IN_CRITICAL(pthread)) + kse_thr_interrupt(&pthread->tmbx, + restart ? -2 : -1); + } + } + else { + /* if process signal not exists, just return */ + if (fromproc) { + if (!_thr_getprocsig(sig, &siginfo)) + return; + info = &siginfo; + } /* * Process according to thread state: */ switch (pthread->state) { - /* - * States which do not change when a signal is trapped: - */ case PS_DEAD: case PS_DEADLOCK: + case PS_STATE_MAX: + return; /* XXX return false */ case PS_LOCKWAIT: case PS_SUSPENDED: - case PS_STATE_MAX: /* * You can't call a signal handler for threads in these * states. */ suppress_handler = 1; break; - - /* - * States which do not need any cleanup handling when signals - * occur: - */ case PS_RUNNING: - /* - * Remove the thread from the queue before changing its - * priority: - */ - if ((pthread->flags & THR_FLAGS_IN_RUNQ) != 0) + if ((pthread->flags & THR_FLAGS_IN_RUNQ)) { THR_RUNQ_REMOVE(pthread); + pthread->active_priority |= THR_SIGNAL_PRIORITY; + THR_RUNQ_INSERT_TAIL(pthread); + } else { + /* Possible not in RUNQ and has curframe ? */ + pthread->active_priority |= THR_SIGNAL_PRIORITY; + } + suppress_handler = 1; break; - /* * States which cannot be interrupted but still require the * signal handler to run: */ case PS_COND_WAIT: case PS_MUTEX_WAIT: - /* - * Remove the thread from the wait queue. It will - * be added back to the wait queue once all signal - * handlers have been invoked. - */ - KSE_WAITQ_REMOVE(pthread->kse, pthread); break; case PS_SLEEP_WAIT: /* * Unmasked signals always cause sleep to terminate * early regardless of SA_RESTART: */ pthread->interrupted = 1; - KSE_WAITQ_REMOVE(pthread->kse, pthread); - THR_SET_STATE(pthread, PS_RUNNING); break; case PS_JOIN: + break; + case PS_SIGSUSPEND: - KSE_WAITQ_REMOVE(pthread->kse, pthread); - THR_SET_STATE(pthread, PS_RUNNING); + pthread->interrupted = 1; break; case PS_SIGWAIT: + if (info == NULL) + build_siginfo(&pthread->siginfo[sig], sig); + else if (info != &pthread->siginfo[sig]) + memcpy(&pthread->siginfo[sig], info, + sizeof(*info)); /* * The signal handler is not called for threads in * SIGWAIT. */ suppress_handler = 1; - /* Wake up the thread if the signal is blocked. */ - if (sigismember(pthread->data.sigwait, sig)) { + /* Wake up the thread if the signal is not blocked. */ + if (!SIGISMEMBER(pthread->sigmask, sig)) { /* Return the signal number: */ - pthread->signo = sig; - + *(pthread->data.sigwaitinfo) = pthread->siginfo[sig]; + pthread->sigmask = pthread->oldsigmask; /* Make the thread runnable: */ _thr_setrunnable_unlocked(pthread); - } else + } else { /* Increment the pending signal count. */ - sigaddset(&pthread->sigpend, sig); - break; + SIGADDSET(pthread->sigpend, sig); + pthread->check_pending = 1; + } + + return; } + SIGADDSET(pthread->sigpend, sig); + if (info == NULL) + build_siginfo(&pthread->siginfo[sig], sig); + else if (info != &pthread->siginfo[sig]) + memcpy(&pthread->siginfo[sig], info, sizeof(*info)); + if (suppress_handler == 0) { /* * Setup a signal frame and save the current threads * state: */ - thr_sigframe_add(pthread, sig, info); - - if (pthread->state != PS_RUNNING) - THR_SET_STATE(pthread, PS_RUNNING); - - /* - * The thread should be removed from all scheduling - * queues at this point. Raise the priority and - * place the thread in the run queue. It is also - * possible for a signal to be sent to a suspended - * thread, mostly via pthread_kill(). If a thread - * is suspended, don't insert it into the priority - * queue; just set its state to suspended and it - * will run the signal handler when it is resumed. - */ + thr_sigframe_add(pthread); + if (pthread->flags & THR_FLAGS_IN_RUNQ) + THR_RUNQ_REMOVE(pthread); pthread->active_priority |= THR_SIGNAL_PRIORITY; - if ((pthread->flags & THR_FLAGS_IN_RUNQ) == 0) - THR_RUNQ_INSERT_TAIL(pthread); + _thr_setrunnable_unlocked(pthread); + } else { + pthread->check_pending = 1; } } + + DBG_MSG("<<< _thr_sig_add\n"); } static void thr_sig_check_state(struct pthread *pthread, int sig) { /* * Process according to thread state: */ switch (pthread->state) { /* * States which do not change when a signal is trapped: */ case PS_RUNNING: case PS_LOCKWAIT: case PS_MUTEX_WAIT: case PS_COND_WAIT: case PS_JOIN: case PS_SUSPENDED: case PS_DEAD: case PS_DEADLOCK: case PS_STATE_MAX: break; case PS_SIGWAIT: + build_siginfo(&pthread->siginfo[sig], sig); /* Wake up the thread if the signal is blocked. */ - if (sigismember(pthread->data.sigwait, sig)) { + if (!SIGISMEMBER(pthread->sigmask, sig)) { /* Return the signal number: */ - pthread->signo = sig; - + *(pthread->data.sigwaitinfo) = pthread->siginfo[sig]; + pthread->sigmask = pthread->oldsigmask; /* Change the state of the thread to run: */ _thr_setrunnable_unlocked(pthread); - } else + } else { /* Increment the pending signal count. */ - sigaddset(&pthread->sigpend, sig); + SIGADDSET(pthread->sigpend, sig); + pthread->check_pending = 1; + } break; case PS_SIGSUSPEND: case PS_SLEEP_WAIT: /* * Remove the thread from the wait queue and make it * runnable: */ _thr_setrunnable_unlocked(pthread); /* Flag the operation as interrupted: */ pthread->interrupted = 1; break; } } /* * Send a signal to a specific thread (ala pthread_kill): */ void _thr_sig_send(struct pthread *pthread, int sig) { struct pthread *curthread = _get_curthread(); +#ifdef NOTYET + if ((pthread->attr.flags & PTHREAD_SCOPE_SYSTEM) == 0) { + kse_thr_interrupt(&pthread->tmbx, sig); + return; + } +#endif /* Lock the scheduling queue of the target thread. */ THR_SCHED_LOCK(curthread, pthread); /* Check for signals whose actions are SIG_DFL: */ if (_thread_sigact[sig - 1].sa_handler == SIG_DFL) { /* * Check to see if a temporary signal handler is * installed for sigwaiters: */ - if (_thread_dfl_count[sig] == 0) { + if (_thread_dfl_count[sig - 1] == 0) { /* * Deliver the signal to the process if a handler * is not installed: */ THR_SCHED_UNLOCK(curthread, pthread); kill(getpid(), sig); THR_SCHED_LOCK(curthread, pthread); } /* * Assuming we're still running after the above kill(), * make any necessary state changes to the thread: */ thr_sig_check_state(pthread, sig); THR_SCHED_UNLOCK(curthread, pthread); } /* * Check that the signal is not being ignored: */ else if (_thread_sigact[sig - 1].sa_handler != SIG_IGN) { - if (pthread->state == PS_SIGWAIT && - sigismember(pthread->data.sigwait, sig)) { - /* Return the signal number: */ - pthread->signo = sig; - - /* Change the state of the thread to run: */ - _thr_setrunnable_unlocked(pthread); - THR_SCHED_UNLOCK(curthread, pthread); - } else if (sigismember(&pthread->tmbx.tm_context.uc_sigmask, sig)) { - /* Add the signal to the pending set: */ - sigaddset(&pthread->sigpend, sig); - THR_SCHED_UNLOCK(curthread, pthread); - } else if (pthread == curthread) { - ucontext_t uc; - siginfo_t info; - volatile int once; - - THR_SCHED_UNLOCK(curthread, pthread); - build_siginfo(&info, sig); - once = 0; - THR_GETCONTEXT(&uc); - if (once == 0) { - once = 1; - /* - * Call the signal handler for the current - * thread: - */ - thr_sig_invoke_handler(curthread, sig, - &info, &uc); - } - } else { - /* - * Perform any state changes due to signal - * arrival: - */ - _thr_sig_add(pthread, sig, NULL); - THR_SCHED_UNLOCK(curthread, pthread); - } + _thr_sig_add(pthread, sig, NULL); + THR_SCHED_UNLOCK(curthread, pthread); + /* XXX + * If thread sent signal to itself, check signals now. + * It is not really needed, _kse_critical_leave should + * have already checked signals. + */ + if (pthread == curthread && curthread->check_pending) + _thr_sig_check_pending(curthread); + } else { + THR_SCHED_UNLOCK(curthread, pthread); } } static void -thr_sigframe_add(struct pthread *thread, int sig, siginfo_t *info) +thr_sigframe_add(struct pthread *thread) { if (thread->curframe == NULL) PANIC("Thread doesn't have signal frame "); - if (thread->have_signals == 0) { + if (thread->curframe->psf_valid == 0) { + thread->curframe->psf_valid = 1; /* * Multiple signals can be added to the same signal * frame. Only save the thread's state the first time. */ thr_sigframe_save(thread, thread->curframe); - thread->have_signals = 1; - thread->flags &= THR_FLAGS_PRIVATE; } - sigaddset(&thread->curframe->psf_sigset, sig); - if (info == NULL) - build_siginfo(&thread->siginfo[sig], sig); - else if (info != &thread->siginfo[sig]) - memcpy(&thread->siginfo[sig], info, sizeof(*info)); - - /* Setup the new signal mask. */ - SIGSETOR(thread->tmbx.tm_context.uc_sigmask, - _thread_sigact[sig - 1].sa_mask); - sigaddset(&thread->tmbx.tm_context.uc_sigmask, sig); } -void +static void thr_sigframe_restore(struct pthread *thread, struct pthread_sigframe *psf) { + if (psf->psf_valid == 0) + PANIC("invalid pthread_sigframe\n"); thread->flags = psf->psf_flags; thread->interrupted = psf->psf_interrupted; thread->signo = psf->psf_signo; thread->state = psf->psf_state; thread->data = psf->psf_wait_data; thread->wakeup_time = psf->psf_wakeup_time; - if (thread->sigmask_seqno == psf->psf_seqno) - thread->tmbx.tm_context.uc_sigmask = psf->psf_sigmask; - else - thread->tmbx.tm_context.uc_sigmask = thread->sigmask; } static void thr_sigframe_save(struct pthread *thread, struct pthread_sigframe *psf) { /* This has to initialize all members of the sigframe. */ psf->psf_flags = thread->flags & (THR_FLAGS_PRIVATE | THR_FLAGS_IN_TDLIST); psf->psf_interrupted = thread->interrupted; psf->psf_signo = thread->signo; psf->psf_state = thread->state; psf->psf_wait_data = thread->data; psf->psf_wakeup_time = thread->wakeup_time; - psf->psf_sigmask = thread->tmbx.tm_context.uc_sigmask; - psf->psf_seqno = thread->sigmask_seqno; - sigemptyset(&psf->psf_sigset); } + +void +_thr_signal_init(void) +{ + sigset_t sigset; + struct sigaction act; + int i; + + SIGFILLSET(sigset); + __sys_sigprocmask(SIG_SETMASK, &sigset, &_thr_initial->sigmask); + /* Enter a loop to get the existing signal status: */ + for (i = 1; i <= _SIG_MAXSIG; i++) { + /* Check for signals which cannot be trapped: */ + if (i == SIGKILL || i == SIGSTOP) { + } + + /* Get the signal handler details: */ + else if (__sys_sigaction(i, NULL, + &_thread_sigact[i - 1]) != 0) { + /* + * Abort this process if signal + * initialisation fails: + */ + PANIC("Cannot read signal handler info"); + } + } + /* + * Install the signal handler for SIGINFO. It isn't + * really needed, but it is nice to have for debugging + * purposes. + */ + _thread_sigact[SIGINFO - 1].sa_flags = SA_SIGINFO | SA_RESTART; + SIGEMPTYSET(act.sa_mask); + act.sa_flags = SA_SIGINFO | SA_RESTART; + act.sa_sigaction = (__siginfohandler_t *)&_thr_sig_handler; + if (__sys_sigaction(SIGINFO, &act, NULL) != 0) { + /* + * Abort this process if signal initialisation fails: + */ + PANIC("Cannot initialize signal handler"); + } +} + +void +_thr_signal_deinit(void) +{ + sigset_t tmpmask, oldmask; + int i; + + SIGFILLSET(tmpmask); + SIG_CANTMASK(tmpmask); + __sys_sigprocmask(SIG_SETMASK, &tmpmask, &oldmask); + /* Enter a loop to get the existing signal status: */ + for (i = 1; i <= _SIG_MAXSIG; i++) { + /* Check for signals which cannot be trapped: */ + if (i == SIGKILL || i == SIGSTOP) { + } + + /* Set the signal handler details: */ + else if (__sys_sigaction(i, &_thread_sigact[i - 1], NULL) != 0) { + /* + * Abort this process if signal + * initialisation fails: + */ + PANIC("Cannot set signal handler info"); + } + } + __sys_sigprocmask(SIG_SETMASK, &oldmask, NULL); +} + Index: head/lib/libkse/thread/thr_sigaction.c =================================================================== --- head/lib/libkse/thread/thr_sigaction.c (revision 116976) +++ head/lib/libkse/thread/thr_sigaction.c (revision 116977) @@ -1,106 +1,113 @@ /* * 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. * * $FreeBSD$ */ #include #include #include #include "thr_private.h" __weak_reference(_sigaction, sigaction); int _sigaction(int sig, const struct sigaction * act, struct sigaction * oact) { int ret = 0; struct sigaction gact; + struct pthread *curthread; + kse_critical_t crit; /* Check if the signal number is out of range: */ - if (sig < 1 || sig > NSIG) { + if (sig < 1 || sig > _SIG_MAXSIG) { /* Return an invalid argument: */ errno = EINVAL; ret = -1; } else { - if (_thr_initial == NULL) - _libpthread_init(NULL); + if (!_kse_isthreaded()) + return __sys_sigaction(sig, act, oact); + crit = _kse_critical_enter(); + curthread = _get_curthread(); + KSE_LOCK_ACQUIRE(curthread->kse, &_thread_signal_lock); /* * Check if the existing signal action structure contents are * to be returned: */ if (oact != NULL) { /* Return the existing signal action contents: */ oact->sa_handler = _thread_sigact[sig - 1].sa_handler; oact->sa_mask = _thread_sigact[sig - 1].sa_mask; oact->sa_flags = _thread_sigact[sig - 1].sa_flags; } /* Check if a signal action was supplied: */ if (act != NULL) { /* Set the new signal handler: */ _thread_sigact[sig - 1].sa_mask = act->sa_mask; _thread_sigact[sig - 1].sa_flags = act->sa_flags; _thread_sigact[sig - 1].sa_handler = act->sa_handler; } /* * Check if the kernel needs to be advised of a change * in signal action: */ if (act != NULL && sig != SIGINFO) { gact.sa_mask = act->sa_mask; gact.sa_flags = SA_SIGINFO | act->sa_flags; /* * Check if the signal handler is being set to * the default or ignore handlers: */ if (act->sa_handler == SIG_DFL || act->sa_handler == SIG_IGN) /* Specify the built in handler: */ gact.sa_handler = act->sa_handler; else /* * Specify the thread kernel signal * handler: */ gact.sa_handler = (void (*) ())_thr_sig_handler; /* Change the signal action in the kernel: */ if (__sys_sigaction(sig, &gact, NULL) != 0) ret = -1; } + KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock); + _kse_critical_leave(crit); } /* Return the completion status: */ return (ret); } Index: head/lib/libkse/thread/thr_sigmask.c =================================================================== --- head/lib/libkse/thread/thr_sigmask.c (revision 116976) +++ head/lib/libkse/thread/thr_sigmask.c (revision 116977) @@ -1,103 +1,100 @@ /* * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by John Birrell. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include "thr_private.h" __weak_reference(_pthread_sigmask, pthread_sigmask); int _pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) { struct pthread *curthread = _get_curthread(); int ret; + if (! _kse_isthreaded()) + _kse_setthreaded(1); + + THR_SCHED_LOCK(curthread, curthread); ret = 0; if (oset != NULL) /* Return the current mask: */ - *oset = curthread->tmbx.tm_context.uc_sigmask; + *oset = curthread->sigmask; /* Check if a new signal set was provided by the caller: */ if (set != NULL) { - THR_SCHED_LOCK(curthread, curthread); - /* Process according to what to do: */ switch (how) { /* Block signals: */ case SIG_BLOCK: /* Add signals to the existing mask: */ - SIGSETOR(curthread->tmbx.tm_context.uc_sigmask, *set); + SIGSETOR(curthread->sigmask, *set); break; /* Unblock signals: */ case SIG_UNBLOCK: /* Clear signals from the existing mask: */ - SIGSETNAND(curthread->tmbx.tm_context.uc_sigmask, *set); + SIGSETNAND(curthread->sigmask, *set); break; /* Set the signal process mask: */ case SIG_SETMASK: /* Set the new mask: */ - curthread->tmbx.tm_context.uc_sigmask = *set; + curthread->sigmask = *set; break; /* Trap invalid actions: */ default: /* Return an invalid argument: */ errno = EINVAL; ret = -1; break; } - - if (ret == 0) { - curthread->sigmask = - curthread->tmbx.tm_context.uc_sigmask; - curthread->sigmask_seqno++; - } - + SIG_CANTMASK(curthread->sigmask); THR_SCHED_UNLOCK(curthread, curthread); /* * Run down any pending signals: */ if (ret == 0) _thr_sig_check_pending(curthread); - } + } else + THR_SCHED_UNLOCK(curthread, curthread); return (ret); } Index: head/lib/libkse/thread/thr_sigpending.c =================================================================== --- head/lib/libkse/thread/thr_sigpending.c (revision 116976) +++ head/lib/libkse/thread/thr_sigpending.c (revision 116977) @@ -1,66 +1,71 @@ /* * Copyright (c) 1999 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. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by John Birrell. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include "thr_private.h" __weak_reference(_sigpending, sigpending); int _sigpending(sigset_t *set) { struct pthread *curthread = _get_curthread(); kse_critical_t crit; int ret = 0; /* Check for a null signal set pointer: */ if (set == NULL) { /* Return an invalid argument: */ ret = EINVAL; } else { - *set = curthread->sigpend; + if (!_kse_isthreaded()) + return __sys_sigpending(set); + crit = _kse_critical_enter(); + KSE_SCHED_LOCK(curthread->kse, curthread->kseg); + *set = curthread->sigpend; + KSE_SCHED_UNLOCK(curthread->kse, curthread->kseg); KSE_LOCK_ACQUIRE(curthread->kse, &_thread_signal_lock); SIGSETOR(*set, _thr_proc_sigpending); KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock); _kse_critical_leave(crit); } /* Return the completion status: */ return (ret); } Index: head/lib/libkse/thread/thr_sigprocmask.c =================================================================== --- head/lib/libkse/thread/thr_sigprocmask.c (revision 116976) +++ head/lib/libkse/thread/thr_sigprocmask.c (revision 116977) @@ -1,53 +1,54 @@ /* * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by John Birrell. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include "thr_private.h" __weak_reference(_sigprocmask, sigprocmask); int _sigprocmask(int how, const sigset_t *set, sigset_t *oset) { int ret; - ret = pthread_sigmask(how, set, oset); - if ((ret == 0) && (_kse_isthreaded() == 0)) + if (_kse_isthreaded() == 0) ret = __sys_sigprocmask(how, set, oset); + else + ret = pthread_sigmask(how, set, oset); return (ret); } Index: head/lib/libkse/thread/thr_sigsuspend.c =================================================================== --- head/lib/libkse/thread/thr_sigsuspend.c (revision 116976) +++ head/lib/libkse/thread/thr_sigsuspend.c (revision 116977) @@ -1,87 +1,98 @@ /* * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by John Birrell. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include "thr_private.h" __weak_reference(__sigsuspend, sigsuspend); int _sigsuspend(const sigset_t *set) { struct pthread *curthread = _get_curthread(); int ret = -1; + sigset_t osigmask; + if (!_kse_isthreaded()) + return __sys_sigsuspend(set); + /* Check if a new signal set was provided by the caller: */ if (set != NULL) { THR_LOCK_SWITCH(curthread); + /* Save current sigmask */ + memcpy(&osigmask, &curthread->sigmask, sizeof(osigmask)); /* Change the caller's mask: */ - memcpy(&curthread->tmbx.tm_context.uc_sigmask, - set, sizeof(sigset_t)); + memcpy(&curthread->sigmask, set, sizeof(sigset_t)); THR_SET_STATE(curthread, PS_SIGSUSPEND); /* Wait for a signal: */ _thr_sched_switch_unlocked(curthread); /* Always return an interrupted error: */ errno = EINTR; + THR_SCHED_LOCK(curthread, curthread); /* Restore the signal mask: */ - memcpy(&curthread->tmbx.tm_context.uc_sigmask, - &curthread->sigmask, sizeof(sigset_t)); + memcpy(&curthread->sigmask, &osigmask, sizeof(sigset_t)); + THR_SCHED_UNLOCK(curthread, curthread); + /* + * signal mask is reloaded, need to check if there is + * pending proc signal I can handle. + */ + _thr_sig_check_pending(curthread); } else { /* Return an invalid argument error: */ errno = EINVAL; } /* Return the completion status: */ return (ret); } int __sigsuspend(const sigset_t * set) { struct pthread *curthread = _get_curthread(); int ret; _thr_enter_cancellation_point(curthread); ret = _sigsuspend(set); _thr_leave_cancellation_point(curthread); return (ret); } Index: head/lib/libkse/thread/thr_sigwait.c =================================================================== --- head/lib/libkse/thread/thr_sigwait.c (revision 116976) +++ head/lib/libkse/thread/thr_sigwait.c (revision 116977) @@ -1,175 +1,249 @@ //depot/projects/kse/lib/libpthread/thread/thr_sigwait.c#1 - branch change 15154 (text+ko) /* * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by John Birrell. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include "thr_private.h" -__weak_reference(_sigwait, sigwait); +__weak_reference(__sigwait, sigwait); +__weak_reference(__sigtimedwait, sigtimedwait); +__weak_reference(__sigwaitinfo, sigwaitinfo); -int -_sigwait(const sigset_t *set, int *sig) +static int +lib_sigtimedwait(const sigset_t *set, siginfo_t *info, + const struct timespec * timeout) { struct pthread *curthread = _get_curthread(); int ret = 0; int i; sigset_t tempset, waitset; struct sigaction act; kse_critical_t crit; + siginfo_t siginfo; - _thr_enter_cancellation_point(curthread); + if (!_kse_isthreaded()) { + if (info == NULL) + info = &siginfo; + return __sys_sigtimedwait((sigset_t *)set, info, + (struct timespec *)timeout); + } /* * Specify the thread kernel signal handler. */ act.sa_handler = (void (*) ()) _thr_sig_handler; act.sa_flags = SA_RESTART | SA_SIGINFO; /* Ensure the signal handler cannot be interrupted by other signals: */ - sigfillset(&act.sa_mask); + SIGFILLSET(act.sa_mask); /* * Initialize the set of signals that will be waited on: */ waitset = *set; /* These signals can't be waited on. */ - sigdelset(&waitset, SIGKILL); - sigdelset(&waitset, SIGSTOP); + SIGDELSET(waitset, SIGKILL); + SIGDELSET(waitset, SIGSTOP); - /* - * Check to see if a pending signal is in the wait mask. - * This has to be atomic. - */ - tempset = curthread->sigpend; crit = _kse_critical_enter(); KSE_LOCK_ACQUIRE(curthread->kse, &_thread_signal_lock); - SIGSETOR(tempset, _thr_proc_sigpending); - SIGSETAND(tempset, waitset); - if (SIGNOTEMPTY(tempset)) { - /* Enter a loop to find a pending signal: */ - for (i = 1; i < NSIG; i++) { - if (sigismember (&tempset, i)) - break; - } - /* Clear the pending signal: */ - if (sigismember(&curthread->sigpend, i)) - sigdelset(&curthread->sigpend, i); - else - sigdelset(&_thr_proc_sigpending, i); - - KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock); - _kse_critical_leave(crit); - _thr_leave_cancellation_point(curthread); - /* Return the signal number to the caller: */ - *sig = i; - return (0); - } - /* - * Enter a loop to find the signals that are SIG_DFL. For + * Enter a loop to find the signals that are SIG_DFL. For * these signals we must install a dummy signal handler in * order for the kernel to pass them in to us. POSIX says * that the _application_ must explicitly install a dummy * handler for signals that are SIG_IGN in order to sigwait * on them. Note that SIG_IGN signals are left in the * mask because a subsequent sigaction could enable an * ignored signal. */ - sigemptyset(&tempset); - for (i = 1; i < NSIG; i++) { - if (sigismember(&waitset, i) && + SIGEMPTYSET(tempset); + for (i = 1; i <= _SIG_MAXSIG; i++) { + if (SIGISMEMBER(waitset, i) && (_thread_sigact[i - 1].sa_handler == SIG_DFL)) { - _thread_dfl_count[i]++; - sigaddset(&tempset, i); - if (_thread_dfl_count[i] == 1) { + _thread_dfl_count[i - 1]++; + SIGADDSET(tempset, i); + if (_thread_dfl_count[i - 1] == 1) { if (__sys_sigaction(i, &act, NULL) != 0) - ret = -1; + /* ret = -1 */; } } } - /* Done accessing _thread_dfl_count for now. */ - KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock); - _kse_critical_leave(crit); - if (ret == 0) { - /* - * Save the wait signal mask. The wait signal - * mask is independent of the threads signal mask - * and requires separate storage. - */ - curthread->data.sigwait = &waitset; + if (ret == 0) { + /* Done accessing _thread_dfl_count for now. */ + KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock); + KSE_SCHED_LOCK(curthread->kse, curthread->kseg); + for (i = 1; i <= _SIG_MAXSIG; ++i) { + if (SIGISMEMBER(waitset, i) && + SIGISMEMBER(curthread->sigpend, i)) { + SIGDELSET(curthread->sigpend, i); + *info = curthread->siginfo[i]; + KSE_SCHED_UNLOCK(curthread->kse, + curthread->kseg); + _kse_critical_leave(crit); + return (i); + } + } + curthread->timeout = 0; + _thr_set_timeout(timeout); /* Wait for a signal: */ - THR_LOCK_SWITCH(curthread); + curthread->oldsigmask = curthread->sigmask; + siginfo.si_signo = 0; + curthread->data.sigwaitinfo = &siginfo; + SIGFILLSET(curthread->sigmask); + SIGSETNAND(curthread->sigmask, waitset); THR_SET_STATE(curthread, PS_SIGWAIT); _thr_sched_switch_unlocked(curthread); - /* Return the signal number to the caller: */ - *sig = curthread->signo; + /* + * Return the signal number to the caller: + * XXX Here is race, how about a signal come in before + * we reach here? so we might got an incorrect timeout + * status. + */ + if (siginfo.si_signo > 0) { + if (info) + *info = siginfo; + ret = siginfo.si_signo; + } else { + if (curthread->timeout) + errno = EAGAIN; + ret = -1; + } /* * Probably unnecessary, but since it's in a union struct * we don't know how it could be used in the future. */ - curthread->data.sigwait = NULL; + crit = _kse_critical_enter(); + curthread->data.sigwaitinfo = NULL; + /* + * Relock the array of SIG_DFL wait counts. + */ + KSE_LOCK_ACQUIRE(curthread->kse, &_thread_signal_lock); } - /* - * Relock the array of SIG_DFL wait counts. - */ - crit = _kse_critical_enter(); - KSE_LOCK_ACQUIRE(curthread->kse, &_thread_signal_lock); - /* Restore the sigactions: */ act.sa_handler = SIG_DFL; - for (i = 1; i < NSIG; i++) { - if (sigismember(&tempset, i)) { - _thread_dfl_count[i]--; + for (i = 1; i <= _SIG_MAXSIG; i++) { + if (SIGISMEMBER(tempset, i)) { + _thread_dfl_count[i - 1]--; if ((_thread_sigact[i - 1].sa_handler == SIG_DFL) && - (_thread_dfl_count[i] == 0)) { + (_thread_dfl_count[i - 1] == 0)) { if (__sys_sigaction(i, &act, NULL) != 0) - ret = -1; + /* ret = -1 */ ; } } } /* Done accessing _thread_dfl_count. */ KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock); _kse_critical_leave(crit); + + return (ret); +} + +int +__sigtimedwait(const sigset_t *set, siginfo_t *info, + const struct timespec * timeout) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_enter_cancellation_point(curthread); + ret = lib_sigtimedwait(set, info, timeout); _thr_leave_cancellation_point(curthread); + return (ret); +} - /* Return the completion status: */ +int _sigtimedwait(const sigset_t *set, siginfo_t *info, + const struct timespec * timeout) +{ + return lib_sigtimedwait(set, info, timeout); +} + +int +__sigwaitinfo(const sigset_t *set, siginfo_t *info) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_enter_cancellation_point(curthread); + ret = lib_sigtimedwait(set, info, NULL); + _thr_leave_cancellation_point(curthread); return (ret); } + +int +_sigwaitinfo(const sigset_t *set, siginfo_t *info) +{ + return lib_sigtimedwait(set, info, NULL); +} + +int +__sigwait(const sigset_t *set, int *sig) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_enter_cancellation_point(curthread); + ret = lib_sigtimedwait(set, NULL, NULL); + if (ret > 0) { + *sig = ret; + ret = 0; + } + else + ret = -1; + _thr_leave_cancellation_point(curthread); + return (ret); +} + +int +_sigwait(const sigset_t *set, int *sig) +{ + int ret; + + ret = lib_sigtimedwait(set, NULL, NULL); + if (ret > 0) { + *sig = ret; + ret = 0; + } else { + ret = -1; + } + return (ret); +} + Index: head/lib/libpthread/pthread.map =================================================================== --- head/lib/libpthread/pthread.map (revision 116976) +++ head/lib/libpthread/pthread.map (revision 116977) @@ -1,296 +1,300 @@ # $FreeBSD$ LIBTHREAD_1_0 { global: ___creat; __close; __error; __fcntl; __fsync; __msync; __nanosleep; __open; __poll; __pthread_cond_timedwait; __pthread_cond_wait; __pthread_mutex_lock; __pthread_mutex_trylock; __read; __readv; __select; __sigsuspend; __wait4; __write; __writev; _aio_suspend; _fork; _nanosleep; _pause; _pselect; _pthread_attr_default; _pthread_attr_destroy; _pthread_attr_get_np; _pthread_attr_getdetachstate; _pthread_attr_getguardsize; _pthread_attr_getinheritsched; _pthread_attr_getschedparam; _pthread_attr_getschedpolicy; _pthread_attr_getscope; _pthread_attr_getstack; _pthread_attr_getstackaddr; _pthread_attr_getstacksize; _pthread_attr_init; _pthread_attr_setcreatesuspend_np; _pthread_attr_setdetachstate; _pthread_attr_setguardsize; _pthread_attr_setinheritsched; _pthread_attr_setschedparam; _pthread_attr_setschedpolicy; _pthread_attr_setscope; _pthread_attr_setstack; _pthread_attr_setstackaddr; _pthread_attr_setstacksize; _pthread_cancel; _pthread_cleanup_pop; _pthread_cleanup_push; _pthread_cond_broadcast; _pthread_cond_destroy; _pthread_cond_init; _pthread_cond_signal; _pthread_cond_timedwait; _pthread_cond_wait; _pthread_condattr_default; _pthread_condattr_destroy; _pthread_condattr_init; _pthread_create; _pthread_detach; _pthread_equal; _pthread_exit; _pthread_getconcurrency; _pthread_getprio; _pthread_getschedparam; _pthread_getspecific; _pthread_join; _pthread_key_create; _pthread_key_delete; _pthread_kill; _pthread_main_np; _pthread_multi_np; _pthread_mutex_destroy; _pthread_mutex_getprioceiling; _pthread_mutex_init; _pthread_mutex_lock; _pthread_mutex_setprioceiling; _pthread_mutex_trylock; _pthread_mutex_unlock; _pthread_mutexattr_default; _pthread_mutexattr_destroy; _pthread_mutexattr_getkind_np; _pthread_mutexattr_getprioceiling; _pthread_mutexattr_getprotocol; _pthread_mutexattr_gettype; _pthread_mutexattr_init; _pthread_mutexattr_setkind_np; _pthread_mutexattr_setprioceiling; _pthread_mutexattr_setprotocol; _pthread_mutexattr_settype; _pthread_once; _pthread_resume_all_np; _pthread_resume_np; _pthread_rwlock_destroy; _pthread_rwlock_init; _pthread_rwlock_rdlock; _pthread_rwlock_tryrdlock; _pthread_rwlock_trywrlock; _pthread_rwlock_unlock; _pthread_rwlock_wrlock; _pthread_rwlockattr_destroy; _pthread_rwlockattr_getpshared; _pthread_rwlockattr_init; _pthread_rwlockattr_setpshared; _pthread_self; _pthread_set_name_np; _pthread_setcancelstate; _pthread_setcanceltype; _pthread_setconcurrency; _pthread_setprio; _pthread_setschedparam; _pthread_setspecific; _pthread_sigmask; _pthread_single_np; _pthread_suspend_all_np; _pthread_suspend_np; _pthread_switch_add_np; _pthread_switch_delete_np; _pthread_testcancel; _pthread_yield; _sched_yield; _sem_close; _sem_destroy; _sem_getvalue; _sem_init; _sem_open; _sem_post; _sem_trywait; _sem_unlink; _sem_wait; _sigaction; _sigpending; _sigprocmask; _sigsuspend; _sigwait; + _sigtimedwait; + _sigwaitinfo; _sleep; _spinlock; _spinlock_debug; _spinunlock; _system; _tcdrain; _vfork; _wait; _waitpid; aio_suspend; close; creat; fcntl; fork; fsync; msync; nanosleep; open; pause; poll; pselect; pthread_attr_destroy; pthread_attr_get_np; pthread_attr_getdetachstate; pthread_attr_getguardsize; pthread_attr_getinheritsched; pthread_attr_getschedparam; pthread_attr_getschedpolicy; pthread_attr_getscope; pthread_attr_getstack; pthread_attr_getstackaddr; pthread_attr_getstacksize; pthread_attr_init; pthread_attr_setcreatesuspend_np; pthread_attr_setdetachstate; pthread_attr_setguardsize; pthread_attr_setinheritsched; pthread_attr_setschedparam; pthread_attr_setschedpolicy; pthread_attr_setscope; pthread_attr_setstack; pthread_attr_setstackaddr; pthread_attr_setstacksize; pthread_cancel; pthread_cleanup_pop; pthread_cleanup_push; pthread_cond_broadcast; pthread_cond_destroy; pthread_cond_init; pthread_cond_signal; pthread_cond_timedwait; pthread_cond_wait; pthread_condattr_destroy; pthread_condattr_init; pthread_create; pthread_detach; pthread_equal; pthread_exit; pthread_getconcurrency; pthread_getprio; pthread_getschedparam; pthread_getspecific; pthread_join; pthread_key_create; pthread_key_delete; pthread_kill; pthread_main_np; pthread_multi_np; pthread_mutex_destroy; pthread_mutex_getprioceiling; pthread_mutex_init; pthread_mutex_lock; pthread_mutex_setprioceiling; pthread_mutex_trylock; pthread_mutex_unlock; pthread_mutexattr_destroy; pthread_mutexattr_getkind_np; pthread_mutexattr_getprioceiling; pthread_mutexattr_getprotocol; pthread_mutexattr_gettype; pthread_mutexattr_init; pthread_mutexattr_setkind_np; pthread_mutexattr_setprioceiling; pthread_mutexattr_setprotocol; pthread_mutexattr_settype; pthread_once; pthread_resume_all_np; pthread_resume_np; pthread_rwlock_destroy; pthread_rwlock_init; pthread_rwlock_rdlock; pthread_rwlock_tryrdlock; pthread_rwlock_trywrlock; pthread_rwlock_unlock; pthread_rwlock_wrlock; pthread_rwlockattr_destroy; pthread_rwlockattr_getpshared; pthread_rwlockattr_init; pthread_rwlockattr_setpshared; pthread_self; pthread_set_name_np; pthread_setcancelstate; pthread_setcanceltype; pthread_setconcurrency; pthread_setprio; pthread_setschedparam; pthread_setspecific; pthread_sigmask; pthread_single_np; pthread_suspend_all_np; pthread_suspend_np; pthread_switch_add_np; pthread_switch_delete_np; pthread_testcancel; pthread_yield; read; readv; sched_yield; select; sem_close; sem_destroy; sem_getvalue; sem_init; sem_open; sem_post; sem_trywait; sem_unlink; sem_wait; sigaction; sigpending; sigprocmask; sigsuspend; sigwait; + sigwaitinfo; + sigtimedwait; sleep; system; tcdrain; vfork; wait; wait4; waitpid; write; writev; # Debugger needs these. _thread_list; _thread_next_offset; _thread_uniqueid_offset; _thread_state_offset; _thread_name_offset; _thread_ctx_offset; _thread_PS_RUNNING_value; _thread_PS_DEAD_value; local: *; }; Index: head/lib/libpthread/support/thr_support.c =================================================================== --- head/lib/libpthread/support/thr_support.c (revision 116976) +++ head/lib/libpthread/support/thr_support.c (revision 116977) @@ -1,58 +1,61 @@ /*- * Copyright 2003 Alexander Kabaev. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include "thr_private.h" __strong_reference(clock_gettime, _thr_clock_gettime); __strong_reference(kse_exit, _thr_kse_exit); __strong_reference(kse_wakeup, _thr_kse_wakeup); __strong_reference(kse_create, _thr_kse_create); __strong_reference(kse_thr_interrupt, _thr_kse_thr_interrupt); __strong_reference(kse_release, _thr_kse_release); __strong_reference(sigaction, _thr_sigaction); __strong_reference(sigprocmask, _thr_sigprocmask); __strong_reference(sigemptyset, _thr_sigemptyset); __strong_reference(sigaddset, _thr_sigaddset); __strong_reference(sigfillset, _thr_sigfillset); __strong_reference(sigismember, _thr_sigismember); __strong_reference(sigdelset, _thr_sigdelset); __strong_reference(memset, _thr_memset); __strong_reference(memcpy, _thr_memcpy); __strong_reference(strcpy, _thr_strcpy); __strong_reference(strlen, _thr_strlen); __strong_reference(bzero, _thr_bzero); +__strong_reference(bcopy, _thr_bcopy); __strong_reference(__sys_write, _thr__sys_write); +__strong_reference(__sys_sigtimedwait, _thr__sys_sigtimedwait); + Index: head/lib/libpthread/thread/thr_info.c =================================================================== --- head/lib/libpthread/thread/thr_info.c (revision 116976) +++ head/lib/libpthread/thread/thr_info.c (revision 116977) @@ -1,205 +1,220 @@ /* * 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. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include "thr_private.h" #ifndef NELEMENTS #define NELEMENTS(arr) (sizeof(arr) / sizeof(arr[0])) #endif static void dump_thread(int fd, pthread_t pthread, int long_version); __weak_reference(_pthread_set_name_np, pthread_set_name_np); struct s_thread_info { enum pthread_state state; char *name; }; /* Static variables: */ static const struct s_thread_info thread_info[] = { {PS_RUNNING , "Running"}, {PS_LOCKWAIT , "Waiting on an internal lock"}, {PS_MUTEX_WAIT , "Waiting on a mutex"}, {PS_COND_WAIT , "Waiting on a condition variable"}, {PS_SLEEP_WAIT , "Sleeping"}, {PS_SIGSUSPEND , "Suspended, waiting for a signal"}, {PS_SIGWAIT , "Waiting for a signal"}, {PS_JOIN , "Waiting to join"}, {PS_SUSPENDED , "Suspended"}, {PS_DEAD , "Dead"}, {PS_DEADLOCK , "Deadlocked"}, {PS_STATE_MAX , "Not a real state!"} }; void _thread_dump_info(void) { char s[512], tmpfile[128]; pthread_t pthread; int fd, i; for (i = 0; i < 100000; i++) { - snprintf(tmpfile, sizeof(tmpfile), "/tmp/uthread.dump.%u.%i", + snprintf(tmpfile, sizeof(tmpfile), "/tmp/pthread.dump.%u.%i", getpid(), i); /* Open the dump file for append and create it if necessary: */ if ((fd = __sys_open(tmpfile, O_RDWR | O_CREAT | O_EXCL, 0666)) < 0) { /* Can't open the dump file. */ if (errno == EEXIST) continue; /* * We only need to continue in case of * EEXIT error. Most other error * codes means that we will fail all * the times. */ return; } else { break; } } if (i==100000) { /* all 100000 possibilities are in use :( */ return; } else { /* Dump the active threads. */ strcpy(s, "\n\n========\nACTIVE THREADS\n\n"); __sys_write(fd, s, strlen(s)); /* Enter a loop to report each thread in the global list: */ TAILQ_FOREACH(pthread, &_thread_list, tle) { if (pthread->state != PS_DEAD) dump_thread(fd, pthread, /*long_verson*/ 1); } /* * Dump the ready threads. * XXX - We can't easily do this because the run queues * are per-KSEG. */ strcpy(s, "\n\n========\nREADY THREADS - unimplemented\n\n"); __sys_write(fd, s, strlen(s)); /* * Dump the waiting threads. * XXX - We can't easily do this because the wait queues * are per-KSEG. */ strcpy(s, "\n\n========\nWAITING THREADS - unimplemented\n\n"); __sys_write(fd, s, strlen(s)); /* Close the dump file. */ __sys_close(fd); } } static void dump_thread(int fd, pthread_t pthread, int long_version) { struct pthread *curthread = _get_curthread(); char s[512]; int i; /* Find the state: */ for (i = 0; i < NELEMENTS(thread_info) - 1; i++) if (thread_info[i].state == pthread->state) break; /* Output a record for the thread: */ snprintf(s, sizeof(s), "--------------------\n" "Thread %p (%s) prio %3d, blocked %s, state %s [%s:%d]\n", pthread, (pthread->name == NULL) ? "" : pthread->name, pthread->active_priority, (pthread->blocked != 0) ? "yes" : "no", thread_info[i].name, pthread->fname, pthread->lineno); __sys_write(fd, s, strlen(s)); if (long_version != 0) { /* Check if this is the running thread: */ if (pthread == curthread) { /* Output a record for the running thread: */ strcpy(s, "This is the running thread\n"); __sys_write(fd, s, strlen(s)); } /* Check if this is the initial thread: */ if (pthread == _thr_initial) { /* Output a record for the initial thread: */ strcpy(s, "This is the initial thread\n"); __sys_write(fd, s, strlen(s)); } + /* Check if this is the signal daemon thread: */ + if (pthread == _thr_sig_daemon) { + /* Output a record for the signal thread: */ + strcpy(s, "This is the signal daemon thread\n"); + __sys_write(fd, s, strlen(s)); + } /* Process according to thread state: */ switch (pthread->state) { case PS_SIGWAIT: snprintf(s, sizeof(s), "sigmask (hi)"); + __sys_write(fd, s, strlen(s)); + for (i = _SIG_WORDS - 1; i >= 0; i--) { + snprintf(s, sizeof(s), "%08x\n", + pthread->oldsigmask.__bits[i]); + __sys_write(fd, s, strlen(s)); + } + snprintf(s, sizeof(s), "(lo)\n"); + __sys_write(fd, s, strlen(s)); + snprintf(s, sizeof(s), "waitset (hi)"); __sys_write(fd, s, strlen(s)); for (i = _SIG_WORDS - 1; i >= 0; i--) { snprintf(s, sizeof(s), "%08x\n", pthread->sigmask.__bits[i]); __sys_write(fd, s, strlen(s)); } snprintf(s, sizeof(s), "(lo)\n"); __sys_write(fd, s, strlen(s)); break; /* * Trap other states that are not explicitly * coded to dump information: */ default: /* Nothing to do here. */ break; } } } /* Set the thread name for debug: */ void _pthread_set_name_np(pthread_t thread, char *name) { /* Check if the caller has specified a valid thread: */ if (thread != NULL && thread->magic == THR_MAGIC) { if (thread->name != NULL) { /* Free space for previous name. */ free(thread->name); } thread->name = strdup(name); } } Index: head/lib/libpthread/thread/thr_init.c =================================================================== --- head/lib/libpthread/thread/thr_init.c (revision 116976) +++ head/lib/libpthread/thread/thr_init.c (revision 116977) @@ -1,530 +1,495 @@ /* * 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. * * $FreeBSD$ */ /* Allocate space for global thread variables here: */ #define GLOBAL_PTHREAD_PRIVATE #include "namespace.h" #include #include +#include #include #include #include #include #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" #include "ksd.h" 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); static void init_private(void); static void init_main_thread(struct pthread *thread); /* * All weak references used within libc should be in this table. * This is so that static libraries will work. */ static void *references[] = { &_accept, &_bind, &_close, &_connect, &_dup, &_dup2, &_execve, &_fcntl, &_flock, &_flockfile, &_fstat, &_fstatfs, &_fsync, &_funlockfile, &_getdirentries, &_getlogin, &_getpeername, &_getsockname, &_getsockopt, &_ioctl, &_kevent, &_listen, &_nanosleep, &_open, &_pthread_getspecific, &_pthread_key_create, &_pthread_key_delete, &_pthread_mutex_destroy, &_pthread_mutex_init, &_pthread_mutex_lock, &_pthread_mutex_trylock, &_pthread_mutex_unlock, &_pthread_mutexattr_init, &_pthread_mutexattr_destroy, &_pthread_mutexattr_settype, &_pthread_once, &_pthread_setspecific, &_read, &_readv, &_recvfrom, &_recvmsg, &_select, &_sendmsg, &_sendto, &_setsockopt, &_sigaction, &_sigprocmask, &_sigsuspend, &_socket, &_socketpair, &_thread_init_hack, &_wait4, &_write, &_writev }; /* * These are needed when linking statically. All references within * libgcc (and in the future libc) to these routines are weak, but * if they are not (strongly) referenced by the application or other * libraries, then the actual functions will not be loaded. */ static void *libgcc_references[] = { &_pthread_once, &_pthread_key_create, &_pthread_key_delete, &_pthread_getspecific, &_pthread_setspecific, &_pthread_mutex_init, &_pthread_mutex_destroy, &_pthread_mutex_lock, &_pthread_mutex_trylock, &_pthread_mutex_unlock }; #define DUAL_ENTRY(entry) \ (pthread_func_t)entry, (pthread_func_t)entry static pthread_func_t jmp_table[][2] = { {DUAL_ENTRY(_pthread_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 */ {(pthread_func_t)__pthread_cond_wait, (pthread_func_t)_pthread_cond_wait}, /* PJT_COND_WAIT */ {DUAL_ENTRY(_pthread_getspecific)}, /* PJT_GETSPECIFIC */ {DUAL_ENTRY(_pthread_key_create)}, /* PJT_KEY_CREATE */ {DUAL_ENTRY(_pthread_key_delete)}, /* PJT_KEY_DELETE*/ {DUAL_ENTRY(_pthread_main_np)}, /* PJT_MAIN_NP */ {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_mutexattr_destroy)}, /* PJT_MUTEXATTR_DESTROY */ {DUAL_ENTRY(_pthread_mutexattr_init)}, /* PJT_MUTEXATTR_INIT */ {DUAL_ENTRY(_pthread_mutexattr_settype)}, /* PJT_MUTEXATTR_SETTYPE */ {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_setspecific)}, /* PJT_SETSPECIFIC */ {DUAL_ENTRY(_pthread_sigmask)} /* PJT_SIGMASK */ }; static int init_once = 0; /* * Threaded process initialization. * * This is only called under two conditions: * * 1) Some thread routines have detected that the library hasn't yet * been initialized (_thr_initial == NULL && curthread == NULL), or * * 2) An explicit call to reinitialize after a fork (indicated * by curthread != NULL) */ void _libpthread_init(struct pthread *curthread) { int fd; /* Check if this function has already been called: */ if ((_thr_initial != NULL) && (curthread == NULL)) /* Only initialize the threaded application once. */ return; /* * Make gcc quiescent about {,libgcc_}references not being * referenced: */ if ((references[0] == NULL) || (libgcc_references[0] == NULL)) PANIC("Failed loading mandatory references in _thread_init"); /* * Check the size of the jump table to make sure it is preset * with the correct number of entries. */ if (sizeof(jmp_table) != (sizeof(pthread_func_t) * PJT_MAX * 2)) PANIC("Thread jump table not properly initialized"); memcpy(__thr_jtable, jmp_table, sizeof(jmp_table)); /* * Check for the special case of this process running as * or in place of init as pid = 1: */ if ((_thr_pid = getpid()) == 1) { /* * Setup a new session for this process which is * assumed to be running as root. */ if (setsid() == -1) PANIC("Can't set session ID"); if (revoke(_PATH_CONSOLE) != 0) PANIC("Can't revoke console"); if ((fd = __sys_open(_PATH_CONSOLE, O_RDWR)) < 0) PANIC("Can't open console"); if (setlogin("root") == -1) PANIC("Can't set login to root"); if (__sys_ioctl(fd, TIOCSCTTY, (char *) NULL) == -1) PANIC("Can't set controlling terminal"); } /* Initialize pthread private data. */ init_private(); _kse_init(); /* Initialize the initial kse and kseg. */ _kse_initial = _kse_alloc(NULL); if (_kse_initial == NULL) PANIC("Can't allocate initial kse."); _kse_initial->k_kseg = _kseg_alloc(NULL); if (_kse_initial->k_kseg == NULL) PANIC("Can't allocate initial kseg."); _kse_initial->k_schedq = &_kse_initial->k_kseg->kg_schedq; TAILQ_INSERT_TAIL(&_kse_initial->k_kseg->kg_kseq, _kse_initial, k_kgqe); _kse_initial->k_kseg->kg_ksecount = 1; /* Set the initial thread. */ if (curthread == NULL) { /* Create and initialize the initial thread. */ curthread = _thr_alloc(NULL); if (curthread == NULL) PANIC("Can't allocate initial thread"); _thr_initial = curthread; init_main_thread(curthread); } else { /* * The initial thread is the current thread. It is * assumed that the current thread is already initialized * because it is left over from a fork(). */ _thr_initial = curthread; } _kse_initial->k_kseg->kg_threadcount = 0; _thr_initial->kse = _kse_initial; _thr_initial->kseg = _kse_initial->k_kseg; _thr_initial->active = 1; /* * Add the thread to the thread list and to the KSEG's thread * queue. */ THR_LIST_ADD(_thr_initial); KSEG_THRQ_ADD(_kse_initial->k_kseg, _thr_initial); /* Setup the KSE/thread specific data for the current KSE/thread. */ if (_ksd_setprivate(&_thr_initial->kse->k_ksd) != 0) PANIC("Can't set initial KSE specific data"); _set_curkse(_thr_initial->kse); _thr_initial->kse->k_curthread = _thr_initial; _thr_initial->kse->k_flags |= KF_INITIALIZED; _kse_initial->k_curthread = _thr_initial; - + _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) { void *p; int i; /* Zero the initial thread structure. */ p = thread->alloc_addr; memset(thread, 0, sizeof(struct pthread)); thread->alloc_addr = p; /* Setup the thread attributes. */ 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((void *)_usrstack - THR_STACK_INITIAL - _thr_guard_default, _thr_guard_default, 0, MAP_ANON, -1, 0) == MAP_FAILED) PANIC("Cannot allocate red zone for initial thread"); /* * Mark the stack as an application supplied stack so that it * isn't deallocated. * * XXX - I'm not sure it would hurt anything to deallocate * the main thread stack because deallocation doesn't * actually free() it; it just puts it in the free * stack queue for later reuse. */ thread->attr.stackaddr_attr = (void *)_usrstack - THR_STACK_INITIAL; thread->attr.stacksize_attr = THR_STACK_INITIAL; thread->attr.guardsize_attr = _thr_guard_default; thread->attr.flags |= THR_STACK_USER; /* * Write a magic value to the thread structure * to help identify valid ones: */ thread->magic = THR_MAGIC; thread->slice_usec = -1; thread->cancelflags = PTHREAD_CANCEL_ENABLE | PTHREAD_CANCEL_DEFERRED; thread->name = strdup("initial thread"); /* Initialize the thread for signals: */ - sigemptyset(&thread->sigmask); + SIGEMPTYSET(thread->sigmask); /* * Set up the thread mailbox. The threads saved context * is also in the mailbox. */ thread->tmbx.tm_udata = thread; - thread->tmbx.tm_context.uc_sigmask = thread->sigmask; thread->tmbx.tm_context.uc_stack.ss_size = thread->attr.stacksize_attr; thread->tmbx.tm_context.uc_stack.ss_sp = thread->attr.stackaddr_attr; /* Default the priority of the initial thread: */ thread->base_priority = THR_DEFAULT_PRIORITY; thread->active_priority = THR_DEFAULT_PRIORITY; thread->inherited_priority = 0; /* Initialize the mutex queue: */ TAILQ_INIT(&thread->mutexq); /* Initialize thread locking. */ if (_lock_init(&thread->lock, LCK_ADAPTIVE, _thr_lock_wait, _thr_lock_wakeup) != 0) PANIC("Cannot initialize initial thread lock"); for (i = 0; i < MAX_THR_LOCKLEVEL; i++) { _lockuser_init(&thread->lockusers[i], (void *)thread); _LCK_SET_PRIVATE2(&thread->lockusers[i], (void *)thread); } /* Initialize hooks in the thread structure: */ thread->specific = NULL; thread->cleanup = NULL; thread->flags = 0; thread->continuation = NULL; thread->state = PS_RUNNING; thread->uniqueid = 0; } static void init_private(void) { struct clockinfo clockinfo; - struct sigaction act; size_t len; int mib[2]; - int i; /* * Avoid reinitializing some things if they don't need to be, * e.g. after a fork(). */ if (init_once == 0) { /* Find the stack top */ mib[0] = CTL_KERN; mib[1] = KERN_USRSTACK; len = sizeof (_usrstack); if (sysctl(mib, 2, &_usrstack, &len, NULL, 0) == -1) PANIC("Cannot get kern.usrstack from sysctl"); /* * Create a red zone below the main stack. All other * stacks are constrained to a maximum size by the * parameters passed to mmap(), but this stack is only * limited by resource limits, so this stack needs an * explicitly mapped red zone to protect the thread stack * that is just beyond. */ if (mmap((void *)_usrstack - THR_STACK_INITIAL - _thr_guard_default, _thr_guard_default, 0, MAP_ANON, -1, 0) == MAP_FAILED) PANIC("Cannot allocate red zone for initial thread"); /* Get the kernel clockrate: */ mib[0] = CTL_KERN; mib[1] = KERN_CLOCKRATE; len = sizeof (struct clockinfo); if (sysctl(mib, 2, &clockinfo, &len, NULL, 0) == 0) _clock_res_usec = clockinfo.tick; else _clock_res_usec = CLOCK_RES_USEC; _thr_page_size = getpagesize(); _thr_guard_default = _thr_page_size; - - /* Enter a loop to get the existing signal status: */ - for (i = 1; i < NSIG; i++) { - /* Check for signals which cannot be trapped: */ - if (i == SIGKILL || i == SIGSTOP) { - } - - /* Get the signal handler details: */ - else if (__sys_sigaction(i, NULL, - &_thread_sigact[i - 1]) != 0) { - /* - * Abort this process if signal - * initialisation fails: - */ - PANIC("Cannot read signal handler info"); - } - } - /* - * Install the signal handler for SIGINFO. It isn't - * really needed, but it is nice to have for debugging - * purposes. - */ - if (__sys_sigaction(SIGINFO, &act, NULL) != 0) { - /* - * Abort this process if signal initialisation fails: - */ - PANIC("Cannot initialize signal handler"); - } - _thread_sigact[SIGINFO - 1].sa_flags = SA_SIGINFO | SA_RESTART; - init_once = 1; /* Don't do this again. */ } else { /* * Destroy the locks before creating them. We don't * know what state they are in so it is better to just * recreate them. */ _lock_destroy(&_thread_signal_lock); _lock_destroy(&_mutex_static_lock); _lock_destroy(&_rwlock_static_lock); _lock_destroy(&_keytable_lock); } /* Initialize everything else. */ TAILQ_INIT(&_thread_list); TAILQ_INIT(&_thread_gc_list); - /* Enter a loop to get the existing signal status: */ - /* Initialize the SIG_DFL dummy handler count. */ bzero(_thread_dfl_count, sizeof(_thread_dfl_count)); /* * Initialize the lock for temporary installation of signal * handlers (to support sigwait() semantics) and for the * process signal mask and pending signal sets. */ if (_lock_init(&_thread_signal_lock, LCK_ADAPTIVE, _kse_lock_wait, _kse_lock_wakeup) != 0) PANIC("Cannot initialize _thread_signal_lock"); if (_lock_init(&_mutex_static_lock, LCK_ADAPTIVE, _thr_lock_wait, _thr_lock_wakeup) != 0) PANIC("Cannot initialize mutex static init lock"); if (_lock_init(&_rwlock_static_lock, LCK_ADAPTIVE, _thr_lock_wait, _thr_lock_wakeup) != 0) PANIC("Cannot initialize rwlock static init lock"); if (_lock_init(&_keytable_lock, LCK_ADAPTIVE, _thr_lock_wait, _thr_lock_wakeup) != 0) PANIC("Cannot initialize thread specific keytable lock"); _thr_spinlock_init(); /* Clear pending signals and get the process signal mask. */ - sigemptyset(&_thr_proc_sigpending); - __sys_sigprocmask(SIG_SETMASK, NULL, &_thr_proc_sigmask); + SIGEMPTYSET(_thr_proc_sigpending); /* * _thread_list_lock and _kse_count are initialized * by _kse_init() */ } Index: head/lib/libpthread/thread/thr_kern.c =================================================================== --- head/lib/libpthread/thread/thr_kern.c (revision 116976) +++ head/lib/libpthread/thread/thr_kern.c (revision 116977) @@ -1,2210 +1,2252 @@ /* * Copyright (C) 2003 Daniel M. Eischen * Copyright (C) 2002 Jonathon Mini * 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 #include #include #include #include +#include #include #include #include #include #include #include #include #include #include "atomic_ops.h" #include "thr_private.h" #include "libc_private.h" #include "ksd.h" /*#define DEBUG_THREAD_KERN */ #ifdef DEBUG_THREAD_KERN #define DBG_MSG stdout_debug #else #define DBG_MSG(x...) #endif /* * Define a high water mark for the maximum number of threads that * will be cached. Once this level is reached, any extra threads * will be free()'d. * * XXX - It doesn't make sense to worry about the maximum number of * KSEs that we can cache because the system will limit us to * something *much* less than the maximum number of threads * that we can have. Disregarding KSEs in their own group, * the maximum number of KSEs is the number of processors in * the system. */ #define MAX_CACHED_THREADS 100 #define KSE_STACKSIZE 16384 #define KSE_SET_MBOX(kse, thrd) \ (kse)->k_mbx.km_curthread = &(thrd)->tmbx #define KSE_SET_EXITED(kse) (kse)->k_flags |= KF_EXITED /* * Macros for manipulating the run queues. The priority queue * routines use the thread's pqe link and also handle the setting * and clearing of the thread's THR_FLAGS_IN_RUNQ flag. */ #define KSE_RUNQ_INSERT_HEAD(kse, thrd) \ _pq_insert_head(&(kse)->k_schedq->sq_runq, thrd) #define KSE_RUNQ_INSERT_TAIL(kse, thrd) \ _pq_insert_tail(&(kse)->k_schedq->sq_runq, thrd) #define KSE_RUNQ_REMOVE(kse, thrd) \ _pq_remove(&(kse)->k_schedq->sq_runq, thrd) #define KSE_RUNQ_FIRST(kse) _pq_first(&(kse)->k_schedq->sq_runq) #define KSE_RUNQ_THREADS(kse) ((kse)->k_schedq->sq_runq.pq_threads) /* * We've got to keep track of everything that is allocated, not only * to have a speedy free list, but also so they can be deallocated * after a fork(). */ static TAILQ_HEAD(, kse) active_kseq; static TAILQ_HEAD(, kse) free_kseq; static TAILQ_HEAD(, kse_group) free_kse_groupq; static TAILQ_HEAD(, kse_group) active_kse_groupq; static TAILQ_HEAD(, kse_group) gc_ksegq; static struct lock kse_lock; /* also used for kseg queue */ static int free_kse_count = 0; static int free_kseg_count = 0; static TAILQ_HEAD(, pthread) free_threadq; static struct lock thread_lock; static int free_thread_count = 0; static int inited = 0; static int active_threads = 1; static int active_kse_count = 0; static int active_kseg_count = 0; static u_int64_t next_uniqueid = 1; #ifdef DEBUG_THREAD_KERN static void dump_queues(struct kse *curkse); #endif static void kse_check_completed(struct kse *kse); static void kse_check_waitq(struct kse *kse); -static void kse_check_signals(struct kse *kse); static void kse_fini(struct kse *curkse); static void kse_reinit(struct kse *kse); static void kse_sched_multi(struct kse *curkse); #ifdef NOT_YET static void kse_sched_single(struct kse *curkse); #endif static void kse_switchout_thread(struct kse *kse, struct pthread *thread); static void kse_wait(struct kse *kse, struct pthread *td_wait); static void kse_free_unlocked(struct kse *kse); static void kseg_free_unlocked(struct kse_group *kseg); static void kseg_init(struct kse_group *kseg); static void kseg_reinit(struct kse_group *kseg); static void kse_waitq_insert(struct pthread *thread); static void kse_wakeup_multi(struct kse *curkse); static void kse_wakeup_one(struct pthread *thread); static void thr_cleanup(struct kse *kse, struct pthread *curthread); static void thr_link(struct pthread *thread); -static void thr_resume_wrapper(int unused_1, siginfo_t *unused_2, - ucontext_t *ucp); +static void thr_resume_wrapper(int sig, siginfo_t *, ucontext_t *); static void thr_resume_check(struct pthread *curthread, ucontext_t *ucp, struct pthread_sigframe *psf); static int thr_timedout(struct pthread *thread, struct timespec *curtime); static void thr_unlink(struct pthread *thread); + /* * This is called after a fork(). * No locks need to be taken here since we are guaranteed to be * single threaded. + * + * XXX + * POSIX says for threaded process, fork() function is used + * only to run new programs, and the effects of calling functions + * that require certain resources between the call to fork() and + * the call to an exec function are undefined. + * + * Here it is not safe to reinitialize the library after fork(). + * Because memory management may be corrupted, further calling + * malloc()/free() may cause undefined behavior. */ void _kse_single_thread(struct pthread *curthread) { +#ifdef NOTYET struct kse *kse; struct kse_group *kseg; struct pthread *thread; kse_critical_t crit; int i; + /* * Disable upcalls and clear the threaded flag. * XXX - I don't think we need to disable upcalls after a fork(). * but it doesn't hurt. */ crit = _kse_critical_enter(); __isthreaded = 0; active_threads = 1; + _thr_signal_deinit(); /* * Enter a loop to remove and free all threads other than * the running thread from the active thread list: */ while ((thread = TAILQ_FIRST(&_thread_list)) != NULL) { THR_GCLIST_REMOVE(thread); /* * Remove this thread from the list (the current * thread will be removed but re-added by libpthread * initialization. */ TAILQ_REMOVE(&_thread_list, thread, tle); /* Make sure this isn't the running thread: */ if (thread != curthread) { _thr_stack_free(&thread->attr); if (thread->specific != NULL) free(thread->specific); for (i = 0; i < MAX_THR_LOCKLEVEL; i++) { _lockuser_destroy(&thread->lockusers[i]); } _lock_destroy(&thread->lock); free(thread); } } TAILQ_INIT(&curthread->mutexq); /* initialize mutex queue */ curthread->joiner = NULL; /* no joining threads yet */ curthread->refcount = 0; - sigemptyset(&curthread->sigpend); /* clear pending signals */ + SIGEMPTYSET(curthread->sigpend); /* clear pending signals */ if (curthread->specific != NULL) { free(curthread->specific); curthread->specific = NULL; curthread->specific_data_count = 0; } /* Free the free KSEs: */ while ((kse = TAILQ_FIRST(&free_kseq)) != NULL) { TAILQ_REMOVE(&free_kseq, kse, k_qe); for (i = 0; i < MAX_KSE_LOCKLEVEL; i++) { _lockuser_destroy(&kse->k_lockusers[i]); } _lock_destroy(&kse->k_lock); _ksd_destroy(&kse->k_ksd); if (kse->k_stack.ss_sp != NULL) free(kse->k_stack.ss_sp); free(kse); } free_kse_count = 0; /* Free the active KSEs: */ while ((kse = TAILQ_FIRST(&active_kseq)) != NULL) { TAILQ_REMOVE(&active_kseq, kse, k_qe); for (i = 0; i < MAX_KSE_LOCKLEVEL; i++) { _lockuser_destroy(&kse->k_lockusers[i]); } _lock_destroy(&kse->k_lock); if (kse->k_stack.ss_sp != NULL) free(kse->k_stack.ss_sp); free(kse); } active_kse_count = 0; /* Free the free KSEGs: */ while ((kseg = TAILQ_FIRST(&free_kse_groupq)) != NULL) { TAILQ_REMOVE(&free_kse_groupq, kseg, kg_qe); _lock_destroy(&kseg->kg_lock); _pq_free(&kseg->kg_schedq.sq_runq); free(kseg); } free_kseg_count = 0; /* Free the active KSEGs: */ while ((kseg = TAILQ_FIRST(&active_kse_groupq)) != NULL) { TAILQ_REMOVE(&active_kse_groupq, kseg, kg_qe); _lock_destroy(&kseg->kg_lock); _pq_free(&kseg->kg_schedq.sq_runq); free(kseg); } active_kseg_count = 0; /* Free the free threads. */ while ((thread = TAILQ_FIRST(&free_threadq)) != NULL) { TAILQ_REMOVE(&free_threadq, thread, tle); if (thread->specific != NULL) free(thread->specific); for (i = 0; i < MAX_THR_LOCKLEVEL; i++) { _lockuser_destroy(&thread->lockusers[i]); } _lock_destroy(&thread->lock); free(thread); } free_thread_count = 0; /* Free the to-be-gc'd threads. */ while ((thread = TAILQ_FIRST(&_thread_gc_list)) != NULL) { TAILQ_REMOVE(&_thread_gc_list, thread, gcle); for (i = 0; i < MAX_THR_LOCKLEVEL; i++) { _lockuser_destroy(&thread->lockusers[i]); } _lock_destroy(&thread->lock); free(thread); } TAILQ_INIT(&gc_ksegq); _gc_count = 0; if (inited != 0) { /* * Destroy these locks; they'll be recreated to assure they * are in the unlocked state. */ _lock_destroy(&kse_lock); _lock_destroy(&thread_lock); _lock_destroy(&_thread_list_lock); inited = 0; } /* * After a fork(), the leftover thread goes back to being * scope process. */ curthread->attr.flags &= ~PTHREAD_SCOPE_SYSTEM; curthread->attr.flags |= PTHREAD_SCOPE_PROCESS; /* * After a fork, we are still operating on the thread's original * stack. Don't clear the THR_FLAGS_USER from the thread's * attribute flags. */ /* Initialize the threads library. */ curthread->kse = NULL; curthread->kseg = NULL; _kse_initial = NULL; _libpthread_init(curthread); +#else + _ksd_readandclear_tmbx(); + __isthreaded = 0; + active_threads = 0; + _thr_signal_deinit(); +#endif } /* * This is used to initialize housekeeping and to initialize the * KSD for the KSE. */ void _kse_init(void) { if (inited == 0) { TAILQ_INIT(&active_kseq); TAILQ_INIT(&active_kse_groupq); TAILQ_INIT(&free_kseq); TAILQ_INIT(&free_kse_groupq); TAILQ_INIT(&free_threadq); TAILQ_INIT(&gc_ksegq); if (_lock_init(&kse_lock, LCK_ADAPTIVE, _kse_lock_wait, _kse_lock_wakeup) != 0) PANIC("Unable to initialize free KSE queue lock"); if (_lock_init(&thread_lock, LCK_ADAPTIVE, _kse_lock_wait, _kse_lock_wakeup) != 0) PANIC("Unable to initialize free thread queue lock"); if (_lock_init(&_thread_list_lock, LCK_ADAPTIVE, _kse_lock_wait, _kse_lock_wakeup) != 0) PANIC("Unable to initialize thread list lock"); active_kse_count = 0; active_kseg_count = 0; _gc_count = 0; inited = 1; } } int _kse_isthreaded(void) { return (__isthreaded != 0); } /* * This is called when the first thread (other than the initial * thread) is created. */ int _kse_setthreaded(int threaded) { if ((threaded != 0) && (__isthreaded == 0)) { /* * Locking functions in libc are required when there are * threads other than the initial thread. */ __isthreaded = 1; /* * Tell the kernel to create a KSE for the initial thread * and enable upcalls in it. */ + _thr_signal_init(); _kse_initial->k_flags |= KF_STARTED; if (kse_create(&_kse_initial->k_mbx, 0) != 0) { _kse_initial->k_flags &= ~KF_STARTED; __isthreaded = 0; /* may abort() */ - DBG_MSG("kse_create failed\n"); + PANIC("kse_create() failed\n"); return (-1); } KSE_SET_MBOX(_kse_initial, _thr_initial); + _thr_start_sig_daemon(); _thr_setmaxconcurrency(); } return (0); } /* * Lock wait and wakeup handlers for KSE locks. These are only used by * KSEs, and should never be used by threads. KSE locks include the * KSE group lock (used for locking the scheduling queue) and the * kse_lock defined above. * * When a KSE lock attempt blocks, the entire KSE blocks allowing another * KSE to run. For the most part, it doesn't make much sense to try and * schedule another thread because you need to lock the scheduling queue * in order to do that. And since the KSE lock is used to lock the scheduling * queue, you would just end up blocking again. */ void _kse_lock_wait(struct lock *lock, struct lockuser *lu) { struct kse *curkse = (struct kse *)_LCK_GET_PRIVATE(lu); struct timespec ts; int saved_flags; if (curkse->k_mbx.km_curthread != NULL) PANIC("kse_lock_wait does not disable upcall.\n"); /* * Enter a loop to wait until we get the lock. */ ts.tv_sec = 0; ts.tv_nsec = 1000000; /* 1 sec */ while (!_LCK_GRANTED(lu)) { /* * Yield the kse and wait to be notified when the lock * is granted. */ saved_flags = curkse->k_mbx.km_flags; curkse->k_mbx.km_flags |= KMF_NOUPCALL | KMF_NOCOMPLETED; kse_release(&ts); curkse->k_mbx.km_flags = saved_flags; } } void _kse_lock_wakeup(struct lock *lock, struct lockuser *lu) { struct kse *curkse; struct kse *kse; struct kse_mailbox *mbx; curkse = _get_curkse(); kse = (struct kse *)_LCK_GET_PRIVATE(lu); if (kse == curkse) PANIC("KSE trying to wake itself up in lock"); else { mbx = &kse->k_mbx; _lock_grant(lock, lu); /* * Notify the owning kse that it has the lock. * It is safe to pass invalid address to kse_wakeup * even if the mailbox is not in kernel at all, * and waking up a wrong kse is also harmless. */ kse_wakeup(mbx); } } /* * Thread wait and wakeup handlers for thread locks. These are only used * by threads, never by KSEs. Thread locks include the per-thread lock * (defined in its structure), and condition variable and mutex locks. */ void _thr_lock_wait(struct lock *lock, struct lockuser *lu) { struct pthread *curthread = (struct pthread *)lu->lu_private; do { THR_SCHED_LOCK(curthread, curthread); THR_SET_STATE(curthread, PS_LOCKWAIT); THR_SCHED_UNLOCK(curthread, curthread); _thr_sched_switch(curthread); } while (!_LCK_GRANTED(lu)); } void _thr_lock_wakeup(struct lock *lock, struct lockuser *lu) { struct pthread *thread; struct pthread *curthread; curthread = _get_curthread(); thread = (struct pthread *)_LCK_GET_PRIVATE(lu); THR_SCHED_LOCK(curthread, thread); _lock_grant(lock, lu); _thr_setrunnable_unlocked(thread); THR_SCHED_UNLOCK(curthread, thread); } kse_critical_t _kse_critical_enter(void) { kse_critical_t crit; crit = _ksd_readandclear_tmbx(); return (crit); } void _kse_critical_leave(kse_critical_t crit) { struct pthread *curthread; _ksd_set_tmbx(crit); if ((crit != NULL) && ((curthread = _get_curthread()) != NULL)) THR_YIELD_CHECK(curthread); } int _kse_in_critical(void) { return (_ksd_get_tmbx() == NULL); } void _thr_critical_enter(struct pthread *thread) { thread->critical_count++; } void _thr_critical_leave(struct pthread *thread) { thread->critical_count--; THR_YIELD_CHECK(thread); } void _thr_sched_switch(struct pthread *curthread) { struct kse *curkse; (void)_kse_critical_enter(); curkse = _get_curkse(); KSE_SCHED_LOCK(curkse, curkse->k_kseg); _thr_sched_switch_unlocked(curthread); } /* * XXX - We may need to take the scheduling lock before calling * this, or perhaps take the lock within here before * doing anything else. */ void _thr_sched_switch_unlocked(struct pthread *curthread) { struct pthread *td; struct pthread_sigframe psf; struct kse *curkse; int ret; volatile int uts_once; volatile int resume_once = 0; + ucontext_t uc; /* We're in the scheduler, 5 by 5: */ curkse = _get_curkse(); curthread->need_switchout = 1; /* The thread yielded on its own. */ curthread->critical_yield = 0; /* No need to yield anymore. */ curthread->slice_usec = -1; /* Restart the time slice. */ /* Thread can unlock the scheduler lock. */ curthread->lock_switch = 1; /* * The signal frame is allocated off the stack because * a thread can be interrupted by other signals while * it is running down pending signals. */ - sigemptyset(&psf.psf_sigset); + psf.psf_valid = 0; curthread->curframe = &psf; /* * Enter the scheduler if any one of the following is true: * * o The current thread is dead; it's stack needs to be * cleaned up and it can't be done while operating on * it. + * o The current thread has signals pending, should + * let scheduler install signal trampoline for us. * o There are no runnable threads. * o The next thread to run won't unlock the scheduler * lock. A side note: the current thread may be run * instead of the next thread in the run queue, but * we don't bother checking for that. */ if ((curthread->state == PS_DEAD) || (((td = KSE_RUNQ_FIRST(curkse)) == NULL) && (curthread->state != PS_RUNNING)) || - ((td != NULL) && (td->lock_switch == 0))) + ((td != NULL) && (td->lock_switch == 0))) { + curkse->k_switch = 1; _thread_enter_uts(&curthread->tmbx, &curkse->k_mbx); + } else { uts_once = 0; THR_GETCONTEXT(&curthread->tmbx.tm_context); if (uts_once == 0) { uts_once = 1; /* Switchout the current thread. */ kse_switchout_thread(curkse, curthread); /* Choose another thread to run. */ td = KSE_RUNQ_FIRST(curkse); KSE_RUNQ_REMOVE(curkse, td); curkse->k_curthread = td; /* * Make sure the current thread's kse points to * this kse. */ td->kse = curkse; /* * Reset accounting. */ td->tmbx.tm_uticks = 0; td->tmbx.tm_sticks = 0; /* * Reset the time slice if this thread is running * for the first time or running again after using * its full time slice allocation. */ if (td->slice_usec == -1) td->slice_usec = 0; /* Mark the thread active. */ td->active = 1; /* Remove the frame reference. */ td->curframe = NULL; /* * Continue the thread at its current frame: */ ret = _thread_switch(&td->tmbx, NULL); /* This point should not be reached. */ if (ret != 0) PANIC("Bad return from _thread_switch"); PANIC("Thread has returned from _thread_switch"); } } + if (psf.psf_valid) { + /* + * It is ugly we must increase critical count, because we + * have a frame saved, we must backout state in psf + * before we can process signals. + */ + curthread->critical_count++; + } + if (curthread->lock_switch != 0) { /* * Unlock the scheduling queue and leave the * critical region. */ /* Don't trust this after a switch! */ curkse = _get_curkse(); curthread->lock_switch = 0; KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); _kse_critical_leave(&curthread->tmbx); } /* * This thread is being resumed; check for cancellations. */ - if ((resume_once == 0) && (!THR_IN_CRITICAL(curthread))) { - resume_once = 1; - thr_resume_check(curthread, &curthread->tmbx.tm_context, &psf); + if ((psf.psf_valid || curthread->check_pending)) { + resume_once = 0; + THR_GETCONTEXT(&uc); + if (resume_once == 0) { + resume_once = 1; + curthread->check_pending = 0; + thr_resume_check(curthread, &uc, &psf); + } } - THR_ACTIVATE_LAST_LOCK(curthread); } /* * This is the scheduler for a KSE which runs a scope system thread. * The multi-thread KSE scheduler should also work for a single threaded * KSE, but we use a separate scheduler so that it can be fine-tuned * to be more efficient (and perhaps not need a separate stack for * the KSE, allowing it to use the thread's stack). * * XXX - This probably needs some work. */ #ifdef NOT_YET static void kse_sched_single(struct kse *curkse) { struct pthread *curthread = curkse->k_curthread; struct pthread *td_wait; struct timespec ts; int level; if (curthread->active == 0) { if (curthread->state != PS_RUNNING) { /* Check to see if the thread has timed out. */ KSE_GET_TOD(curkse, &ts); if (thr_timedout(curthread, &ts) != 0) { curthread->timeout = 1; curthread->state = PS_RUNNING; } } } /* This thread no longer needs to yield the CPU: */ curthread->critical_yield = 0; curthread->need_switchout = 0; /* * Lock the scheduling queue. * * There is no scheduling queue for single threaded KSEs, * but we need a lock for protection regardless. */ KSE_SCHED_LOCK(curkse, curkse->k_kseg); /* * This has to do the job of kse_switchout_thread(), only * for a single threaded KSE/KSEG. */ switch (curthread->state) { case PS_DEAD: /* Unlock the scheduling queue and exit the KSE and thread. */ thr_cleaup(curkse, curthread); KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); break; case PS_COND_WAIT: case PS_SLEEP_WAIT: /* Only insert threads that can timeout: */ if (curthread->wakeup_time.tv_sec != -1) { /* Insert into the waiting queue: */ KSE_WAITQ_INSERT(curkse, curthread); } break; case PS_LOCKWAIT: level = curthread->locklevel - 1; if (!_LCK_GRANTED(&curthread->lockusers[level])) KSE_WAITQ_INSERT(curkse, curthread); else THR_SET_STATE(curthread, PS_RUNNING); break; case PS_JOIN: case PS_MUTEX_WAIT: case PS_RUNNING: case PS_SIGSUSPEND: case PS_SIGWAIT: case PS_SUSPENDED: case PS_DEADLOCK: default: /* * These states don't timeout and don't need * to be in the waiting queue. */ break; } while (curthread->state != PS_RUNNING) { curthread->active = 0; td_wait = KSE_WAITQ_FIRST(curkse); kse_wait(curkse, td_wait); if (td_wait != NULL) { KSE_GET_TOD(curkse, &ts); if (thr_timedout(curthread, &ts)) { /* Indicate the thread timedout: */ td_wait->timeout = 1; /* Make the thread runnable. */ THR_SET_STATE(td_wait, PS_RUNNING); KSE_WAITQ_REMOVE(curkse, td_wait); } } - KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); - kse_check_signals(curkse); - KSE_SCHED_LOCK(curkse, curkse->k_kseg); } /* Remove the frame reference. */ curthread->curframe = NULL; /* Unlock the scheduling queue. */ KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); /* * Continue the thread at its current frame: */ DBG_MSG("Continuing bound thread %p\n", curthread); _thread_switch(&curthread->tmbx, &curkse->k_mbx.km_curthread); PANIC("Thread has returned from _thread_switch"); } #endif #ifdef DEBUG_THREAD_KERN static void dump_queues(struct kse *curkse) { struct pthread *thread; DBG_MSG("Threads in waiting queue:\n"); TAILQ_FOREACH(thread, &curkse->k_kseg->kg_schedq.sq_waitq, pqe) { DBG_MSG(" thread %p, state %d, blocked %d\n", thread, thread->state, thread->blocked); } } #endif /* * This is the scheduler for a KSE which runs multiple threads. */ static void kse_sched_multi(struct kse *curkse) { struct pthread *curthread, *td_wait; struct pthread_sigframe *curframe; int ret; THR_ASSERT(curkse->k_mbx.km_curthread == NULL, "Mailbox not null in kse_sched_multi"); /* Check for first time initialization: */ if ((curkse->k_flags & KF_INITIALIZED) == 0) { /* Setup this KSEs specific data. */ _ksd_setprivate(&curkse->k_ksd); _set_curkse(curkse); /* Set this before grabbing the context. */ curkse->k_flags |= KF_INITIALIZED; } /* This may have returned from a kse_release(). */ if (KSE_WAITING(curkse)) { DBG_MSG("Entered upcall when KSE is waiting."); KSE_CLEAR_WAIT(curkse); } - /* Lock the scheduling lock. */ - curthread = curkse->k_curthread; - if ((curthread == NULL) || (curthread->need_switchout == 0)) { - /* This is an upcall; take the scheduler lock. */ + /*If this is an upcall; take the scheduler lock. */ + if (curkse->k_switch == 0) KSE_SCHED_LOCK(curkse, curkse->k_kseg); - } + curkse->k_switch = 0; + curthread = curkse->k_curthread; + if (KSE_IS_IDLE(curkse)) { KSE_CLEAR_IDLE(curkse); curkse->k_kseg->kg_idle_kses--; } /* * If the current thread was completed in another KSE, then * it will be in the run queue. Don't mark it as being blocked. */ if ((curthread != NULL) && ((curthread->flags & THR_FLAGS_IN_RUNQ) == 0) && (curthread->need_switchout == 0)) { /* * Assume the current thread is blocked; when the * completed threads are checked and if the current * thread is among the completed, the blocked flag * will be cleared. */ curthread->blocked = 1; } /* Check for any unblocked threads in the kernel. */ kse_check_completed(curkse); /* * Check for threads that have timed-out. */ kse_check_waitq(curkse); /* * Switchout the current thread, if necessary, as the last step * so that it is inserted into the run queue (if it's runnable) * _after_ any other threads that were added to it above. */ if (curthread == NULL) ; /* Nothing to do here. */ else if ((curthread->need_switchout == 0) && (curthread->blocked == 0) && (THR_IN_CRITICAL(curthread))) { /* * Resume the thread and tell it to yield when * it leaves the critical region. */ curthread->critical_yield = 1; curthread->active = 1; if ((curthread->flags & THR_FLAGS_IN_RUNQ) != 0) KSE_RUNQ_REMOVE(curkse, curthread); curkse->k_curthread = curthread; curthread->kse = curkse; DBG_MSG("Continuing thread %p in critical region\n", curthread); kse_wakeup_multi(curkse); KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); ret = _thread_switch(&curthread->tmbx, &curkse->k_mbx.km_curthread); if (ret != 0) PANIC("Can't resume thread in critical region\n"); } else if ((curthread->flags & THR_FLAGS_IN_RUNQ) == 0) kse_switchout_thread(curkse, curthread); curkse->k_curthread = NULL; kse_wakeup_multi(curkse); - /* This has to be done without the scheduling lock held. */ - KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); - kse_check_signals(curkse); - KSE_SCHED_LOCK(curkse, curkse->k_kseg); - #ifdef DEBUG_THREAD_KERN dump_queues(curkse); #endif /* Check if there are no threads ready to run: */ while (((curthread = KSE_RUNQ_FIRST(curkse)) == NULL) && (curkse->k_kseg->kg_threadcount != 0)) { /* * Wait for a thread to become active or until there are * no more threads. */ td_wait = KSE_WAITQ_FIRST(curkse); kse_wait(curkse, td_wait); kse_check_completed(curkse); kse_check_waitq(curkse); - KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); - kse_check_signals(curkse); - KSE_SCHED_LOCK(curkse, curkse->k_kseg); } /* Check for no more threads: */ if (curkse->k_kseg->kg_threadcount == 0) { /* * Normally this shouldn't return, but it will if there * are other KSEs running that create new threads that * are assigned to this KSE[G]. For instance, if a scope * system thread were to create a scope process thread * and this kse[g] is the initial kse[g], then that newly * created thread would be assigned to us (the initial * kse[g]). */ KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); kse_fini(curkse); /* never returns */ } THR_ASSERT(curthread != NULL, "Return from kse_wait/fini without thread."); THR_ASSERT(curthread->state != PS_DEAD, "Trying to resume dead thread!"); KSE_RUNQ_REMOVE(curkse, curthread); /* * Make the selected thread the current thread. */ curkse->k_curthread = curthread; /* * Make sure the current thread's kse points to this kse. */ curthread->kse = curkse; /* * Reset accounting. */ curthread->tmbx.tm_uticks = 0; curthread->tmbx.tm_sticks = 0; /* * Reset the time slice if this thread is running for the first * time or running again after using its full time slice allocation. */ if (curthread->slice_usec == -1) curthread->slice_usec = 0; /* Mark the thread active. */ curthread->active = 1; /* Remove the frame reference. */ curframe = curthread->curframe; curthread->curframe = NULL; kse_wakeup_multi(curkse); /* * The thread's current signal frame will only be NULL if it * is being resumed after being blocked in the kernel. In * this case, and if the thread needs to run down pending * signals or needs a cancellation check, we need to add a * signal frame to the thread's context. */ #ifdef NOT_YET - if ((curframe == NULL) && ((curthread->have_signals != 0) || + if ((((curframe == NULL) && (curthread->check_pending != 0)) || (((curthread->cancelflags & THR_AT_CANCEL_POINT) == 0) && - ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0)))) + ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0))) && + !THR_IN_CRITICAL(curthread)) signalcontext(&curthread->tmbx.tm_context, 0, (__sighandler_t *)thr_resume_wrapper); #else - if ((curframe == NULL) && (curthread->have_signals != 0)) + if ((curframe == NULL) && (curthread->check_pending != 0) && + !THR_IN_CRITICAL(curthread)) { + curthread->check_pending = 0; signalcontext(&curthread->tmbx.tm_context, 0, (__sighandler_t *)thr_resume_wrapper); + } #endif /* * Continue the thread at its current frame: */ if (curthread->lock_switch != 0) { /* * This thread came from a scheduler switch; it will * unlock the scheduler lock and set the mailbox. */ ret = _thread_switch(&curthread->tmbx, NULL); } else { /* This thread won't unlock the scheduler lock. */ KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); ret = _thread_switch(&curthread->tmbx, &curkse->k_mbx.km_curthread); } if (ret != 0) PANIC("Thread has returned from _thread_switch"); /* This point should not be reached. */ PANIC("Thread has returned from _thread_switch"); } static void -kse_check_signals(struct kse *curkse) +thr_resume_wrapper(int sig, siginfo_t *siginfo, ucontext_t *ucp) { - sigset_t sigset; - int i; - - /* Deliver posted signals. */ - for (i = 0; i < _SIG_WORDS; i++) { - atomic_swap_int(&curkse->k_mbx.km_sigscaught.__bits[i], - 0, &sigset.__bits[i]); - } - if (SIGNOTEMPTY(sigset)) { - /* - * Dispatch each signal. - * - * XXX - There is no siginfo for any of these. - * I think there should be, especially for - * signals from other processes (si_pid, si_uid). - */ - for (i = 1; i < NSIG; i++) { - if (sigismember(&sigset, i) != 0) { - DBG_MSG("Dispatching signal %d\n", i); - _thr_sig_dispatch(curkse, i, - NULL /* no siginfo */); - } - } - sigemptyset(&sigset); - __sys_sigprocmask(SIG_SETMASK, &sigset, NULL); - } -} - -static void -thr_resume_wrapper(int unused_1, siginfo_t *unused_2, ucontext_t *ucp) -{ struct pthread *curthread = _get_curthread(); + struct kse *curkse; + int ret; + DBG_MSG(">>> sig wrapper\n"); + if (curthread->lock_switch) + PANIC("thr_resume_wrapper, lock_switch != 0\n"); thr_resume_check(curthread, ucp, NULL); + _kse_critical_enter(); + curkse = _get_curkse(); + curthread->tmbx.tm_context = *ucp; + ret = _thread_switch(&curthread->tmbx, &curkse->k_mbx.km_curthread); + if (ret != 0) + PANIC("thr_resume_wrapper: thread has returned " + "from _thread_switch"); + /* THR_SETCONTEXT(ucp); */ /* not work, why ? */ } static void thr_resume_check(struct pthread *curthread, ucontext_t *ucp, struct pthread_sigframe *psf) { - /* Check signals before cancellations. */ - while (curthread->have_signals != 0) { - /* Clear the pending flag. */ - curthread->have_signals = 0; + _thr_sig_rundown(curthread, ucp, psf); - /* - * It's perfectly valid, though not portable, for - * signal handlers to munge their interrupted context - * and expect to return to it. Ensure we use the - * correct context when running down signals. - */ - _thr_sig_rundown(curthread, ucp, psf); - } - #ifdef NOT_YET if (((curthread->cancelflags & THR_AT_CANCEL_POINT) == 0) && ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0)) pthread_testcancel(); #endif } /* * Clean up a thread. This must be called with the thread's KSE * scheduling lock held. The thread must be a thread from the * KSE's group. */ static void thr_cleanup(struct kse *curkse, struct pthread *thread) { struct pthread *joiner; int sys_scope; if ((joiner = thread->joiner) != NULL) { /* Joinee scheduler lock held; joiner won't leave. */ if (joiner->kseg == curkse->k_kseg) { if (joiner->join_status.thread == thread) { joiner->join_status.thread = NULL; joiner->join_status.ret = thread->ret; _thr_setrunnable_unlocked(joiner); } } else { KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); /* The joiner may have removed itself and exited. */ if (_thr_ref_add(thread, joiner, 0) == 0) { KSE_SCHED_LOCK(curkse, joiner->kseg); if (joiner->join_status.thread == thread) { joiner->join_status.thread = NULL; joiner->join_status.ret = thread->ret; _thr_setrunnable_unlocked(joiner); } KSE_SCHED_UNLOCK(curkse, joiner->kseg); _thr_ref_delete(thread, joiner); } KSE_SCHED_LOCK(curkse, curkse->k_kseg); } thread->attr.flags |= PTHREAD_DETACHED; } if (!(sys_scope = (thread->attr.flags & PTHREAD_SCOPE_SYSTEM))) { /* * Remove the thread from the KSEG's list of threads. */ KSEG_THRQ_REMOVE(thread->kseg, thread); /* * Migrate the thread to the main KSE so that this * KSE and KSEG can be cleaned when their last thread * exits. */ thread->kseg = _kse_initial->k_kseg; thread->kse = _kse_initial; } thread->flags |= THR_FLAGS_GC_SAFE; /* * We can't hold the thread list lock while holding the * scheduler lock. */ KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); DBG_MSG("Adding thread %p to GC list\n", thread); KSE_LOCK_ACQUIRE(curkse, &_thread_list_lock); THR_GCLIST_ADD(thread); /* Use thread_list_lock */ active_threads--; - if (active_threads == 0) { + if (active_threads == 1) { KSE_LOCK_RELEASE(curkse, &_thread_list_lock); exit(0); } KSE_LOCK_RELEASE(curkse, &_thread_list_lock); if (sys_scope) { /* * System scope thread is single thread group, * when thread is exited, its kse and ksegrp should * be recycled as well. */ kse_exit(); PANIC("kse_exit() failed for system scope thread"); } KSE_SCHED_LOCK(curkse, curkse->k_kseg); } void _thr_gc(struct pthread *curthread) { struct pthread *td, *td_next; kse_critical_t crit; TAILQ_HEAD(, pthread) worklist; TAILQ_INIT(&worklist); crit = _kse_critical_enter(); KSE_LOCK_ACQUIRE(curthread->kse, &_thread_list_lock); /* Check the threads waiting for GC. */ for (td = TAILQ_FIRST(&_thread_gc_list); td != NULL; td = td_next) { td_next = TAILQ_NEXT(td, gcle); if ((td->flags & THR_FLAGS_GC_SAFE) == 0) continue; else if (((td->attr.flags & PTHREAD_SCOPE_SYSTEM) != 0) && ((td->kse->k_mbx.km_flags & KMF_DONE) == 0)) { /* * The thread and KSE are operating on the same * stack. Wait for the KSE to exit before freeing * the thread's stack as well as everything else. */ continue; } /* * Remove the thread from the GC list. If the thread * isn't yet detached, it will get added back to the * GC list at a later time. */ THR_GCLIST_REMOVE(td); DBG_MSG("Freeing thread %p stack\n", td); /* * We can free the thread stack since it's no longer * in use. */ _thr_stack_free(&td->attr); if (((td->attr.flags & PTHREAD_DETACHED) != 0) && (td->refcount == 0)) { /* * The thread has detached and is no longer * referenced. It is safe to remove all * remnants of the thread. */ THR_LIST_REMOVE(td); TAILQ_INSERT_HEAD(&worklist, td, gcle); } } KSE_LOCK_RELEASE(curthread->kse, &_thread_list_lock); _kse_critical_leave(crit); while ((td = TAILQ_FIRST(&worklist)) != NULL) { TAILQ_REMOVE(&worklist, td, gcle); if ((td->attr.flags & PTHREAD_SCOPE_SYSTEM) != 0) { crit = _kse_critical_enter(); KSE_LOCK_ACQUIRE(curthread->kse, &kse_lock); kse_free_unlocked(td->kse); kseg_free_unlocked(td->kseg); KSE_LOCK_RELEASE(curthread->kse, &kse_lock); _kse_critical_leave(crit); } - DBG_MSG("Freeing thread %p\n", td); - _thr_free(curthread, td); + /* + * XXX we don't free initial thread, because there might + * have some code referencing initial thread. + */ + if (td != _thr_initial) { + DBG_MSG("Freeing thread %p\n", td); + _thr_free(curthread, td); + } else + DBG_MSG("Initial thread won't be freed\n"); } /* XXX free kse and ksegrp list should be looked as well */ } /* * Only new threads that are running or suspended may be scheduled. */ int _thr_schedule_add(struct pthread *curthread, struct pthread *newthread) { - struct kse *curkse; kse_critical_t crit; int ret; /* Add the new thread. */ thr_link(newthread); /* * If this is the first time creating a thread, make sure * the mailbox is set for the current thread. */ if ((newthread->attr.flags & PTHREAD_SCOPE_SYSTEM) != 0) { #ifdef NOT_YET /* We use the thread's stack as the KSE's stack. */ new_thread->kse->k_mbx.km_stack.ss_sp = new_thread->attr.stackaddr_attr; new_thread->kse->k_mbx.km_stack.ss_size = new_thread->attr.stacksize_attr; #endif /* * No need to lock the scheduling queue since the * KSE/KSEG pair have not yet been started. */ KSEG_THRQ_ADD(newthread->kseg, newthread); if (newthread->state == PS_RUNNING) THR_RUNQ_INSERT_TAIL(newthread); newthread->kse->k_curthread = NULL; newthread->kse->k_mbx.km_flags = 0; newthread->kse->k_mbx.km_func = (kse_func_t *)kse_sched_multi; newthread->kse->k_mbx.km_quantum = 0; /* * This thread needs a new KSE and KSEG. */ - crit = _kse_critical_enter(); - curkse = _get_curkse(); - _ksd_setprivate(&newthread->kse->k_ksd); - newthread->kse->k_flags |= KF_INITIALIZED|KF_STARTED; + newthread->kse->k_flags &= ~KF_INITIALIZED; + newthread->kse->k_flags |= KF_STARTED; ret = kse_create(&newthread->kse->k_mbx, 1); if (ret != 0) ret = errno; - _ksd_setprivate(&curkse->k_ksd); - _kse_critical_leave(crit); } else { /* * Lock the KSE and add the new thread to its list of * assigned threads. If the new thread is runnable, also * add it to the KSE's run queue. */ + crit = _kse_critical_enter(); KSE_SCHED_LOCK(curthread->kse, newthread->kseg); KSEG_THRQ_ADD(newthread->kseg, newthread); if (newthread->state == PS_RUNNING) THR_RUNQ_INSERT_TAIL(newthread); if ((newthread->kse->k_flags & KF_STARTED) == 0) { /* * This KSE hasn't been started yet. Start it * outside of holding the lock. */ newthread->kse->k_flags |= KF_STARTED; newthread->kse->k_mbx.km_func = (kse_func_t *)kse_sched_multi; newthread->kse->k_mbx.km_flags = 0; kse_create(&newthread->kse->k_mbx, 0); } else if ((newthread->state == PS_RUNNING) && KSE_IS_IDLE(newthread->kse)) { /* * The thread is being scheduled on another KSEG. */ kse_wakeup_one(newthread); } KSE_SCHED_UNLOCK(curthread->kse, newthread->kseg); + _kse_critical_leave(crit); ret = 0; } if (ret != 0) thr_unlink(newthread); return (ret); } void kse_waitq_insert(struct pthread *thread) { struct pthread *td; if (thread->wakeup_time.tv_sec == -1) TAILQ_INSERT_TAIL(&thread->kse->k_schedq->sq_waitq, thread, pqe); else { td = TAILQ_FIRST(&thread->kse->k_schedq->sq_waitq); while ((td != NULL) && (td->wakeup_time.tv_sec != -1) && ((td->wakeup_time.tv_sec < thread->wakeup_time.tv_sec) || ((td->wakeup_time.tv_sec == thread->wakeup_time.tv_sec) && (td->wakeup_time.tv_nsec <= thread->wakeup_time.tv_nsec)))) td = TAILQ_NEXT(td, pqe); if (td == NULL) TAILQ_INSERT_TAIL(&thread->kse->k_schedq->sq_waitq, thread, pqe); else TAILQ_INSERT_BEFORE(td, thread, pqe); } thread->flags |= THR_FLAGS_IN_WAITQ; } /* * This must be called with the scheduling lock held. */ static void kse_check_completed(struct kse *kse) { struct pthread *thread; struct kse_thr_mailbox *completed; + int sig; if ((completed = kse->k_mbx.km_completed) != NULL) { kse->k_mbx.km_completed = NULL; while (completed != NULL) { thread = completed->tm_udata; DBG_MSG("Found completed thread %p, name %s\n", thread, (thread->name == NULL) ? "none" : thread->name); thread->blocked = 0; if (thread != kse->k_curthread) { if ((thread->flags & THR_FLAGS_SUSPENDED) != 0) THR_SET_STATE(thread, PS_SUSPENDED); else KSE_RUNQ_INSERT_TAIL(kse, thread); if ((thread->kse != kse) && (thread->kse->k_curthread == thread)) { thread->kse->k_curthread = NULL; thread->active = 0; } } + if ((sig = thread->tmbx.tm_syncsig.si_signo) != 0) { + if (SIGISMEMBER(thread->sigmask, sig)) + SIGADDSET(thread->sigpend, sig); + else + _thr_sig_add(thread, sig, &thread->tmbx.tm_syncsig); + thread->tmbx.tm_syncsig.si_signo = 0; + } completed = completed->tm_next; } } } /* * This must be called with the scheduling lock held. */ static void kse_check_waitq(struct kse *kse) { struct pthread *pthread; struct timespec ts; KSE_GET_TOD(kse, &ts); /* * Wake up threads that have timedout. This has to be * done before adding the current thread to the run queue * so that a CPU intensive thread doesn't get preference * over waiting threads. */ while (((pthread = KSE_WAITQ_FIRST(kse)) != NULL) && thr_timedout(pthread, &ts)) { /* Remove the thread from the wait queue: */ KSE_WAITQ_REMOVE(kse, pthread); DBG_MSG("Found timedout thread %p in waitq\n", pthread); /* Indicate the thread timedout: */ pthread->timeout = 1; /* Add the thread to the priority queue: */ if ((pthread->flags & THR_FLAGS_SUSPENDED) != 0) THR_SET_STATE(pthread, PS_SUSPENDED); else { THR_SET_STATE(pthread, PS_RUNNING); KSE_RUNQ_INSERT_TAIL(kse, pthread); } } } static int thr_timedout(struct pthread *thread, struct timespec *curtime) { if (thread->wakeup_time.tv_sec < 0) return (0); else if (thread->wakeup_time.tv_sec > curtime->tv_sec) return (0); else if ((thread->wakeup_time.tv_sec == curtime->tv_sec) && (thread->wakeup_time.tv_nsec > curtime->tv_nsec)) return (0); else return (1); } /* * This must be called with the scheduling lock held. * * Each thread has a time slice, a wakeup time (used when it wants * to wait for a specified amount of time), a run state, and an * active flag. * * When a thread gets run by the scheduler, the active flag is * set to non-zero (1). When a thread performs an explicit yield * or schedules a state change, it enters the scheduler and the * active flag is cleared. When the active flag is still seen * set in the scheduler, that means that the thread is blocked in * the kernel (because it is cleared before entering the scheduler * in all other instances). * * The wakeup time is only set for those states that can timeout. * It is set to (-1, -1) for all other instances. * * The thread's run state, aside from being useful when debugging, * is used to place the thread in an appropriate queue. There * are 2 basic queues: * * o run queue - queue ordered by priority for all threads * that are runnable * o waiting queue - queue sorted by wakeup time for all threads * that are not otherwise runnable (not blocked * in kernel, not waiting for locks) * * The thread's time slice is used for round-robin scheduling * (the default scheduling policy). While a SCHED_RR thread * is runnable it's time slice accumulates. When it reaches * the time slice interval, it gets reset and added to the end * of the queue of threads at its priority. When a thread no * longer becomes runnable (blocks in kernel, waits, etc), its * time slice is reset. * * The job of kse_switchout_thread() is to handle all of the above. */ static void kse_switchout_thread(struct kse *kse, struct pthread *thread) { int level; int i; + int restart; + siginfo_t siginfo; /* * Place the currently running thread into the * appropriate queue(s). */ DBG_MSG("Switching out thread %p, state %d\n", thread, thread->state); THR_DEACTIVATE_LAST_LOCK(thread); if (thread->blocked != 0) { thread->active = 0; thread->need_switchout = 0; /* This thread must have blocked in the kernel. */ /* thread->slice_usec = -1;*/ /* restart timeslice */ - /* - * XXX - Check for pending signals for this thread to - * see if we need to interrupt it in the kernel. - */ - /* if (thread->check_pending != 0) */ if ((thread->slice_usec != -1) && (thread->attr.sched_policy != SCHED_FIFO)) thread->slice_usec += (thread->tmbx.tm_uticks + thread->tmbx.tm_sticks) * _clock_res_usec; + /* + * Check for pending signals for this thread to + * see if we need to interrupt it in the kernel. + */ + if (thread->check_pending != 0) { + for (i = 1; i <= _SIG_MAXSIG; ++i) { + if (SIGISMEMBER(thread->sigpend, i) && + !SIGISMEMBER(thread->sigmask, i)) { + restart = _thread_sigact[1 - 1].sa_flags & SA_RESTART; + kse_thr_interrupt(&thread->tmbx, + restart ? -2 : -1); + break; + } + } + } } else { switch (thread->state) { case PS_DEAD: /* * The scheduler is operating on a different * stack. It is safe to do garbage collecting * here. */ thread->active = 0; thread->need_switchout = 0; thr_cleanup(kse, thread); return; break; case PS_RUNNING: if ((thread->flags & THR_FLAGS_SUSPENDED) != 0) THR_SET_STATE(thread, PS_SUSPENDED); break; case PS_COND_WAIT: case PS_SLEEP_WAIT: /* Insert into the waiting queue: */ KSE_WAITQ_INSERT(kse, thread); break; case PS_LOCKWAIT: /* * This state doesn't timeout. */ thread->wakeup_time.tv_sec = -1; thread->wakeup_time.tv_nsec = -1; level = thread->locklevel - 1; if (!_LCK_GRANTED(&thread->lockusers[level])) KSE_WAITQ_INSERT(kse, thread); else THR_SET_STATE(thread, PS_RUNNING); break; + case PS_SIGWAIT: + KSE_WAITQ_INSERT(kse, thread); + break; case PS_JOIN: case PS_MUTEX_WAIT: case PS_SIGSUSPEND: - case PS_SIGWAIT: case PS_SUSPENDED: case PS_DEADLOCK: default: /* * These states don't timeout. */ thread->wakeup_time.tv_sec = -1; thread->wakeup_time.tv_nsec = -1; /* Insert into the waiting queue: */ KSE_WAITQ_INSERT(kse, thread); break; } if (thread->state != PS_RUNNING) { /* Restart the time slice: */ thread->slice_usec = -1; } else { if (thread->need_switchout != 0) /* * The thread yielded on its own; * restart the timeslice. */ thread->slice_usec = -1; else if ((thread->slice_usec != -1) && (thread->attr.sched_policy != SCHED_FIFO)) { thread->slice_usec += (thread->tmbx.tm_uticks + thread->tmbx.tm_sticks) * _clock_res_usec; /* Check for time quantum exceeded: */ if (thread->slice_usec > TIMESLICE_USEC) thread->slice_usec = -1; } if (thread->slice_usec == -1) { /* * The thread exceeded its time quantum or * it yielded the CPU; place it at the tail * of the queue for its priority. */ KSE_RUNQ_INSERT_TAIL(kse, thread); } else { /* * The thread hasn't exceeded its interval * Place it at the head of the queue for its * priority. */ KSE_RUNQ_INSERT_HEAD(kse, thread); } } } thread->active = 0; thread->need_switchout = 0; if (thread->check_pending != 0) { /* Install pending signals into the frame. */ thread->check_pending = 0; - for (i = 0; i < _SIG_MAXSIG; i++) { - if (sigismember(&thread->sigpend, i) && - !sigismember(&thread->tmbx.tm_context.uc_sigmask, i)) + KSE_LOCK_ACQUIRE(kse, &_thread_signal_lock); + for (i = 1; i <= _SIG_MAXSIG; i++) { + if (SIGISMEMBER(thread->sigmask, i)) + continue; + if (SIGISMEMBER(thread->sigpend, i)) _thr_sig_add(thread, i, &thread->siginfo[i]); + else if (SIGISMEMBER(_thr_proc_sigpending, i) && + _thr_getprocsig_unlocked(i, &siginfo)) { + _thr_sig_add(thread, i, &siginfo); + } } + KSE_LOCK_RELEASE(kse, &_thread_signal_lock); } } /* * This function waits for the smallest timeout value of any waiting * thread, or until it receives a message from another KSE. * * This must be called with the scheduling lock held. */ static void kse_wait(struct kse *kse, struct pthread *td_wait) { struct timespec ts, ts_sleep; int saved_flags; KSE_GET_TOD(kse, &ts); if ((td_wait == NULL) || (td_wait->wakeup_time.tv_sec < 0)) { /* Limit sleep to no more than 1 minute. */ ts_sleep.tv_sec = 60; ts_sleep.tv_nsec = 0; } else { TIMESPEC_SUB(&ts_sleep, &td_wait->wakeup_time, &ts); if (ts_sleep.tv_sec > 60) { ts_sleep.tv_sec = 60; ts_sleep.tv_nsec = 0; } } /* Don't sleep for negative times. */ if ((ts_sleep.tv_sec >= 0) && (ts_sleep.tv_nsec >= 0)) { KSE_SET_IDLE(kse); kse->k_kseg->kg_idle_kses++; KSE_SCHED_UNLOCK(kse, kse->k_kseg); saved_flags = kse->k_mbx.km_flags; kse->k_mbx.km_flags |= KMF_NOUPCALL; kse_release(&ts_sleep); kse->k_mbx.km_flags = saved_flags; KSE_SCHED_LOCK(kse, kse->k_kseg); if (KSE_IS_IDLE(kse)) { KSE_CLEAR_IDLE(kse); kse->k_kseg->kg_idle_kses--; } } } /* * Avoid calling this kse_exit() so as not to confuse it with the * system call of the same name. */ static void kse_fini(struct kse *kse) { /* struct kse_group *free_kseg = NULL; */ struct timespec ts; /* * Check to see if this is one of the main kses. */ if (kse->k_kseg != _kse_initial->k_kseg) { PANIC("shouldn't get here"); /* This is for supporting thread groups. */ #ifdef NOT_YET /* Remove this KSE from the KSEG's list of KSEs. */ KSE_SCHED_LOCK(kse, kse->k_kseg); TAILQ_REMOVE(&kse->k_kseg->kg_kseq, kse, k_kgqe); kse->k_kseg->kg_ksecount--; if (TAILQ_EMPTY(&kse->k_kseg->kg_kseq)) free_kseg = kse->k_kseg; KSE_SCHED_UNLOCK(kse, kse->k_kseg); /* * Add this KSE to the list of free KSEs along with * the KSEG if is now orphaned. */ KSE_LOCK_ACQUIRE(kse, &kse_lock); if (free_kseg != NULL) kseg_free_unlocked(free_kseg); kse_free_unlocked(kse); KSE_LOCK_RELEASE(kse, &kse_lock); kse_exit(); /* Never returns. */ PANIC("kse_exit()"); #endif } else { #ifdef NOT_YET /* * In future, we might allow program to kill * kse in initial group. */ if (kse != _kse_initial) { KSE_SCHED_LOCK(kse, kse->k_kseg); TAILQ_REMOVE(&kse->k_kseg->kg_kseq, kse, k_kgqe); kse->k_kseg->kg_ksecount--; KSE_SCHED_UNLOCK(kse, kse->k_kseg); KSE_LOCK_ACQUIRE(kse, &kse_lock); kse_free_unlocked(kse); KSE_LOCK_RELEASE(kse, &kse_lock); kse_exit(); /* Never returns. */ PANIC("kse_exit() failed for initial kseg"); } #endif KSE_SCHED_LOCK(kse, kse->k_kseg); KSE_SET_IDLE(kse); kse->k_kseg->kg_idle_kses++; KSE_SCHED_UNLOCK(kse, kse->k_kseg); ts.tv_sec = 120; ts.tv_nsec = 0; kse->k_mbx.km_flags = 0; kse_release(&ts); /* Never reach */ } } void _thr_set_timeout(const struct timespec *timeout) { struct pthread *curthread = _get_curthread(); struct timespec ts; /* Reset the timeout flag for the running thread: */ curthread->timeout = 0; /* Check if the thread is to wait forever: */ if (timeout == NULL) { /* * Set the wakeup time to something that can be recognised as * different to an actual time of day: */ curthread->wakeup_time.tv_sec = -1; curthread->wakeup_time.tv_nsec = -1; } /* Check if no waiting is required: */ else if ((timeout->tv_sec == 0) && (timeout->tv_nsec == 0)) { /* Set the wake up time to 'immediately': */ curthread->wakeup_time.tv_sec = 0; curthread->wakeup_time.tv_nsec = 0; } else { /* Calculate the time for the current thread to wakeup: */ KSE_GET_TOD(curthread->kse, &ts); TIMESPEC_ADD(&curthread->wakeup_time, &ts, timeout); } } void _thr_panic_exit(char *file, int line, char *msg) { char buf[256]; snprintf(buf, sizeof(buf), "(%s:%d) %s\n", file, line, msg); __sys_write(2, buf, strlen(buf)); abort(); } void _thr_setrunnable(struct pthread *curthread, struct pthread *thread) { kse_critical_t crit; crit = _kse_critical_enter(); KSE_SCHED_LOCK(curthread->kse, thread->kseg); _thr_setrunnable_unlocked(thread); KSE_SCHED_UNLOCK(curthread->kse, thread->kseg); _kse_critical_leave(crit); } void _thr_setrunnable_unlocked(struct pthread *thread) { if ((thread->kseg->kg_flags & KGF_SINGLE_THREAD) != 0) { /* No silly queues for these threads. */ if ((thread->flags & THR_FLAGS_SUSPENDED) != 0) THR_SET_STATE(thread, PS_SUSPENDED); else THR_SET_STATE(thread, PS_RUNNING); } else if (thread->state != PS_RUNNING) { if ((thread->flags & THR_FLAGS_IN_WAITQ) != 0) KSE_WAITQ_REMOVE(thread->kse, thread); if ((thread->flags & THR_FLAGS_SUSPENDED) != 0) THR_SET_STATE(thread, PS_SUSPENDED); else { THR_SET_STATE(thread, PS_RUNNING); if ((thread->blocked == 0) && (thread->active == 0) && (thread->flags & THR_FLAGS_IN_RUNQ) == 0) THR_RUNQ_INSERT_TAIL(thread); } } /* * XXX - Threads are not yet assigned to specific KSEs; they are * assigned to the KSEG. So the fact that a thread's KSE is * waiting doesn't necessarily mean that it will be the KSE * that runs the thread after the lock is granted. But we * don't know if the other KSEs within the same KSEG are * also in a waiting state or not so we err on the side of * caution and wakeup the thread's last known KSE. We * ensure that the threads KSE doesn't change while it's * scheduling lock is held so it is safe to reference it * (the KSE). If the KSE wakes up and doesn't find any more * work it will again go back to waiting so no harm is done. */ kse_wakeup_one(thread); } static void kse_wakeup_one(struct pthread *thread) { struct kse *ke; if (KSE_IS_IDLE(thread->kse)) { KSE_CLEAR_IDLE(thread->kse); thread->kseg->kg_idle_kses--; KSE_WAKEUP(thread->kse); } else { TAILQ_FOREACH(ke, &thread->kseg->kg_kseq, k_kgqe) { if (KSE_IS_IDLE(ke)) { KSE_CLEAR_IDLE(ke); ke->k_kseg->kg_idle_kses--; KSE_WAKEUP(ke); return; } } } } static void kse_wakeup_multi(struct kse *curkse) { struct kse *ke; int tmp; if ((tmp = KSE_RUNQ_THREADS(curkse)) && curkse->k_kseg->kg_idle_kses) { TAILQ_FOREACH(ke, &curkse->k_kseg->kg_kseq, k_kgqe) { if (KSE_IS_IDLE(ke)) { KSE_CLEAR_IDLE(ke); ke->k_kseg->kg_idle_kses--; KSE_WAKEUP(ke); if (--tmp == 0) break; } } } } struct pthread * _get_curthread(void) { return (_ksd_curthread()); } /* This assumes the caller has disabled upcalls. */ struct kse * _get_curkse(void) { return (_ksd_curkse()); } void _set_curkse(struct kse *kse) { _ksd_setprivate(&kse->k_ksd); } /* * Allocate a new KSEG. * * We allow the current thread to be NULL in the case that this * is the first time a KSEG is being created (library initialization). * In this case, we don't need to (and can't) take any locks. */ struct kse_group * _kseg_alloc(struct pthread *curthread) { struct kse_group *kseg = NULL; kse_critical_t crit; if ((curthread != NULL) && (free_kseg_count > 0)) { /* Use the kse lock for the kseg queue. */ crit = _kse_critical_enter(); KSE_LOCK_ACQUIRE(curthread->kse, &kse_lock); if ((kseg = TAILQ_FIRST(&free_kse_groupq)) != NULL) { TAILQ_REMOVE(&free_kse_groupq, kseg, kg_qe); free_kseg_count--; active_kseg_count++; TAILQ_INSERT_TAIL(&active_kse_groupq, kseg, kg_qe); } KSE_LOCK_RELEASE(curthread->kse, &kse_lock); _kse_critical_leave(crit); if (kseg) kseg_reinit(kseg); } /* * If requested, attempt to allocate a new KSE group only if the * KSE allocation was successful and a KSE group wasn't found in * the free list. */ if ((kseg == NULL) && ((kseg = (struct kse_group *)malloc(sizeof(*kseg))) != NULL)) { if (_pq_alloc(&kseg->kg_schedq.sq_runq, THR_MIN_PRIORITY, THR_LAST_PRIORITY) != 0) { free(kseg); kseg = NULL; } else { kseg_init(kseg); /* Add the KSEG to the list of active KSEGs. */ if (curthread != NULL) { crit = _kse_critical_enter(); KSE_LOCK_ACQUIRE(curthread->kse, &kse_lock); active_kseg_count++; TAILQ_INSERT_TAIL(&active_kse_groupq, kseg, kg_qe); KSE_LOCK_RELEASE(curthread->kse, &kse_lock); _kse_critical_leave(crit); } else { active_kseg_count++; TAILQ_INSERT_TAIL(&active_kse_groupq, kseg, kg_qe); } } } return (kseg); } /* * This must be called with the kse lock held and when there are * no more threads that reference it. */ static void kseg_free_unlocked(struct kse_group *kseg) { TAILQ_REMOVE(&active_kse_groupq, kseg, kg_qe); TAILQ_INSERT_HEAD(&free_kse_groupq, kseg, kg_qe); free_kseg_count++; active_kseg_count--; } void _kseg_free(struct kse_group *kseg) { struct kse *curkse; kse_critical_t crit; crit = _kse_critical_enter(); curkse = _get_curkse(); KSE_LOCK_ACQUIRE(curkse, &kse_lock); kseg_free_unlocked(kseg); KSE_LOCK_RELEASE(curkse, &kse_lock); _kse_critical_leave(crit); } /* * Allocate a new KSE. * * We allow the current thread to be NULL in the case that this * is the first time a KSE is being created (library initialization). * In this case, we don't need to (and can't) take any locks. */ struct kse * _kse_alloc(struct pthread *curthread) { struct kse *kse = NULL; kse_critical_t crit; int need_ksd = 0; int i; if ((curthread != NULL) && (free_kse_count > 0)) { crit = _kse_critical_enter(); KSE_LOCK_ACQUIRE(curthread->kse, &kse_lock); /* Search for a finished KSE. */ kse = TAILQ_FIRST(&free_kseq); while ((kse != NULL) && ((kse->k_mbx.km_flags & KMF_DONE) == 0)) { kse = TAILQ_NEXT(kse, k_qe); } if (kse != NULL) { DBG_MSG("found an unused kse.\n"); TAILQ_REMOVE(&free_kseq, kse, k_qe); free_kse_count--; TAILQ_INSERT_TAIL(&active_kseq, kse, k_qe); active_kse_count++; } KSE_LOCK_RELEASE(curthread->kse, &kse_lock); _kse_critical_leave(crit); if (kse != NULL) kse_reinit(kse); } if ((kse == NULL) && ((kse = (struct kse *)malloc(sizeof(*kse))) != NULL)) { bzero(kse, sizeof(*kse)); /* Initialize the lockusers. */ for (i = 0; i < MAX_KSE_LOCKLEVEL; i++) { _lockuser_init(&kse->k_lockusers[i], (void *)kse); _LCK_SET_PRIVATE2(&kse->k_lockusers[i], NULL); } /* _lock_init(kse->k_lock, ...) */ /* We had to malloc a kse; mark it as needing a new ID.*/ need_ksd = 1; /* * Create the KSE context. * * XXX - For now this is done here in the allocation. * In the future, we may want to have it done * outside the allocation so that scope system * threads (one thread per KSE) are not required * to have a stack for an unneeded kse upcall. */ kse->k_mbx.km_func = (kse_func_t *)kse_sched_multi; kse->k_mbx.km_stack.ss_sp = (char *)malloc(KSE_STACKSIZE); kse->k_mbx.km_stack.ss_size = KSE_STACKSIZE; kse->k_mbx.km_udata = (void *)kse; kse->k_mbx.km_quantum = 20000; /* * We need to keep a copy of the stack in case it * doesn't get used; a KSE running a scope system * thread will use that thread's stack. */ kse->k_stack.ss_sp = kse->k_mbx.km_stack.ss_sp; kse->k_stack.ss_size = kse->k_mbx.km_stack.ss_size; if (kse->k_mbx.km_stack.ss_sp == NULL) { for (i = 0; i < MAX_KSE_LOCKLEVEL; i++) { _lockuser_destroy(&kse->k_lockusers[i]); } /* _lock_destroy(&kse->k_lock); */ free(kse); kse = NULL; } } if ((kse != NULL) && (need_ksd != 0)) { /* This KSE needs initialization. */ if (curthread != NULL) { crit = _kse_critical_enter(); KSE_LOCK_ACQUIRE(curthread->kse, &kse_lock); } /* Initialize KSD inside of the lock. */ if (_ksd_create(&kse->k_ksd, (void *)kse, sizeof(*kse)) != 0) { if (curthread != NULL) { KSE_LOCK_RELEASE(curthread->kse, &kse_lock); _kse_critical_leave(crit); } free(kse->k_mbx.km_stack.ss_sp); for (i = 0; i < MAX_KSE_LOCKLEVEL; i++) { _lockuser_destroy(&kse->k_lockusers[i]); } free(kse); + if (curthread != NULL) { + KSE_LOCK_RELEASE(curthread->kse, &kse_lock); + _kse_critical_leave(crit); + } return (NULL); } kse->k_flags = 0; TAILQ_INSERT_TAIL(&active_kseq, kse, k_qe); active_kse_count++; if (curthread != NULL) { KSE_LOCK_RELEASE(curthread->kse, &kse_lock); _kse_critical_leave(crit); } } return (kse); } static void kse_reinit(struct kse *kse) { /* * XXX - For now every kse has its stack. * In the future, we may want to have it done * outside the allocation so that scope system * threads (one thread per KSE) are not required * to have a stack for an unneeded kse upcall. */ kse->k_mbx.km_flags = 0; kse->k_curthread = 0; kse->k_kseg = 0; kse->k_schedq = 0; kse->k_locklevel = 0; - sigemptyset(&kse->k_sigmask); + SIGEMPTYSET(kse->k_sigmask); bzero(&kse->k_sigq, sizeof(kse->k_sigq)); kse->k_check_sigq = 0; kse->k_flags = 0; kse->k_waiting = 0; kse->k_idle = 0; kse->k_error = 0; kse->k_cpu = 0; kse->k_done = 0; + kse->k_switch = 0; } void kse_free_unlocked(struct kse *kse) { TAILQ_REMOVE(&active_kseq, kse, k_qe); active_kse_count--; kse->k_kseg = NULL; kse->k_mbx.km_quantum = 20000; kse->k_flags = 0; TAILQ_INSERT_HEAD(&free_kseq, kse, k_qe); free_kse_count++; } void _kse_free(struct pthread *curthread, struct kse *kse) { kse_critical_t crit; if (curthread == NULL) kse_free_unlocked(kse); else { crit = _kse_critical_enter(); KSE_LOCK_ACQUIRE(curthread->kse, &kse_lock); kse_free_unlocked(kse); KSE_LOCK_RELEASE(curthread->kse, &kse_lock); _kse_critical_leave(crit); } } static void kseg_init(struct kse_group *kseg) { kseg_reinit(kseg); _lock_init(&kseg->kg_lock, LCK_ADAPTIVE, _kse_lock_wait, _kse_lock_wakeup); } static void kseg_reinit(struct kse_group *kseg) { TAILQ_INIT(&kseg->kg_kseq); TAILQ_INIT(&kseg->kg_threadq); TAILQ_INIT(&kseg->kg_schedq.sq_waitq); kseg->kg_threadcount = 0; kseg->kg_ksecount = 0; kseg->kg_idle_kses = 0; kseg->kg_flags = 0; } struct pthread * _thr_alloc(struct pthread *curthread) { kse_critical_t crit; void *p; struct pthread *thread = NULL; if (curthread != NULL) { if (GC_NEEDED()) _thr_gc(curthread); if (free_thread_count > 0) { crit = _kse_critical_enter(); KSE_LOCK_ACQUIRE(curthread->kse, &thread_lock); if ((thread = TAILQ_FIRST(&free_threadq)) != NULL) { TAILQ_REMOVE(&free_threadq, thread, tle); free_thread_count--; } KSE_LOCK_RELEASE(curthread->kse, &thread_lock); _kse_critical_leave(crit); } } if (thread == NULL) { p = malloc(sizeof(struct pthread) + THR_ALIGNBYTES); if (p != NULL) { thread = (struct pthread *)THR_ALIGN(p); thread->alloc_addr = p; } } return (thread); } void _thr_free(struct pthread *curthread, struct pthread *thread) { kse_critical_t crit; int i; DBG_MSG("Freeing thread %p\n", thread); if ((curthread == NULL) || (free_thread_count >= MAX_CACHED_THREADS)) { for (i = 0; i < MAX_THR_LOCKLEVEL; i++) { _lockuser_destroy(&thread->lockusers[i]); } _lock_destroy(&thread->lock); free(thread->alloc_addr); } else { crit = _kse_critical_enter(); KSE_LOCK_ACQUIRE(curthread->kse, &thread_lock); TAILQ_INSERT_TAIL(&free_threadq, thread, tle); free_thread_count++; KSE_LOCK_RELEASE(curthread->kse, &thread_lock); _kse_critical_leave(crit); } } /* * Add an active thread: * * o Assign the thread a unique id (which GDB uses to track * threads. * o Add the thread to the list of all threads and increment * number of active threads. */ static void thr_link(struct pthread *thread) { kse_critical_t crit; struct kse *curkse; + struct pthread *curthread; crit = _kse_critical_enter(); curkse = _get_curkse(); - + curthread = _get_curthread(); + thread->sigmask = curthread->sigmask; KSE_LOCK_ACQUIRE(curkse, &_thread_list_lock); /* * Initialize the unique id (which GDB uses to track * threads), add the thread to the list of all threads, * and */ thread->uniqueid = next_uniqueid++; THR_LIST_ADD(thread); active_threads++; KSE_LOCK_RELEASE(curkse, &_thread_list_lock); _kse_critical_leave(crit); } /* * Remove an active thread. */ static void thr_unlink(struct pthread *thread) { kse_critical_t crit; struct kse *curkse; crit = _kse_critical_enter(); curkse = _get_curkse(); KSE_LOCK_ACQUIRE(curkse, &_thread_list_lock); THR_LIST_REMOVE(thread); active_threads--; KSE_LOCK_RELEASE(curkse, &_thread_list_lock); _kse_critical_leave(crit); } Index: head/lib/libpthread/thread/thr_kill.c =================================================================== --- head/lib/libpthread/thread/thr_kill.c (revision 116976) +++ head/lib/libpthread/thread/thr_kill.c (revision 116977) @@ -1,66 +1,66 @@ /* * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by John Birrell. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include "thr_private.h" __weak_reference(_pthread_kill, pthread_kill); int _pthread_kill(pthread_t pthread, int sig) { struct pthread *curthread = _get_curthread(); int ret; /* Check for invalid signal numbers: */ - if (sig < 0 || sig >= NSIG) + if (sig < 0 || sig > _SIG_MAXSIG) /* Invalid signal: */ ret = EINVAL; /* * Ensure the thread is in the list of active threads, and the * signal is valid (signal 0 specifies error checking only) and * not being ignored: */ else if ((ret = _thr_ref_add(curthread, pthread, /*include dead*/0)) == 0) { if ((sig > 0) && (_thread_sigact[sig - 1].sa_handler != SIG_IGN)) _thr_sig_send(pthread, sig); _thr_ref_delete(curthread, pthread); } /* Return the completion status: */ return (ret); } Index: head/lib/libpthread/thread/thr_private.h =================================================================== --- head/lib/libpthread/thread/thr_private.h (revision 116976) +++ head/lib/libpthread/thread/thr_private.h (revision 116977) @@ -1,1231 +1,1236 @@ /* * 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. * * Private thread definitions for the uthread kernel. * * $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 "lock.h" #include "pthread_md.h" /* * Evaluate the storage class specifier. */ #ifdef GLOBAL_PTHREAD_PRIVATE #define SCLASS #define SCLASS_PRESET(x...) = x #else #define SCLASS extern #define SCLASS_PRESET(x...) #endif /* * Kernel fatal error handler macro. */ #define PANIC(string) _thr_exit(__FILE__,__LINE__,string) /* Output debug messages like this: */ #define stdout_debug(args...) _thread_printf(STDOUT_FILENO, ##args) #define stderr_debug(args...) _thread_printf(STDOUT_FILENO, ##args) #define DBG_MUTEX 0x0001 #define DBG_SIG 0x0002 #define THR_ASSERT(cond, msg) do { \ if (!(cond)) \ PANIC(msg); \ } while (0) /* * State change macro without scheduling queue change: */ #define THR_SET_STATE(thrd, newstate) do { \ (thrd)->state = newstate; \ (thrd)->fname = __FILE__; \ (thrd)->lineno = __LINE__; \ } while (0) /* * Define the signals to be used for scheduling. */ #define _ITIMER_SCHED_TIMER ITIMER_PROF #define _SCHED_SIGNAL SIGPROF #define TIMESPEC_ADD(dst, src, val) \ do { \ (dst)->tv_sec = (src)->tv_sec + (val)->tv_sec; \ (dst)->tv_nsec = (src)->tv_nsec + (val)->tv_nsec; \ if ((dst)->tv_nsec > 1000000000) { \ (dst)->tv_sec++; \ (dst)->tv_nsec -= 1000000000; \ } \ } while (0) #define TIMESPEC_SUB(dst, src, val) \ do { \ (dst)->tv_sec = (src)->tv_sec - (val)->tv_sec; \ (dst)->tv_nsec = (src)->tv_nsec - (val)->tv_nsec; \ if ((dst)->tv_nsec < 0) { \ (dst)->tv_sec--; \ (dst)->tv_nsec += 1000000000; \ } \ } while (0) /* * Priority queues. * * XXX It'd be nice if these were contained in uthread_priority_queue.[ch]. */ typedef struct pq_list { TAILQ_HEAD(, pthread) pl_head; /* list of threads at this priority */ TAILQ_ENTRY(pq_list) pl_link; /* link for queue of priority lists */ int pl_prio; /* the priority of this list */ int pl_queued; /* is this in the priority queue */ } pq_list_t; typedef struct pq_queue { TAILQ_HEAD(, pq_list) pq_queue; /* queue of priority lists */ pq_list_t *pq_lists; /* array of all priority lists */ int pq_size; /* number of priority lists */ #define PQF_ACTIVE 0x0001 int pq_flags; int pq_threads; } pq_queue_t; /* * Each KSEG has a scheduling queue. For now, threads that exist in their * own KSEG (system scope) will get a full priority queue. In the future * this can be optimized for the single thread per KSEG case. */ struct sched_queue { pq_queue_t sq_runq; TAILQ_HEAD(, pthread) sq_waitq; /* waiting in userland */ }; /* Used to maintain pending and active signals: */ struct sigstatus { siginfo_t *info; /* arg 2 to signal handler */ int pending; /* Is this a pending signal? */ int blocked; /* * This signal has occured and hasn't * yet been handled; ignore subsequent * signals until the handler is done. */ int signo; }; typedef struct kse_thr_mailbox *kse_critical_t; struct kse_group; -#define MAX_KSE_LOCKLEVEL 3 +#define MAX_KSE_LOCKLEVEL 5 struct kse { struct kse_mailbox k_mbx; /* kernel kse mailbox */ /* -- location and order specific items for gdb -- */ struct pthread *k_curthread; /* current thread */ struct kse_group *k_kseg; /* parent KSEG */ struct sched_queue *k_schedq; /* scheduling queue */ /* -- end of location and order specific items -- */ TAILQ_ENTRY(kse) k_qe; /* KSE list link entry */ TAILQ_ENTRY(kse) k_kgqe; /* KSEG's KSE list entry */ struct ksd k_ksd; /* KSE specific data */ /* * Items that are only modified by the kse, or that otherwise * don't need to be locked when accessed */ struct lock k_lock; struct lockuser k_lockusers[MAX_KSE_LOCKLEVEL]; int k_locklevel; sigset_t k_sigmask; - struct sigstatus k_sigq[NSIG]; + struct sigstatus k_sigq[_SIG_MAXSIG]; stack_t k_stack; int k_check_sigq; int k_flags; #define KF_STARTED 0x0001 /* kernel kse created */ #define KF_INITIALIZED 0x0002 /* initialized on 1st upcall */ int k_waiting; int k_idle; /* kse is idle */ int k_error; /* syscall errno in critical */ int k_cpu; /* CPU ID when bound */ int k_done; /* this KSE is done */ + int k_switch; /* thread switch in UTS */ }; /* * Each KSE group contains one or more KSEs in which threads can run. * At least for now, there is one scheduling queue per KSE group; KSEs * within the same KSE group compete for threads from the same scheduling * queue. A scope system thread has one KSE in one KSE group; the group * does not use its scheduling queue. */ struct kse_group { TAILQ_HEAD(, kse) kg_kseq; /* list of KSEs in group */ TAILQ_HEAD(, pthread) kg_threadq; /* list of threads in group */ TAILQ_ENTRY(kse_group) kg_qe; /* link entry */ struct sched_queue kg_schedq; /* scheduling queue */ struct lock kg_lock; int kg_threadcount; /* # of assigned threads */ int kg_ksecount; /* # of assigned KSEs */ int kg_idle_kses; int kg_flags; #define KGF_SINGLE_THREAD 0x0001 /* scope system kse group */ #define KGF_SCHEDQ_INITED 0x0002 /* has an initialized schedq */ }; /* * Add/remove threads from a KSE's scheduling queue. * For now the scheduling queue is hung off the KSEG. */ #define KSEG_THRQ_ADD(kseg, thr) \ do { \ TAILQ_INSERT_TAIL(&(kseg)->kg_threadq, thr, kle);\ (kseg)->kg_threadcount++; \ } while (0) #define KSEG_THRQ_REMOVE(kseg, thr) \ do { \ TAILQ_REMOVE(&(kseg)->kg_threadq, thr, kle); \ (kseg)->kg_threadcount--; \ } while (0) /* * Lock acquire and release for KSEs. */ #define KSE_LOCK_ACQUIRE(kse, lck) \ do { \ if ((kse)->k_locklevel >= MAX_KSE_LOCKLEVEL) \ PANIC("Exceeded maximum lock level"); \ else { \ (kse)->k_locklevel++; \ _lock_acquire((lck), \ &(kse)->k_lockusers[(kse)->k_locklevel - 1], 0); \ } \ } while (0) #define KSE_LOCK_RELEASE(kse, lck) \ do { \ if ((kse)->k_locklevel > 0) { \ _lock_release((lck), \ &(kse)->k_lockusers[(kse)->k_locklevel - 1]); \ (kse)->k_locklevel--; \ } \ } while (0) /* * Lock our own KSEG. */ #define KSE_LOCK(curkse) \ KSE_LOCK_ACQUIRE(curkse, &(curkse)->k_kseg->kg_lock) #define KSE_UNLOCK(curkse) \ KSE_LOCK_RELEASE(curkse, &(curkse)->k_kseg->kg_lock) /* * Lock a potentially different KSEG. */ #define KSE_SCHED_LOCK(curkse, kseg) \ KSE_LOCK_ACQUIRE(curkse, &(kseg)->kg_lock) #define KSE_SCHED_UNLOCK(curkse, kseg) \ KSE_LOCK_RELEASE(curkse, &(kseg)->kg_lock) /* * Waiting queue manipulation macros (using pqe link): */ #define KSE_WAITQ_REMOVE(kse, thrd) \ do { \ if (((thrd)->flags & THR_FLAGS_IN_WAITQ) != 0) { \ TAILQ_REMOVE(&(kse)->k_schedq->sq_waitq, thrd, pqe); \ (thrd)->flags &= ~THR_FLAGS_IN_WAITQ; \ } \ } while (0) #define KSE_WAITQ_INSERT(kse, thrd) kse_waitq_insert(thrd) #define KSE_WAITQ_FIRST(kse) TAILQ_FIRST(&(kse)->k_schedq->sq_waitq) #define KSE_SET_WAIT(kse) atomic_store_rel_int(&(kse)->k_waiting, 1) #define KSE_CLEAR_WAIT(kse) atomic_store_rel_int(&(kse)->k_waiting, 0) #define KSE_WAITING(kse) (kse)->k_waiting != 0 #define KSE_WAKEUP(kse) kse_wakeup(&(kse)->k_mbx) #define KSE_SET_IDLE(kse) ((kse)->k_idle = 1) #define KSE_CLEAR_IDLE(kse) ((kse)->k_idle = 0) #define KSE_IS_IDLE(kse) ((kse)->k_idle != 0) /* * TailQ initialization values. */ #define TAILQ_INITIALIZER { NULL, NULL } /* * lock initialization values. */ #define LCK_INITIALIZER { NULL, NULL, LCK_DEFAULT } struct pthread_mutex { /* * Lock for accesses to this structure. */ struct lock m_lock; enum pthread_mutextype m_type; int m_protocol; TAILQ_HEAD(mutex_head, pthread) m_queue; struct pthread *m_owner; long m_flags; int m_count; int m_refcount; /* * Used for priority inheritence and protection. * * m_prio - For priority inheritence, the highest active * priority (threads locking the mutex inherit * this priority). For priority protection, the * ceiling priority of this mutex. * m_saved_prio - mutex owners inherited priority before * taking the mutex, restored when the owner * unlocks the mutex. */ int m_prio; int m_saved_prio; /* * Link for list of all mutexes a thread currently owns. */ TAILQ_ENTRY(pthread_mutex) m_qe; }; /* * Flags for mutexes. */ #define MUTEX_FLAGS_PRIVATE 0x01 #define MUTEX_FLAGS_INITED 0x02 #define MUTEX_FLAGS_BUSY 0x04 /* * Static mutex initialization values. */ #define PTHREAD_MUTEX_STATIC_INITIALIZER \ { LCK_INITIALIZER, PTHREAD_MUTEX_DEFAULT, PTHREAD_PRIO_NONE, \ TAILQ_INITIALIZER, NULL, MUTEX_FLAGS_PRIVATE, 0, 0, 0, 0, \ TAILQ_INITIALIZER } struct pthread_mutex_attr { enum pthread_mutextype m_type; int m_protocol; int m_ceiling; long m_flags; }; #define PTHREAD_MUTEXATTR_STATIC_INITIALIZER \ { PTHREAD_MUTEX_DEFAULT, PTHREAD_PRIO_NONE, 0, MUTEX_FLAGS_PRIVATE } /* * Condition variable definitions. */ enum pthread_cond_type { COND_TYPE_FAST, COND_TYPE_MAX }; struct pthread_cond { /* * Lock for accesses to this structure. */ struct lock c_lock; enum pthread_cond_type c_type; TAILQ_HEAD(cond_head, pthread) c_queue; struct pthread_mutex *c_mutex; long c_flags; long c_seqno; }; struct pthread_cond_attr { enum pthread_cond_type c_type; long c_flags; }; /* * Flags for condition variables. */ #define COND_FLAGS_PRIVATE 0x01 #define COND_FLAGS_INITED 0x02 #define COND_FLAGS_BUSY 0x04 /* * Static cond initialization values. */ #define PTHREAD_COND_STATIC_INITIALIZER \ { LCK_INITIALIZER, COND_TYPE_FAST, TAILQ_INITIALIZER, \ NULL, NULL, 0, 0 } /* * Semaphore definitions. */ struct sem { #define SEM_MAGIC ((u_int32_t) 0x09fa4012) u_int32_t magic; pthread_mutex_t lock; pthread_cond_t gtzero; u_int32_t count; u_int32_t nwaiters; }; /* * Cleanup definitions. */ struct pthread_cleanup { struct pthread_cleanup *next; void (*routine) (); void *routine_arg; }; struct pthread_attr { int sched_policy; int sched_inherit; int sched_interval; int prio; int suspend; #define THR_STACK_USER 0x100 /* 0xFF reserved for */ int flags; void *arg_attr; void (*cleanup_attr) (); void *stackaddr_attr; size_t stacksize_attr; size_t guardsize_attr; }; /* * Thread creation state attributes. */ #define THR_CREATE_RUNNING 0 #define THR_CREATE_SUSPENDED 1 /* * Miscellaneous definitions. */ #define THR_STACK_DEFAULT 65536 /* * 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 0x100000 /* * Define the different priority ranges. All applications have thread * priorities constrained within 0-31. The threads library raises the * priority when delivering signals in order to ensure that signal * delivery happens (from the POSIX spec) "as soon as possible". * In the future, the threads library will also be able to map specific * threads into real-time (cooperating) processes or kernel threads. * The RT and SIGNAL priorities will be used internally and added to * thread base priorities so that the scheduling queue can handle both * normal and RT priority threads with and without signal handling. * * The approach taken is that, within each class, signal delivery * always has priority over thread execution. */ #define THR_DEFAULT_PRIORITY 15 #define THR_MIN_PRIORITY 0 #define THR_MAX_PRIORITY 31 /* 0x1F */ #define THR_SIGNAL_PRIORITY 32 /* 0x20 */ #define THR_RT_PRIORITY 64 /* 0x40 */ #define THR_FIRST_PRIORITY THR_MIN_PRIORITY #define THR_LAST_PRIORITY \ (THR_MAX_PRIORITY + THR_SIGNAL_PRIORITY + THR_RT_PRIORITY) #define THR_BASE_PRIORITY(prio) ((prio) & THR_MAX_PRIORITY) /* * Clock resolution in microseconds. */ #define CLOCK_RES_USEC 10000 /* * Time slice period in microseconds. */ #define TIMESLICE_USEC 20000 /* * XXX - Define a thread-safe macro to get the current time of day * which is updated at regular intervals by something. * * For now, we just make the system call to get the time. */ #define KSE_GET_TOD(curkse, tsp) \ do { \ *tsp = (curkse)->k_mbx.km_timeofday; \ if ((tsp)->tv_sec == 0) \ clock_gettime(CLOCK_REALTIME, tsp); \ } while (0) struct pthread_rwlockattr { int pshared; }; struct pthread_rwlock { pthread_mutex_t lock; /* monitor lock */ int state; /* 0 = idle >0 = # of readers -1 = writer */ pthread_cond_t read_signal; pthread_cond_t write_signal; int blocked_writers; }; /* * Thread states. */ enum pthread_state { PS_RUNNING, PS_LOCKWAIT, PS_MUTEX_WAIT, PS_COND_WAIT, PS_SLEEP_WAIT, PS_SIGSUSPEND, PS_SIGWAIT, PS_JOIN, PS_SUSPENDED, PS_DEAD, PS_DEADLOCK, PS_STATE_MAX }; union pthread_wait_data { pthread_mutex_t mutex; pthread_cond_t cond; - const sigset_t *sigwait; /* Waiting on a signal in sigwait */ struct lock *lock; + siginfo_t *sigwaitinfo; /* used to save siginfo for sigwaitinfo() */ }; /* * Define a continuation routine that can be used to perform a * transfer of control: */ typedef void (*thread_continuation_t) (void *); /* * This stores a thread's state prior to running a signal handler. * It is used when a signal is delivered to a thread blocked in * userland. If the signal handler returns normally, the thread's * state is restored from here. */ struct pthread_sigframe { + int psf_valid; int psf_flags; int psf_interrupted; int psf_signo; enum pthread_state psf_state; union pthread_wait_data psf_wait_data; struct timespec psf_wakeup_time; sigset_t psf_sigset; sigset_t psf_sigmask; int psf_seqno; }; struct join_status { struct pthread *thread; void *ret; int error; }; struct pthread_specific_elem { const void *data; int seqno; }; -#define MAX_THR_LOCKLEVEL 3 +#define MAX_THR_LOCKLEVEL 5 /* * Thread structure. */ struct pthread { /* * Thread mailbox is first so it cal be aligned properly. */ struct kse_thr_mailbox tmbx; void *alloc_addr; /* real address (unaligned) */ /* * Magic value to help recognize a valid thread structure * from an invalid one: */ #define THR_MAGIC ((u_int32_t) 0xd09ba115) u_int32_t magic; char *name; u_int64_t uniqueid; /* for gdb */ /* Queue entry for list of all threads: */ TAILQ_ENTRY(pthread) tle; /* link for all threads in process */ TAILQ_ENTRY(pthread) kle; /* link for all threads in KSE/KSEG */ /* Queue entry for GC lists: */ TAILQ_ENTRY(pthread) gcle; /* * Lock for accesses to this thread structure. */ struct lock lock; struct lockuser lockusers[MAX_THR_LOCKLEVEL]; int locklevel; kse_critical_t critical[MAX_KSE_LOCKLEVEL]; struct kse *kse; struct kse_group *kseg; /* * Thread start routine, argument, stack pointer and thread * attributes. */ void *(*start_routine)(void *); void *arg; struct pthread_attr attr; int active; /* thread running */ int blocked; /* thread blocked in kernel */ int need_switchout; int need_wakeup; /* * Used for tracking delivery of signal handlers. */ struct pthread_sigframe *curframe; - siginfo_t siginfo[NSIG]; + siginfo_t siginfo[_SIG_MAXSIG]; /* * Cancelability flags - the lower 2 bits are used by cancel * definitions in pthread.h */ #define THR_AT_CANCEL_POINT 0x0004 #define THR_CANCELLING 0x0008 #define THR_CANCEL_NEEDED 0x0010 int cancelflags; thread_continuation_t continuation; /* * The thread's base and pending signal masks. The active * signal mask is stored in the thread's context (in mailbox). */ + sigset_t oldsigmask; sigset_t sigmask; sigset_t sigpend; - int sigmask_seqno; int check_pending; - int have_signals; int refcount; /* Thread state: */ enum pthread_state state; volatile int lock_switch; /* * Number of microseconds accumulated by this thread when * time slicing is active. */ long slice_usec; /* * Time to wake up thread. This is used for sleeping threads and * for any operation which may time out (such as select). */ struct timespec wakeup_time; /* TRUE if operation has timed out. */ int timeout; /* * Error variable used instead of errno. The function __error() * returns a pointer to this. */ int error; /* * The joiner is the thread that is joining to this thread. The * join status keeps track of a join operation to another thread. */ struct pthread *joiner; struct join_status join_status; /* * The current thread can belong to only one scheduling queue at * a time (ready or waiting queue). It can also belong to: * * o A queue of threads waiting for a mutex * o A queue of threads waiting for a condition variable * * It is possible for a thread to belong to more than one of the * above queues if it is handling a signal. A thread may only * enter a mutex or condition variable queue when it is not * being called from a signal handler. If a thread is a member * of one of these queues when a signal handler is invoked, it * must be removed from the queue before invoking the handler * and then added back to the queue after return from the handler. * * Use pqe for the scheduling queue link (both ready and waiting), * sqe for synchronization (mutex, condition variable, and join) * queue links, and qe for all other links. */ TAILQ_ENTRY(pthread) pqe; /* priority, wait queues link */ TAILQ_ENTRY(pthread) sqe; /* synchronization queue link */ /* Wait data. */ union pthread_wait_data data; /* * Set to TRUE if a blocking operation was * interrupted by a signal: */ int interrupted; /* Signal number when in state PS_SIGWAIT: */ int signo; /* * Set to non-zero when this thread has entered a critical * region. We allow for recursive entries into critical regions. */ int critical_count; /* * Set to TRUE if this thread should yield after leaving a * critical region to check for signals, messages, etc. */ int critical_yield; int sflags; #define THR_FLAGS_IN_SYNCQ 0x0001 /* Miscellaneous flags; only set with scheduling lock held. */ int flags; #define THR_FLAGS_PRIVATE 0x0001 #define THR_FLAGS_IN_WAITQ 0x0002 /* in waiting queue using pqe link */ #define THR_FLAGS_IN_RUNQ 0x0004 /* in run queue using pqe link */ #define THR_FLAGS_EXITING 0x0008 /* thread is exiting */ #define THR_FLAGS_SUSPENDED 0x0010 /* thread is suspended */ #define THR_FLAGS_GC_SAFE 0x0020 /* thread safe for cleaning */ #define THR_FLAGS_IN_TDLIST 0x0040 /* thread in all thread list */ #define THR_FLAGS_IN_GCLIST 0x0080 /* thread in gc list */ /* * Base priority is the user setable and retrievable priority * of the thread. It is only affected by explicit calls to * set thread priority and upon thread creation via a thread * attribute or default priority. */ char base_priority; /* * Inherited priority is the priority a thread inherits by * taking a priority inheritence or protection mutex. It * is not affected by base priority changes. Inherited * priority defaults to and remains 0 until a mutex is taken * that is being waited on by any other thread whose priority * is non-zero. */ char inherited_priority; /* * Active priority is always the maximum of the threads base * priority and inherited priority. When there is a change * in either the base or inherited priority, the active * priority must be recalculated. */ char active_priority; /* Number of priority ceiling or protection mutexes owned. */ int priority_mutex_count; /* * Queue of currently owned mutexes. */ TAILQ_HEAD(, pthread_mutex) mutexq; void *ret; struct pthread_specific_elem *specific; int specific_data_count; /* * Current locks bitmap for rtld. */ int rtld_bits; /* Cleanup handlers Link List */ struct pthread_cleanup *cleanup; char *fname; /* Ptr to source file name */ int lineno; /* Source line number. */ }; /* * Critical regions can also be detected by looking at the threads * current lock level. Ensure these macros increment and decrement * the lock levels such that locks can not be held with a lock level * of 0. */ #define THR_IN_CRITICAL(thrd) \ (((thrd)->locklevel > 0) || \ ((thrd)->critical_count > 0)) #define THR_YIELD_CHECK(thrd) \ do { \ if (((thrd)->critical_yield != 0) && \ !(THR_IN_CRITICAL(thrd))) \ _thr_sched_switch(thrd); \ else if (((thrd)->check_pending != 0) && \ !(THR_IN_CRITICAL(thrd))) \ _thr_sig_check_pending(thrd); \ } while (0) #define THR_LOCK_ACQUIRE(thrd, lck) \ do { \ if ((thrd)->locklevel >= MAX_THR_LOCKLEVEL) \ PANIC("Exceeded maximum lock level"); \ else { \ THR_DEACTIVATE_LAST_LOCK(thrd); \ (thrd)->locklevel++; \ _lock_acquire((lck), \ &(thrd)->lockusers[(thrd)->locklevel - 1], \ (thrd)->active_priority); \ } \ } while (0) #define THR_LOCK_RELEASE(thrd, lck) \ do { \ if ((thrd)->locklevel > 0) { \ _lock_release((lck), \ &(thrd)->lockusers[(thrd)->locklevel - 1]); \ (thrd)->locklevel--; \ THR_ACTIVATE_LAST_LOCK(thrd); \ if ((thrd)->locklevel == 0) \ THR_YIELD_CHECK(thrd); \ } \ } while (0) #define THR_ACTIVATE_LAST_LOCK(thrd) \ do { \ if ((thrd)->locklevel > 0) \ _lockuser_setactive( \ &(thrd)->lockusers[(thrd)->locklevel - 1], 1); \ } while (0) #define THR_DEACTIVATE_LAST_LOCK(thrd) \ do { \ if ((thrd)->locklevel > 0) \ _lockuser_setactive( \ &(thrd)->lockusers[(thrd)->locklevel - 1], 0); \ } while (0) /* * For now, threads will have their own lock separate from their * KSE scheduling lock. */ #define THR_LOCK(thr) THR_LOCK_ACQUIRE(thr, &(thr)->lock) #define THR_UNLOCK(thr) THR_LOCK_RELEASE(thr, &(thr)->lock) #define THR_THREAD_LOCK(curthrd, thr) THR_LOCK_ACQUIRE(curthrd, &(thr)->lock) #define THR_THREAD_UNLOCK(curthrd, thr) THR_LOCK_RELEASE(curthrd, &(thr)->lock) /* * Priority queue manipulation macros (using pqe link). We use * the thread's kseg link instead of the kse link because a thread * does not (currently) have a statically assigned kse. */ #define THR_RUNQ_INSERT_HEAD(thrd) \ _pq_insert_head(&(thrd)->kseg->kg_schedq.sq_runq, thrd) #define THR_RUNQ_INSERT_TAIL(thrd) \ _pq_insert_tail(&(thrd)->kseg->kg_schedq.sq_runq, thrd) #define THR_RUNQ_REMOVE(thrd) \ _pq_remove(&(thrd)->kseg->kg_schedq.sq_runq, thrd) #define THR_RUNQ_FIRST() \ _pq_first(&(thrd)->kseg->kg_schedq.sq_runq) /* * Macros to insert/remove threads to the all thread list and * the gc list. */ #define THR_LIST_ADD(thrd) do { \ if (((thrd)->flags & THR_FLAGS_IN_TDLIST) == 0) { \ TAILQ_INSERT_HEAD(&_thread_list, thrd, tle); \ (thrd)->flags |= THR_FLAGS_IN_TDLIST; \ } \ } while (0) #define THR_LIST_REMOVE(thrd) do { \ if (((thrd)->flags & THR_FLAGS_IN_TDLIST) != 0) { \ TAILQ_REMOVE(&_thread_list, thrd, tle); \ (thrd)->flags &= ~THR_FLAGS_IN_TDLIST; \ } \ } while (0) #define THR_GCLIST_ADD(thrd) do { \ if (((thrd)->flags & THR_FLAGS_IN_GCLIST) == 0) { \ TAILQ_INSERT_HEAD(&_thread_gc_list, thrd, gcle);\ (thrd)->flags |= THR_FLAGS_IN_GCLIST; \ _gc_count++; \ } \ } while (0) #define THR_GCLIST_REMOVE(thrd) do { \ if (((thrd)->flags & THR_FLAGS_IN_GCLIST) != 0) { \ TAILQ_REMOVE(&_thread_gc_list, thrd, gcle); \ (thrd)->flags &= ~THR_FLAGS_IN_GCLIST; \ _gc_count--; \ } \ } while (0) #define GC_NEEDED() (atomic_load_acq_int(&_gc_count) >= 5) /* * Locking the scheduling queue for another thread uses that thread's * KSEG lock. */ #define THR_SCHED_LOCK(curthr, thr) do { \ (curthr)->critical[(curthr)->locklevel] = _kse_critical_enter(); \ (curthr)->locklevel++; \ KSE_SCHED_LOCK((curthr)->kse, (thr)->kseg); \ } while (0) #define THR_SCHED_UNLOCK(curthr, thr) do { \ KSE_SCHED_UNLOCK((curthr)->kse, (thr)->kseg); \ (curthr)->locklevel--; \ _kse_critical_leave((curthr)->critical[(curthr)->locklevel]); \ } while (0) /* Take the scheduling lock with the intent to call the scheduler. */ #define THR_LOCK_SWITCH(curthr) do { \ (void)_kse_critical_enter(); \ KSE_SCHED_LOCK((curthr)->kse, (curthr)->kseg); \ } while (0) #define THR_CRITICAL_ENTER(thr) (thr)->critical_count++ #define THR_CRITICAL_LEAVE(thr) do { \ (thr)->critical_count--; \ if (((thr)->critical_yield != 0) && \ ((thr)->critical_count == 0)) { \ (thr)->critical_yield = 0; \ _thr_sched_switch(thr); \ } \ } while (0) #define THR_IS_ACTIVE(thrd) \ ((thrd)->kse != NULL) && ((thrd)->kse->k_curthread == (thrd)) #define THR_IN_SYNCQ(thrd) (((thrd)->sflags & THR_FLAGS_IN_SYNCQ) != 0) #define THR_IS_SUSPENDED(thrd) \ (((thrd)->state == PS_SUSPENDED) || \ (((thrd)->flags & THR_FLAGS_SUSPENDED) != 0)) #define THR_IS_EXITING(thrd) (((thrd)->flags & THR_FLAGS_EXITING) != 0) /* * Global variables for the pthread kernel. */ SCLASS void *_usrstack SCLASS_PRESET(NULL); SCLASS struct kse *_kse_initial SCLASS_PRESET(NULL); SCLASS struct pthread *_thr_initial SCLASS_PRESET(NULL); /* List of all threads: */ SCLASS TAILQ_HEAD(, pthread) _thread_list SCLASS_PRESET(TAILQ_HEAD_INITIALIZER(_thread_list)); /* List of threads needing GC: */ SCLASS TAILQ_HEAD(, pthread) _thread_gc_list SCLASS_PRESET(TAILQ_HEAD_INITIALIZER(_thread_gc_list)); /* Default thread attributes: */ SCLASS struct pthread_attr _pthread_attr_default SCLASS_PRESET({ SCHED_RR, 0, TIMESLICE_USEC, THR_DEFAULT_PRIORITY, THR_CREATE_RUNNING, PTHREAD_CREATE_JOINABLE, NULL, NULL, NULL, THR_STACK_DEFAULT }); /* Default mutex attributes: */ SCLASS struct pthread_mutex_attr _pthread_mutexattr_default SCLASS_PRESET({PTHREAD_MUTEX_DEFAULT, PTHREAD_PRIO_NONE, 0, 0 }); /* Default condition variable attributes: */ SCLASS struct pthread_cond_attr _pthread_condattr_default SCLASS_PRESET({COND_TYPE_FAST, 0}); /* Clock resolution in usec. */ SCLASS int _clock_res_usec SCLASS_PRESET(CLOCK_RES_USEC); /* Array of signal actions for this process: */ -SCLASS struct sigaction _thread_sigact[NSIG]; +SCLASS struct sigaction _thread_sigact[_SIG_MAXSIG]; /* * Array of counts of dummy handlers for SIG_DFL signals. This is used to * assure that there is always a dummy signal handler installed while there * is a thread sigwait()ing on the corresponding signal. */ -SCLASS int _thread_dfl_count[NSIG]; +SCLASS int _thread_dfl_count[_SIG_MAXSIG]; /* * Lock for above count of dummy handlers and for the process signal * mask and pending signal sets. */ SCLASS struct lock _thread_signal_lock; /* Pending signals and mask for this process: */ SCLASS sigset_t _thr_proc_sigpending; -SCLASS sigset_t _thr_proc_sigmask SCLASS_PRESET({{0, 0, 0, 0}}); -SCLASS siginfo_t _thr_proc_siginfo[NSIG]; +SCLASS siginfo_t _thr_proc_siginfo[_SIG_MAXSIG]; SCLASS pid_t _thr_pid SCLASS_PRESET(0); /* Garbage collector lock. */ SCLASS struct lock _gc_lock; SCLASS int _gc_check SCLASS_PRESET(0); SCLASS int _gc_count SCLASS_PRESET(0); SCLASS struct lock _mutex_static_lock; SCLASS struct lock _rwlock_static_lock; SCLASS struct lock _keytable_lock; SCLASS struct lock _thread_list_lock; SCLASS int _thr_guard_default; SCLASS int _thr_page_size; - +SCLASS pthread_t _thr_sig_daemon; SCLASS int _thr_debug_flags SCLASS_PRESET(0); /* Undefine the storage class and preset specifiers: */ #undef SCLASS #undef SCLASS_PRESET /* * Function prototype definitions. */ __BEGIN_DECLS int _cond_reinit(pthread_cond_t *); void _cond_wait_backout(struct pthread *); struct pthread *_get_curthread(void); struct kse *_get_curkse(void); void _set_curkse(struct kse *); struct kse *_kse_alloc(struct pthread *); kse_critical_t _kse_critical_enter(void); void _kse_critical_leave(kse_critical_t); int _kse_in_critical(void); void _kse_free(struct pthread *, struct kse *); void _kse_init(); struct kse_group *_kseg_alloc(struct pthread *); void _kse_lock_wait(struct lock *, struct lockuser *lu); void _kse_lock_wakeup(struct lock *, struct lockuser *lu); void _kse_sig_check_pending(struct kse *); void _kse_single_thread(struct pthread *); void _kse_start(struct kse *); int _kse_setthreaded(int); int _kse_isthreaded(void); void _kseg_free(struct kse_group *); int _mutex_cv_lock(pthread_mutex_t *); int _mutex_cv_unlock(pthread_mutex_t *); void _mutex_lock_backout(struct pthread *); void _mutex_notify_priochange(struct pthread *, struct pthread *, int); int _mutex_reinit(struct pthread_mutex *); void _mutex_unlock_private(struct pthread *); void _libpthread_init(struct pthread *); int _pq_alloc(struct pq_queue *, int, int); void _pq_free(struct pq_queue *); int _pq_init(struct pq_queue *); void _pq_remove(struct pq_queue *pq, struct pthread *); void _pq_insert_head(struct pq_queue *pq, struct pthread *); void _pq_insert_tail(struct pq_queue *pq, struct pthread *); struct pthread *_pq_first(struct pq_queue *pq); void *_pthread_getspecific(pthread_key_t); int _pthread_key_create(pthread_key_t *, void (*) (void *)); int _pthread_key_delete(pthread_key_t); int _pthread_mutex_destroy(pthread_mutex_t *); int _pthread_mutex_init(pthread_mutex_t *, const pthread_mutexattr_t *); int _pthread_mutex_lock(pthread_mutex_t *); int _pthread_mutex_trylock(pthread_mutex_t *); int _pthread_mutex_unlock(pthread_mutex_t *); int _pthread_mutexattr_init(pthread_mutexattr_t *); int _pthread_mutexattr_destroy(pthread_mutexattr_t *); int _pthread_mutexattr_settype(pthread_mutexattr_t *, int); int _pthread_once(pthread_once_t *, void (*) (void)); int _pthread_rwlock_init(pthread_rwlock_t *, const pthread_rwlockattr_t *); int _pthread_rwlock_destroy (pthread_rwlock_t *); struct pthread *_pthread_self(void); int _pthread_setspecific(pthread_key_t, const void *); struct pthread *_thr_alloc(struct pthread *); int _thread_enter_uts(struct kse_thr_mailbox *, struct kse_mailbox *); int _thread_switch(struct kse_thr_mailbox *, struct kse_thr_mailbox **); void _thr_exit(char *, int, char *); void _thr_exit_cleanup(void); void _thr_lock_wait(struct lock *lock, struct lockuser *lu); void _thr_lock_wakeup(struct lock *lock, struct lockuser *lu); int _thr_ref_add(struct pthread *, struct pthread *, int); void _thr_ref_delete(struct pthread *, struct pthread *); int _thr_schedule_add(struct pthread *, struct pthread *); void _thr_schedule_remove(struct pthread *, struct pthread *); void _thr_setrunnable(struct pthread *curthread, struct pthread *thread); void _thr_setrunnable_unlocked(struct pthread *thread); void _thr_sig_add(struct pthread *, int, siginfo_t *); void _thr_sig_dispatch(struct kse *, int, siginfo_t *); int _thr_stack_alloc(struct pthread_attr *); void _thr_stack_free(struct pthread_attr *); void _thr_exit_cleanup(void); void _thr_free(struct pthread *, struct pthread *); void _thr_gc(struct pthread *); void _thr_panic_exit(char *, int, char *); void _thread_cleanupspecific(void); void _thread_dump_info(void); void _thread_printf(int, const char *, ...); -void _thr_sched_frame(struct pthread_sigframe *); void _thr_sched_switch(struct pthread *); void _thr_sched_switch_unlocked(struct pthread *); void _thr_set_timeout(const struct timespec *); void _thr_seterrno(struct pthread *, int); void _thr_sig_handler(int, siginfo_t *, ucontext_t *); void _thr_sig_check_pending(struct pthread *); void _thr_sig_rundown(struct pthread *, ucontext_t *, struct pthread_sigframe *); void _thr_sig_send(struct pthread *pthread, int sig); -void _thr_sig_wrapper(void); void _thr_sigframe_restore(struct pthread *thread, struct pthread_sigframe *psf); void _thr_spinlock_init(void); void _thr_enter_cancellation_point(struct pthread *); void _thr_leave_cancellation_point(struct pthread *); int _thr_setconcurrency(int new_level); int _thr_setmaxconcurrency(void); +int _thr_start_sig_daemon(void); +int _thr_getprocsig(int sig, siginfo_t *siginfo); +int _thr_getprocsig_unlocked(int sig, siginfo_t *siginfo); +void _thr_signal_init(void); +void _thr_signal_deinit(void); /* * Aliases for _pthread functions. Should be called instead of * originals if PLT replocation is unwanted at runtme. */ int _thr_cond_broadcast(pthread_cond_t *); int _thr_cond_signal(pthread_cond_t *); int _thr_cond_wait(pthread_cond_t *, pthread_mutex_t *); int _thr_mutex_lock(pthread_mutex_t *); int _thr_mutex_unlock(pthread_mutex_t *); int _thr_rwlock_rdlock (pthread_rwlock_t *); int _thr_rwlock_wrlock (pthread_rwlock_t *); int _thr_rwlock_unlock (pthread_rwlock_t *); /* XXX - Stuff that goes away when my sources get more up to date. */ /* #include */ #ifdef SYS_KSE_H int __sys_kse_create(struct kse_mailbox *, int); int __sys_kse_thr_wakeup(struct kse_mailbox *); int __sys_kse_exit(struct kse_mailbox *); int __sys_kse_release(struct kse_mailbox *); #endif /* #include */ #ifdef _SYS_AIO_H_ int __sys_aio_suspend(const struct aiocb * const[], int, const struct timespec *); #endif /* #include */ #ifdef _SYS_FCNTL_H_ int __sys_fcntl(int, int, ...); int __sys_open(const char *, int, ...); #endif /* #include */ #ifdef _SYS_IOCTL_H_ int __sys_ioctl(int, unsigned long, ...); #endif /* #inclde */ #ifdef _SCHED_H_ int __sys_sched_yield(void); #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 *); #endif /* #include */ #ifdef _SYS_SOCKET_H_ int __sys_sendfile(int, int, off_t, size_t, struct sf_hdtr *, off_t *, int); #endif /* #include */ #ifdef _SYS_UIO_H_ ssize_t __sys_readv(int, const struct iovec *, int); ssize_t __sys_writev(int, const struct iovec *, int); #endif /* #include */ #ifdef _TIME_H_ int __sys_nanosleep(const struct timespec *, struct timespec *); #endif /* #include */ #ifdef _UNISTD_H_ int __sys_close(int); int __sys_execve(const char *, char * const *, char * const *); int __sys_fork(void); int __sys_fsync(int); pid_t __sys_getpid(void); int __sys_select(int, fd_set *, fd_set *, fd_set *, struct timeval *); ssize_t __sys_read(int, void *, size_t); ssize_t __sys_write(int, const void *, size_t); void __sys_exit(int); +int __sys_sigwait(const sigset_t *, int *); +int __sys_sigtimedwait(sigset_t *, siginfo_t *, struct timespec *); #endif /* #include */ #ifdef _SYS_POLL_H_ int __sys_poll(struct pollfd *, unsigned, int); #endif /* #include */ #ifdef _SYS_MMAN_H_ int __sys_msync(void *, size_t, int); #endif #endif /* !_THR_PRIVATE_H */ Index: head/lib/libpthread/thread/thr_sig.c =================================================================== --- head/lib/libpthread/thread/thr_sig.c (revision 116976) +++ head/lib/libpthread/thread/thr_sig.c (revision 116977) @@ -1,912 +1,1010 @@ /* * 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. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include "thr_private.h" #include "pthread_md.h" /* Prototypes: */ static void build_siginfo(siginfo_t *info, int signo); -/* static void thr_sig_add(struct pthread *pthread, int sig, siginfo_t *info); */ static void thr_sig_check_state(struct pthread *pthread, int sig); static struct pthread *thr_sig_find(struct kse *curkse, int sig, siginfo_t *info); static void handle_special_signals(struct kse *curkse, int sig); -static void thr_sigframe_add(struct pthread *thread, int sig, - siginfo_t *info); +static void thr_sigframe_add(struct pthread *thread); static void thr_sigframe_restore(struct pthread *thread, struct pthread_sigframe *psf); static void thr_sigframe_save(struct pthread *thread, struct pthread_sigframe *psf); -static void thr_sig_invoke_handler(struct pthread *, int sig, - siginfo_t *info, ucontext_t *ucp); /* #define DEBUG_SIGNAL */ #ifdef DEBUG_SIGNAL #define DBG_MSG stdout_debug #else #define DBG_MSG(x...) #endif /* * Signal setup and delivery. * * 1) Delivering signals to threads in the same KSE. * These signals are sent by upcall events and are set in the * km_sigscaught field of the KSE mailbox. Since these signals * are received while operating on the KSE stack, they can be * delivered either by using signalcontext() to add a stack frame * to the target thread's stack, or by adding them in the thread's * pending set and having the thread run them down after it * 2) Delivering signals to threads in other KSEs/KSEGs. * 3) Delivering signals to threads in critical regions. * 4) Delivering signals to threads after they change their signal masks. * * Methods of delivering signals. * * 1) Add a signal frame to the thread's saved context. * 2) Add the signal to the thread structure, mark the thread as * having signals to handle, and let the thread run them down * after it resumes from the KSE scheduler. * * Problem with 1). You can't do this to a running thread or a * thread in a critical region. * * Problem with 2). You can't do this to a thread that doesn't * yield in some way (explicitly enters the scheduler). A thread * blocked in the kernel or a CPU hungry thread will not see the * signal without entering the scheduler. * * The solution is to use both 1) and 2) to deliver signals: * * o Thread in critical region - use 2). When the thread * leaves the critical region it will check to see if it * has pending signals and run them down. * * o Thread enters scheduler explicitly - use 2). The thread * can check for pending signals after it returns from the * the scheduler. * * o Thread is running and not current thread - use 2). When the * thread hits a condition specified by one of the other bullets, * the signal will be delivered. * * o Thread is running and is current thread (e.g., the thread * has just changed its signal mask and now sees that it has * pending signals) - just run down the pending signals. * * o Thread is swapped out due to quantum expiration - use 1) * * o Thread is blocked in kernel - kse_thr_wakeup() and then * use 1) */ /* * Rules for selecting threads for signals received: * * 1) If the signal is a sychronous signal, it is delivered to * the generating (current thread). If the thread has the * signal masked, it is added to the threads pending signal * set until the thread unmasks it. * * 2) A thread in sigwait() where the signal is in the thread's * waitset. * * 3) A thread in sigsuspend() where the signal is not in the * thread's suspended signal mask. * * 4) Any thread (first found/easiest to deliver) that has the * signal unmasked. */ +static void * +sig_daemon(void *arg /* Unused */) +{ + int i; + kse_critical_t crit; + struct timespec ts; + sigset_t set; + struct kse *curkse; + struct pthread *curthread = _get_curthread(); + + DBG_MSG("signal daemon started"); + + curthread->name = strdup("signal thread"); + crit = _kse_critical_enter(); + curkse = _get_curkse(); + SIGFILLSET(set); + __sys_sigprocmask(SIG_SETMASK, &set, NULL); + __sys_sigpending(&set); + ts.tv_sec = 0; + ts.tv_nsec = 0; + while (1) { + KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock); + _thr_proc_sigpending = set; + KSE_LOCK_RELEASE(curkse, &_thread_signal_lock); + for (i = 1; i <= _SIG_MAXSIG; i++) { + if (SIGISMEMBER(set, i) != 0) + _thr_sig_dispatch(curkse, i, + NULL /* no siginfo */); + } + ts.tv_sec = 30; + ts.tv_nsec = 0; + curkse->k_mbx.km_flags = + KMF_NOUPCALL | KMF_NOCOMPLETED | KMF_WAITSIGEVENT; + kse_release(&ts); + curkse->k_mbx.km_flags = 0; + set = curkse->k_mbx.km_sigscaught; + } + return (0); +} + +/* Utility function to create signal daemon thread */ +int +_thr_start_sig_daemon(void) +{ + pthread_attr_t attr; + sigset_t sigset, oldset; + + SIGFILLSET(sigset); + pthread_sigmask(SIG_SETMASK, &sigset, &oldset); + pthread_attr_init(&attr); + pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); + /* sigmask will be inherited */ + if (pthread_create(&_thr_sig_daemon, &attr, sig_daemon, NULL)) + PANIC("can not create signal daemon thread!\n"); + pthread_attr_destroy(&attr); + pthread_sigmask(SIG_SETMASK, &oldset, NULL); + return (0); +} + /* * This signal handler only delivers asynchronous signals. * This must be called with upcalls disabled and without * holding any locks. */ void _thr_sig_dispatch(struct kse *curkse, int sig, siginfo_t *info) { struct pthread *thread; DBG_MSG(">>> _thr_sig_dispatch(%d)\n", sig); /* Some signals need special handling: */ handle_special_signals(curkse, sig); - DBG_MSG("dispatch sig:%d\n", sig); while ((thread = thr_sig_find(curkse, sig, info)) != NULL) { /* * Setup the target thread to receive the signal: */ DBG_MSG("Got signal %d, selecting thread %p\n", sig, thread); KSE_SCHED_LOCK(curkse, thread->kseg); if ((thread->state == PS_DEAD) || (thread->state == PS_DEADLOCK) || THR_IS_EXITING(thread) || THR_IS_SUSPENDED(thread)) { KSE_SCHED_UNLOCK(curkse, thread->kseg); _thr_ref_delete(NULL, thread); - } else if (sigismember(&thread->tmbx.tm_context.uc_sigmask, - sig)) { + } else if (SIGISMEMBER(thread->sigmask, sig)) { KSE_SCHED_UNLOCK(curkse, thread->kseg); _thr_ref_delete(NULL, thread); - } - else { + } else { _thr_sig_add(thread, sig, info); KSE_SCHED_UNLOCK(curkse, thread->kseg); _thr_ref_delete(NULL, thread); break; } } + DBG_MSG("<<< _thr_sig_dispatch\n"); } void _thr_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp) { __siginfohandler_t *sigfunc; struct kse *curkse; curkse = _get_curkse(); if ((curkse == NULL) || ((curkse->k_flags & KF_STARTED) == 0)) { /* Upcalls are not yet started; just call the handler. */ sigfunc = _thread_sigact[sig - 1].sa_sigaction; - ucp->uc_sigmask = _thr_proc_sigmask; if (((__sighandler_t *)sigfunc != SIG_DFL) && ((__sighandler_t *)sigfunc != SIG_IGN) && (sigfunc != (__siginfohandler_t *)_thr_sig_handler)) { if (((_thread_sigact[sig - 1].sa_flags & SA_SIGINFO) != 0) || (info == NULL)) (*(sigfunc))(sig, info, ucp); else (*(sigfunc))(sig, (siginfo_t*)(intptr_t)info->si_code, ucp); } } else { /* Nothing. */ DBG_MSG("Got signal %d\n", sig); - sigaddset(&curkse->k_mbx.km_sigscaught, sig); - ucp->uc_sigmask = _thr_proc_sigmask; + /* XXX Bound thread will fall into this... */ } } +/* Must be called with signal lock and schedule lock held in order */ static void thr_sig_invoke_handler(struct pthread *curthread, int sig, siginfo_t *info, ucontext_t *ucp) { void (*sigfunc)(int, siginfo_t *, void *); - sigset_t saved_mask; - int saved_seqno; + sigset_t sigmask; + int sa_flags; + struct sigaction act; + struct kse *curkse; - /* Invoke the signal handler without going through the scheduler: + /* + * Invoke the signal handler without going through the scheduler: */ DBG_MSG("Got signal %d, calling handler for current thread %p\n", sig, curthread); + if (!_kse_in_critical()) + PANIC("thr_sig_invoke_handler without in critical\n"); + curkse = _get_curkse(); /* - * Setup the threads signal mask. - * - * The mask is changed in the thread's active signal mask - * (in the context) and not in the base signal mask because - * a thread is allowed to change its signal mask within a - * signal handler. If it does, the signal mask restored - * after the handler should be the same as that set by the - * thread during the handler, not the original mask from - * before calling the handler. The thread could also - * modify the signal mask in the context and expect this - * mask to be used. - */ - THR_SCHED_LOCK(curthread, curthread); - saved_mask = curthread->tmbx.tm_context.uc_sigmask; - saved_seqno = curthread->sigmask_seqno; - SIGSETOR(curthread->tmbx.tm_context.uc_sigmask, - _thread_sigact[sig - 1].sa_mask); - sigaddset(&curthread->tmbx.tm_context.uc_sigmask, sig); - THR_SCHED_UNLOCK(curthread, curthread); - - /* * Check that a custom handler is installed and if * the signal is not blocked: */ sigfunc = _thread_sigact[sig - 1].sa_sigaction; - ucp->uc_sigmask = _thr_proc_sigmask; + sa_flags = _thread_sigact[sig - 1].sa_flags & SA_SIGINFO; + sigmask = curthread->sigmask; + SIGSETOR(curthread->sigmask, _thread_sigact[sig - 1].sa_mask); + if (!(sa_flags & (SA_NODEFER | SA_RESETHAND))) + SIGADDSET(curthread->sigmask, sig); + if ((sig != SIGILL) && (sa_flags & SA_RESETHAND)) { + if (_thread_dfl_count[sig - 1] == 0) { + act.sa_handler = SIG_DFL; + act.sa_flags = SA_RESTART; + SIGEMPTYSET(act.sa_mask); + __sys_sigaction(sig, &act, NULL); + __sys_sigaction(sig, NULL, &_thread_sigact[sig - 1]); + } + } + KSE_LOCK_RELEASE(curkse, &_thread_signal_lock); + KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); + _kse_critical_leave(&curthread->tmbx); + ucp->uc_sigmask = sigmask; + if (((__sighandler_t *)sigfunc != SIG_DFL) && ((__sighandler_t *)sigfunc != SIG_IGN)) { - if (((_thread_sigact[sig - 1].sa_flags & SA_SIGINFO) != 0) || - (info == NULL)) + if ((sa_flags & SA_SIGINFO) != 0 || info == NULL) (*(sigfunc))(sig, info, ucp); else (*(sigfunc))(sig, (siginfo_t*)(intptr_t)info->si_code, ucp); + } else { + /* XXX + * TODO: exit process if signal would kill it. + */ +#ifdef NOTYET + if (sigprop(sig) & SA_KILL) + kse_sigexit(sig); +#endif } - + _kse_critical_enter(); + /* Don't trust after critical leave/enter */ + curkse = _get_curkse(); + KSE_SCHED_LOCK(curkse, curkse->k_kseg); + KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock); /* * Restore the thread's signal mask. */ - if (saved_seqno == curthread->sigmask_seqno) - curthread->tmbx.tm_context.uc_sigmask = saved_mask; - else - curthread->tmbx.tm_context.uc_sigmask = curthread->sigmask; + curthread->sigmask = ucp->uc_sigmask; + + DBG_MSG("Got signal %d, handler returned %p\n", sig, curthread); } +int +_thr_getprocsig(int sig, siginfo_t *siginfo) +{ + kse_critical_t crit; + struct kse *curkse; + int ret; + + DBG_MSG(">>> _thr_getprocsig\n"); + + crit = _kse_critical_enter(); + curkse = _get_curkse(); + KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock); + ret = _thr_getprocsig_unlocked(sig, siginfo); + KSE_LOCK_RELEASE(curkse, &_thread_signal_lock); + _kse_critical_leave(crit); + + DBG_MSG("<<< _thr_getprocsig\n"); + return (ret); +} + +int +_thr_getprocsig_unlocked(int sig, siginfo_t *siginfo) +{ + sigset_t sigset; + struct timespec ts; + + /* try to retrieve signal from kernel */ + SIGEMPTYSET(sigset); + SIGADDSET(sigset, sig); + ts.tv_sec = 0; + ts.tv_nsec = 0; + if (__sys_sigtimedwait(&sigset, siginfo, &ts) > 0) { + SIGDELSET(_thr_proc_sigpending, sig); + return (sig); + } + return (0); +} + /* * Find a thread that can handle the signal. This must be called * with upcalls disabled. */ struct pthread * thr_sig_find(struct kse *curkse, int sig, siginfo_t *info) { - int handler_installed; struct pthread *pthread; struct pthread *suspended_thread, *signaled_thread; + siginfo_t si; DBG_MSG("Looking for thread to handle signal %d\n", sig); - handler_installed = (_thread_sigact[sig - 1].sa_handler != SIG_IGN) && - (_thread_sigact[sig - 1].sa_handler != SIG_DFL); - /* Check if the signal requires a dump of thread information: */ if (sig == SIGINFO) { /* Dump thread information to file: */ _thread_dump_info(); } /* * Enter a loop to look for threads that have the signal * unmasked. POSIX specifies that a thread in a sigwait * will get the signal over any other threads. Second * preference will be threads in in a sigsuspend. Third * preference will be the current thread. If none of the * above, then the signal is delivered to the first thread * that is found. Note that if a custom handler is not * installed, the signal only affects threads in sigwait. */ suspended_thread = NULL; signaled_thread = NULL; KSE_LOCK_ACQUIRE(curkse, &_thread_list_lock); TAILQ_FOREACH(pthread, &_thread_list, tle) { + if (pthread == _thr_sig_daemon) + continue; +#ifdef NOTYET + /* Signal delivering to bound thread is done by kernel */ + if (pthread->attr.flags & PTHREAD_SCOPE_SYSTEM) + continue; +#endif + /* Take the scheduling lock. */ KSE_SCHED_LOCK(curkse, pthread->kseg); - if ((pthread->state == PS_SIGWAIT) && - sigismember(pthread->data.sigwait, sig)) { + if ((pthread->state == PS_DEAD) || + (pthread->state == PS_DEADLOCK) || + THR_IS_EXITING(pthread) || + THR_IS_SUSPENDED(pthread) || + SIGISMEMBER(pthread->sigmask, sig)) { + ; /* Skip this thread. */ + } else if (pthread->state == PS_SIGWAIT) { /* - * Return the signal number and make the - * thread runnable. + * retrieve signal from kernel, if it is job control + * signal, and sigaction is SIG_DFL, then we will + * be stopped in kernel, we hold lock here, but that + * does not matter, because that's job control, and + * whole process should be stopped. */ - pthread->signo = sig; - _thr_setrunnable_unlocked(pthread); - + if (_thr_getprocsig(sig, &si)) { + DBG_MSG("Waking thread %p in sigwait" + " with signal %d\n", pthread, sig); + /* where to put siginfo ? */ + *(pthread->data.sigwaitinfo) = si; + pthread->sigmask = pthread->oldsigmask; + _thr_setrunnable_unlocked(pthread); + } KSE_SCHED_UNLOCK(curkse, pthread->kseg); - /* * POSIX doesn't doesn't specify which thread * will get the signal if there are multiple * waiters, so we give it to the first thread * we find. * * Do not attempt to deliver this signal * to other threads and do not add the signal * to the process pending set. */ KSE_LOCK_RELEASE(curkse, &_thread_list_lock); - DBG_MSG("Waking thread %p in sigwait with signal %d\n", - pthread, sig); return (NULL); - } - else if ((pthread->state == PS_DEAD) || - (pthread->state == PS_DEADLOCK) || - THR_IS_EXITING(pthread) || THR_IS_SUSPENDED(pthread)) - ; /* Skip this thread. */ - else if ((handler_installed != 0) && - !sigismember(&pthread->tmbx.tm_context.uc_sigmask, sig)) { + } else { if (pthread->state == PS_SIGSUSPEND) { if (suspended_thread == NULL) { suspended_thread = pthread; suspended_thread->refcount++; } } else if (signaled_thread == NULL) { signaled_thread = pthread; signaled_thread->refcount++; - } + } } KSE_SCHED_UNLOCK(curkse, pthread->kseg); } KSE_LOCK_RELEASE(curkse, &_thread_list_lock); - /* - * Only perform wakeups and signal delivery if there is a - * custom handler installed: - */ - if (handler_installed == 0) { - /* - * There is no handler installed; nothing to do here. - */ - } else if (suspended_thread == NULL && - signaled_thread == NULL) { - /* - * Add it to the set of signals pending - * on the process: - */ - KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock); - if (!sigismember(&_thr_proc_sigpending, sig)) { - sigaddset(&_thr_proc_sigpending, sig); - if (info == NULL) - build_siginfo(&_thr_proc_siginfo[sig], sig); - else - memcpy(&_thr_proc_siginfo[sig], info, - sizeof(*info)); - } - KSE_LOCK_RELEASE(curkse, &_thread_signal_lock); - } else { - /* - * We only deliver the signal to one thread; - * give preference to the suspended thread: - */ - if (suspended_thread != NULL) { - pthread = suspended_thread; + if (suspended_thread != NULL) { + pthread = suspended_thread; + if (signaled_thread) _thr_ref_delete(NULL, signaled_thread); - } else - pthread = signaled_thread; - return (pthread); + } else if (signaled_thread) { + pthread = signaled_thread; + } else { + pthread = NULL; } - return (NULL); + return (pthread); } static void build_siginfo(siginfo_t *info, int signo) { bzero(info, sizeof(*info)); info->si_signo = signo; info->si_pid = _thr_pid; } /* * This is called by a thread when it has pending signals to deliver. * It should only be called from the context of the thread. */ void _thr_sig_rundown(struct pthread *curthread, ucontext_t *ucp, struct pthread_sigframe *psf) { - struct pthread_sigframe psf_save; - sigset_t sigset; + siginfo_t siginfo; int i; + kse_critical_t crit; + struct kse *curkse; - THR_SCHED_LOCK(curthread, curthread); - memcpy(&sigset, &curthread->sigpend, sizeof(sigset)); - sigemptyset(&curthread->sigpend); - if (psf != NULL) { - memcpy(&psf_save, psf, sizeof(*psf)); - SIGSETOR(sigset, psf_save.psf_sigset); - sigemptyset(&psf->psf_sigset); - } - THR_SCHED_UNLOCK(curthread, curthread); - + DBG_MSG(">>> thr_sig_rundown %p\n", curthread); /* Check the threads previous state: */ - if ((psf != NULL) && (psf_save.psf_state != PS_RUNNING)) { + if ((psf != NULL) && (psf->psf_valid != 0)) { /* * Do a little cleanup handling for those threads in * queues before calling the signal handler. Signals * for these threads are temporarily blocked until * after cleanup handling. */ - switch (psf_save.psf_state) { + switch (psf->psf_state) { case PS_COND_WAIT: _cond_wait_backout(curthread); - psf_save.psf_state = PS_RUNNING; + psf->psf_state = PS_RUNNING; break; case PS_MUTEX_WAIT: _mutex_lock_backout(curthread); - psf_save.psf_state = PS_RUNNING; + psf->psf_state = PS_RUNNING; break; + case PS_RUNNING: + break; + default: + psf->psf_state = PS_RUNNING; break; } + /* XXX see comment in thr_sched_switch_unlocked */ + curthread->critical_count--; } + /* * Lower the priority before calling the handler in case * it never returns (longjmps back): */ + crit = _kse_critical_enter(); + curkse = _get_curkse(); + KSE_SCHED_LOCK(curkse, curkse->k_kseg); + KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock); curthread->active_priority &= ~THR_SIGNAL_PRIORITY; - for (i = 1; i < NSIG; i++) { - if (sigismember(&sigset, i) != 0) { - /* Call the handler: */ - thr_sig_invoke_handler(curthread, i, - &curthread->siginfo[i], ucp); + while (1) { + for (i = 1; i <= _SIG_MAXSIG; i++) { + if (SIGISMEMBER(curthread->sigmask, i)) + continue; + if (SIGISMEMBER(curthread->sigpend, i)) { + SIGDELSET(curthread->sigpend, i); + siginfo = curthread->siginfo[i]; + break; + } + if (SIGISMEMBER(_thr_proc_sigpending, i)) { + if (_thr_getprocsig_unlocked(i, &siginfo)) + break; + } } + if (i <= _SIG_MAXSIG) + thr_sig_invoke_handler(curthread, i, &siginfo, ucp); + else + break; } - THR_SCHED_LOCK(curthread, curthread); - if (psf != NULL) - thr_sigframe_restore(curthread, &psf_save); - /* Restore the signal mask. */ - curthread->tmbx.tm_context.uc_sigmask = curthread->sigmask; - THR_SCHED_UNLOCK(curthread, curthread); - _thr_sig_check_pending(curthread); + if (psf != NULL && psf->psf_valid != 0) + thr_sigframe_restore(curthread, psf); + curkse = _get_curkse(); + KSE_LOCK_RELEASE(curkse, &_thread_signal_lock); + KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); + _kse_critical_leave(&curthread->tmbx); + + DBG_MSG("<<< thr_sig_rundown %p\n", curthread); } /* * This checks pending signals for the current thread. It should be * called whenever a thread changes its signal mask. Note that this * is called from a thread (using its stack). * * XXX - We might want to just check to see if there are pending * signals for the thread here, but enter the UTS scheduler * to actually install the signal handler(s). */ void _thr_sig_check_pending(struct pthread *curthread) { - sigset_t sigset; - sigset_t pending_process; - sigset_t pending_thread; - kse_critical_t crit; - int i; + ucontext_t uc; + volatile int once; - curthread->check_pending = 0; + if (THR_IN_CRITICAL(curthread)) + return; - /* - * Check if there are pending signals for the running - * thread or process that aren't blocked: - */ - crit = _kse_critical_enter(); - KSE_LOCK_ACQUIRE(curthread->kse, &_thread_signal_lock); - sigset = _thr_proc_sigpending; - KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock); - _kse_critical_leave(crit); - - THR_SCHED_LOCK(curthread, curthread); - SIGSETOR(sigset, curthread->sigpend); - SIGSETNAND(sigset, curthread->tmbx.tm_context.uc_sigmask); - if (SIGNOTEMPTY(sigset)) { - ucontext_t uc; - volatile int once; - + once = 0; + THR_GETCONTEXT(&uc); + if (once == 0) { + once = 1; curthread->check_pending = 0; - THR_SCHED_UNLOCK(curthread, curthread); - - /* - * Split the pending signals into those that were - * pending on the process and those that were pending - * on the thread. - */ - sigfillset(&pending_process); - sigfillset(&pending_thread); - for (i = 1; i < NSIG; i++) { - if (sigismember(&sigset, i) != 0) { - if (sigismember(&curthread->sigpend, i) != 0) { - build_siginfo(&curthread->siginfo[i], i); - sigdelset(&pending_thread, i); - } else { - memcpy(&curthread->siginfo[i], - &_thr_proc_siginfo[i], - sizeof(siginfo_t)); - sigdelset(&pending_process, i); - } - } - } - /* - * Remove any process pending signals that were scheduled - * to be delivered from process' pending set. - */ - crit = _kse_critical_enter(); - KSE_LOCK_ACQUIRE(curthread->kse, &_thread_signal_lock); - SIGSETAND(_thr_proc_sigpending, pending_process); - KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock); - _kse_critical_leave(crit); - - /* - * Remove any thread pending signals that were scheduled - * to be delivered from thread's pending set. - */ - THR_SCHED_LOCK(curthread, curthread); - SIGSETAND(curthread->sigpend, pending_thread); - THR_SCHED_UNLOCK(curthread, curthread); - - once = 0; - THR_GETCONTEXT(&uc); - if (once == 0) { - once = 1; - for (i = 1; i < NSIG; i++) { - if (sigismember(&sigset, i) != 0) { - /* Call the handler: */ - thr_sig_invoke_handler(curthread, i, - &curthread->siginfo[i], &uc); - } - } - } + _thr_sig_rundown(curthread, &uc, NULL); } - else - THR_SCHED_UNLOCK(curthread, curthread); } /* * This must be called with upcalls disabled. */ static void handle_special_signals(struct kse *curkse, int sig) { switch (sig) { /* * POSIX says that pending SIGCONT signals are * discarded when one of these signals occurs. */ case SIGTSTP: case SIGTTIN: case SIGTTOU: KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock); - sigdelset(&_thr_proc_sigpending, SIGCONT); + SIGDELSET(_thr_proc_sigpending, SIGCONT); KSE_LOCK_RELEASE(curkse, &_thread_signal_lock); break; - + case SIGCONT: + KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock); + SIGDELSET(_thr_proc_sigpending, SIGTSTP); + SIGDELSET(_thr_proc_sigpending, SIGTTIN); + SIGDELSET(_thr_proc_sigpending, SIGTTOU); + KSE_LOCK_RELEASE(curkse, &_thread_signal_lock); default: break; } } /* * Perform thread specific actions in response to a signal. * This function is only called if there is a handler installed * for the signal, and if the target thread has the signal * unmasked. * * This must be called with the thread's scheduling lock held. */ void _thr_sig_add(struct pthread *pthread, int sig, siginfo_t *info) { int restart; int suppress_handler = 0; + int fromproc = 0; + struct pthread *curthread = _get_curthread(); + struct kse *curkse; + siginfo_t siginfo; - if (pthread->curframe == NULL) { - /* - * This thread is active. Just add it to the - * thread's pending set. - */ - sigaddset(&pthread->sigpend, sig); - pthread->check_pending = 1; - if (info == NULL) - build_siginfo(&pthread->siginfo[sig], sig); - else if (info != &pthread->siginfo[sig]) - memcpy(&pthread->siginfo[sig], info, - sizeof(*info)); - if ((pthread->blocked != 0) && !THR_IN_CRITICAL(pthread)) - kse_thr_interrupt(&pthread->tmbx /* XXX - restart?!?! */); - } - else { - restart = _thread_sigact[sig - 1].sa_flags & SA_RESTART; + DBG_MSG(">>> _thr_sig_add\n"); - /* Make sure this signal isn't still in the pending set: */ - sigdelset(&pthread->sigpend, sig); + curkse = _get_curkse(); + restart = _thread_sigact[sig - 1].sa_flags & SA_RESTART; + fromproc = (curthread == _thr_sig_daemon); + if (pthread->state == PS_DEAD || pthread->state == PS_DEADLOCK || + pthread->state == PS_STATE_MAX) + return; /* return false */ + +#ifdef NOTYET + if ((pthread->attrs.flags & PTHREAD_SCOPE_SYSTEM) != 0) { + if (!fromproc) + kse_thr_interrupt(&pthread->tmbx, 0, sig); + return; + } +#endif + + if (pthread->curframe == NULL || + SIGISMEMBER(pthread->sigmask, sig) || + THR_IN_CRITICAL(pthread)) { + /* thread is running or signal was being masked */ + if (!fromproc) { + SIGADDSET(pthread->sigpend, sig); + if (info == NULL) + build_siginfo(&pthread->siginfo[sig], sig); + else if (info != &pthread->siginfo[sig]) + memcpy(&pthread->siginfo[sig], info, + sizeof(*info)); + } else { + if (!_thr_getprocsig(sig, &pthread->siginfo[sig])) + return; + SIGADDSET(pthread->sigpend, sig); + } + if (!SIGISMEMBER(pthread->sigmask, sig)) { + pthread->check_pending = 1; + if (pthread->blocked != 0 && !THR_IN_CRITICAL(pthread)) + kse_thr_interrupt(&pthread->tmbx, + restart ? -2 : -1); + } + } + else { + /* if process signal not exists, just return */ + if (fromproc) { + if (!_thr_getprocsig(sig, &siginfo)) + return; + info = &siginfo; + } /* * Process according to thread state: */ switch (pthread->state) { - /* - * States which do not change when a signal is trapped: - */ case PS_DEAD: case PS_DEADLOCK: + case PS_STATE_MAX: + return; /* XXX return false */ case PS_LOCKWAIT: case PS_SUSPENDED: - case PS_STATE_MAX: /* * You can't call a signal handler for threads in these * states. */ suppress_handler = 1; break; - - /* - * States which do not need any cleanup handling when signals - * occur: - */ case PS_RUNNING: - /* - * Remove the thread from the queue before changing its - * priority: - */ - if ((pthread->flags & THR_FLAGS_IN_RUNQ) != 0) + if ((pthread->flags & THR_FLAGS_IN_RUNQ)) { THR_RUNQ_REMOVE(pthread); + pthread->active_priority |= THR_SIGNAL_PRIORITY; + THR_RUNQ_INSERT_TAIL(pthread); + } else { + /* Possible not in RUNQ and has curframe ? */ + pthread->active_priority |= THR_SIGNAL_PRIORITY; + } + suppress_handler = 1; break; - /* * States which cannot be interrupted but still require the * signal handler to run: */ case PS_COND_WAIT: case PS_MUTEX_WAIT: - /* - * Remove the thread from the wait queue. It will - * be added back to the wait queue once all signal - * handlers have been invoked. - */ - KSE_WAITQ_REMOVE(pthread->kse, pthread); break; case PS_SLEEP_WAIT: /* * Unmasked signals always cause sleep to terminate * early regardless of SA_RESTART: */ pthread->interrupted = 1; - KSE_WAITQ_REMOVE(pthread->kse, pthread); - THR_SET_STATE(pthread, PS_RUNNING); break; case PS_JOIN: + break; + case PS_SIGSUSPEND: - KSE_WAITQ_REMOVE(pthread->kse, pthread); - THR_SET_STATE(pthread, PS_RUNNING); + pthread->interrupted = 1; break; case PS_SIGWAIT: + if (info == NULL) + build_siginfo(&pthread->siginfo[sig], sig); + else if (info != &pthread->siginfo[sig]) + memcpy(&pthread->siginfo[sig], info, + sizeof(*info)); /* * The signal handler is not called for threads in * SIGWAIT. */ suppress_handler = 1; - /* Wake up the thread if the signal is blocked. */ - if (sigismember(pthread->data.sigwait, sig)) { + /* Wake up the thread if the signal is not blocked. */ + if (!SIGISMEMBER(pthread->sigmask, sig)) { /* Return the signal number: */ - pthread->signo = sig; - + *(pthread->data.sigwaitinfo) = pthread->siginfo[sig]; + pthread->sigmask = pthread->oldsigmask; /* Make the thread runnable: */ _thr_setrunnable_unlocked(pthread); - } else + } else { /* Increment the pending signal count. */ - sigaddset(&pthread->sigpend, sig); - break; + SIGADDSET(pthread->sigpend, sig); + pthread->check_pending = 1; + } + + return; } + SIGADDSET(pthread->sigpend, sig); + if (info == NULL) + build_siginfo(&pthread->siginfo[sig], sig); + else if (info != &pthread->siginfo[sig]) + memcpy(&pthread->siginfo[sig], info, sizeof(*info)); + if (suppress_handler == 0) { /* * Setup a signal frame and save the current threads * state: */ - thr_sigframe_add(pthread, sig, info); - - if (pthread->state != PS_RUNNING) - THR_SET_STATE(pthread, PS_RUNNING); - - /* - * The thread should be removed from all scheduling - * queues at this point. Raise the priority and - * place the thread in the run queue. It is also - * possible for a signal to be sent to a suspended - * thread, mostly via pthread_kill(). If a thread - * is suspended, don't insert it into the priority - * queue; just set its state to suspended and it - * will run the signal handler when it is resumed. - */ + thr_sigframe_add(pthread); + if (pthread->flags & THR_FLAGS_IN_RUNQ) + THR_RUNQ_REMOVE(pthread); pthread->active_priority |= THR_SIGNAL_PRIORITY; - if ((pthread->flags & THR_FLAGS_IN_RUNQ) == 0) - THR_RUNQ_INSERT_TAIL(pthread); + _thr_setrunnable_unlocked(pthread); + } else { + pthread->check_pending = 1; } } + + DBG_MSG("<<< _thr_sig_add\n"); } static void thr_sig_check_state(struct pthread *pthread, int sig) { /* * Process according to thread state: */ switch (pthread->state) { /* * States which do not change when a signal is trapped: */ case PS_RUNNING: case PS_LOCKWAIT: case PS_MUTEX_WAIT: case PS_COND_WAIT: case PS_JOIN: case PS_SUSPENDED: case PS_DEAD: case PS_DEADLOCK: case PS_STATE_MAX: break; case PS_SIGWAIT: + build_siginfo(&pthread->siginfo[sig], sig); /* Wake up the thread if the signal is blocked. */ - if (sigismember(pthread->data.sigwait, sig)) { + if (!SIGISMEMBER(pthread->sigmask, sig)) { /* Return the signal number: */ - pthread->signo = sig; - + *(pthread->data.sigwaitinfo) = pthread->siginfo[sig]; + pthread->sigmask = pthread->oldsigmask; /* Change the state of the thread to run: */ _thr_setrunnable_unlocked(pthread); - } else + } else { /* Increment the pending signal count. */ - sigaddset(&pthread->sigpend, sig); + SIGADDSET(pthread->sigpend, sig); + pthread->check_pending = 1; + } break; case PS_SIGSUSPEND: case PS_SLEEP_WAIT: /* * Remove the thread from the wait queue and make it * runnable: */ _thr_setrunnable_unlocked(pthread); /* Flag the operation as interrupted: */ pthread->interrupted = 1; break; } } /* * Send a signal to a specific thread (ala pthread_kill): */ void _thr_sig_send(struct pthread *pthread, int sig) { struct pthread *curthread = _get_curthread(); +#ifdef NOTYET + if ((pthread->attr.flags & PTHREAD_SCOPE_SYSTEM) == 0) { + kse_thr_interrupt(&pthread->tmbx, sig); + return; + } +#endif /* Lock the scheduling queue of the target thread. */ THR_SCHED_LOCK(curthread, pthread); /* Check for signals whose actions are SIG_DFL: */ if (_thread_sigact[sig - 1].sa_handler == SIG_DFL) { /* * Check to see if a temporary signal handler is * installed for sigwaiters: */ - if (_thread_dfl_count[sig] == 0) { + if (_thread_dfl_count[sig - 1] == 0) { /* * Deliver the signal to the process if a handler * is not installed: */ THR_SCHED_UNLOCK(curthread, pthread); kill(getpid(), sig); THR_SCHED_LOCK(curthread, pthread); } /* * Assuming we're still running after the above kill(), * make any necessary state changes to the thread: */ thr_sig_check_state(pthread, sig); THR_SCHED_UNLOCK(curthread, pthread); } /* * Check that the signal is not being ignored: */ else if (_thread_sigact[sig - 1].sa_handler != SIG_IGN) { - if (pthread->state == PS_SIGWAIT && - sigismember(pthread->data.sigwait, sig)) { - /* Return the signal number: */ - pthread->signo = sig; - - /* Change the state of the thread to run: */ - _thr_setrunnable_unlocked(pthread); - THR_SCHED_UNLOCK(curthread, pthread); - } else if (sigismember(&pthread->tmbx.tm_context.uc_sigmask, sig)) { - /* Add the signal to the pending set: */ - sigaddset(&pthread->sigpend, sig); - THR_SCHED_UNLOCK(curthread, pthread); - } else if (pthread == curthread) { - ucontext_t uc; - siginfo_t info; - volatile int once; - - THR_SCHED_UNLOCK(curthread, pthread); - build_siginfo(&info, sig); - once = 0; - THR_GETCONTEXT(&uc); - if (once == 0) { - once = 1; - /* - * Call the signal handler for the current - * thread: - */ - thr_sig_invoke_handler(curthread, sig, - &info, &uc); - } - } else { - /* - * Perform any state changes due to signal - * arrival: - */ - _thr_sig_add(pthread, sig, NULL); - THR_SCHED_UNLOCK(curthread, pthread); - } + _thr_sig_add(pthread, sig, NULL); + THR_SCHED_UNLOCK(curthread, pthread); + /* XXX + * If thread sent signal to itself, check signals now. + * It is not really needed, _kse_critical_leave should + * have already checked signals. + */ + if (pthread == curthread && curthread->check_pending) + _thr_sig_check_pending(curthread); + } else { + THR_SCHED_UNLOCK(curthread, pthread); } } static void -thr_sigframe_add(struct pthread *thread, int sig, siginfo_t *info) +thr_sigframe_add(struct pthread *thread) { if (thread->curframe == NULL) PANIC("Thread doesn't have signal frame "); - if (thread->have_signals == 0) { + if (thread->curframe->psf_valid == 0) { + thread->curframe->psf_valid = 1; /* * Multiple signals can be added to the same signal * frame. Only save the thread's state the first time. */ thr_sigframe_save(thread, thread->curframe); - thread->have_signals = 1; - thread->flags &= THR_FLAGS_PRIVATE; } - sigaddset(&thread->curframe->psf_sigset, sig); - if (info == NULL) - build_siginfo(&thread->siginfo[sig], sig); - else if (info != &thread->siginfo[sig]) - memcpy(&thread->siginfo[sig], info, sizeof(*info)); - - /* Setup the new signal mask. */ - SIGSETOR(thread->tmbx.tm_context.uc_sigmask, - _thread_sigact[sig - 1].sa_mask); - sigaddset(&thread->tmbx.tm_context.uc_sigmask, sig); } -void +static void thr_sigframe_restore(struct pthread *thread, struct pthread_sigframe *psf) { + if (psf->psf_valid == 0) + PANIC("invalid pthread_sigframe\n"); thread->flags = psf->psf_flags; thread->interrupted = psf->psf_interrupted; thread->signo = psf->psf_signo; thread->state = psf->psf_state; thread->data = psf->psf_wait_data; thread->wakeup_time = psf->psf_wakeup_time; - if (thread->sigmask_seqno == psf->psf_seqno) - thread->tmbx.tm_context.uc_sigmask = psf->psf_sigmask; - else - thread->tmbx.tm_context.uc_sigmask = thread->sigmask; } static void thr_sigframe_save(struct pthread *thread, struct pthread_sigframe *psf) { /* This has to initialize all members of the sigframe. */ psf->psf_flags = thread->flags & (THR_FLAGS_PRIVATE | THR_FLAGS_IN_TDLIST); psf->psf_interrupted = thread->interrupted; psf->psf_signo = thread->signo; psf->psf_state = thread->state; psf->psf_wait_data = thread->data; psf->psf_wakeup_time = thread->wakeup_time; - psf->psf_sigmask = thread->tmbx.tm_context.uc_sigmask; - psf->psf_seqno = thread->sigmask_seqno; - sigemptyset(&psf->psf_sigset); } + +void +_thr_signal_init(void) +{ + sigset_t sigset; + struct sigaction act; + int i; + + SIGFILLSET(sigset); + __sys_sigprocmask(SIG_SETMASK, &sigset, &_thr_initial->sigmask); + /* Enter a loop to get the existing signal status: */ + for (i = 1; i <= _SIG_MAXSIG; i++) { + /* Check for signals which cannot be trapped: */ + if (i == SIGKILL || i == SIGSTOP) { + } + + /* Get the signal handler details: */ + else if (__sys_sigaction(i, NULL, + &_thread_sigact[i - 1]) != 0) { + /* + * Abort this process if signal + * initialisation fails: + */ + PANIC("Cannot read signal handler info"); + } + } + /* + * Install the signal handler for SIGINFO. It isn't + * really needed, but it is nice to have for debugging + * purposes. + */ + _thread_sigact[SIGINFO - 1].sa_flags = SA_SIGINFO | SA_RESTART; + SIGEMPTYSET(act.sa_mask); + act.sa_flags = SA_SIGINFO | SA_RESTART; + act.sa_sigaction = (__siginfohandler_t *)&_thr_sig_handler; + if (__sys_sigaction(SIGINFO, &act, NULL) != 0) { + /* + * Abort this process if signal initialisation fails: + */ + PANIC("Cannot initialize signal handler"); + } +} + +void +_thr_signal_deinit(void) +{ + sigset_t tmpmask, oldmask; + int i; + + SIGFILLSET(tmpmask); + SIG_CANTMASK(tmpmask); + __sys_sigprocmask(SIG_SETMASK, &tmpmask, &oldmask); + /* Enter a loop to get the existing signal status: */ + for (i = 1; i <= _SIG_MAXSIG; i++) { + /* Check for signals which cannot be trapped: */ + if (i == SIGKILL || i == SIGSTOP) { + } + + /* Set the signal handler details: */ + else if (__sys_sigaction(i, &_thread_sigact[i - 1], NULL) != 0) { + /* + * Abort this process if signal + * initialisation fails: + */ + PANIC("Cannot set signal handler info"); + } + } + __sys_sigprocmask(SIG_SETMASK, &oldmask, NULL); +} + Index: head/lib/libpthread/thread/thr_sigaction.c =================================================================== --- head/lib/libpthread/thread/thr_sigaction.c (revision 116976) +++ head/lib/libpthread/thread/thr_sigaction.c (revision 116977) @@ -1,106 +1,113 @@ /* * 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. * * $FreeBSD$ */ #include #include #include #include "thr_private.h" __weak_reference(_sigaction, sigaction); int _sigaction(int sig, const struct sigaction * act, struct sigaction * oact) { int ret = 0; struct sigaction gact; + struct pthread *curthread; + kse_critical_t crit; /* Check if the signal number is out of range: */ - if (sig < 1 || sig > NSIG) { + if (sig < 1 || sig > _SIG_MAXSIG) { /* Return an invalid argument: */ errno = EINVAL; ret = -1; } else { - if (_thr_initial == NULL) - _libpthread_init(NULL); + if (!_kse_isthreaded()) + return __sys_sigaction(sig, act, oact); + crit = _kse_critical_enter(); + curthread = _get_curthread(); + KSE_LOCK_ACQUIRE(curthread->kse, &_thread_signal_lock); /* * Check if the existing signal action structure contents are * to be returned: */ if (oact != NULL) { /* Return the existing signal action contents: */ oact->sa_handler = _thread_sigact[sig - 1].sa_handler; oact->sa_mask = _thread_sigact[sig - 1].sa_mask; oact->sa_flags = _thread_sigact[sig - 1].sa_flags; } /* Check if a signal action was supplied: */ if (act != NULL) { /* Set the new signal handler: */ _thread_sigact[sig - 1].sa_mask = act->sa_mask; _thread_sigact[sig - 1].sa_flags = act->sa_flags; _thread_sigact[sig - 1].sa_handler = act->sa_handler; } /* * Check if the kernel needs to be advised of a change * in signal action: */ if (act != NULL && sig != SIGINFO) { gact.sa_mask = act->sa_mask; gact.sa_flags = SA_SIGINFO | act->sa_flags; /* * Check if the signal handler is being set to * the default or ignore handlers: */ if (act->sa_handler == SIG_DFL || act->sa_handler == SIG_IGN) /* Specify the built in handler: */ gact.sa_handler = act->sa_handler; else /* * Specify the thread kernel signal * handler: */ gact.sa_handler = (void (*) ())_thr_sig_handler; /* Change the signal action in the kernel: */ if (__sys_sigaction(sig, &gact, NULL) != 0) ret = -1; } + KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock); + _kse_critical_leave(crit); } /* Return the completion status: */ return (ret); } Index: head/lib/libpthread/thread/thr_sigmask.c =================================================================== --- head/lib/libpthread/thread/thr_sigmask.c (revision 116976) +++ head/lib/libpthread/thread/thr_sigmask.c (revision 116977) @@ -1,103 +1,100 @@ /* * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by John Birrell. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include "thr_private.h" __weak_reference(_pthread_sigmask, pthread_sigmask); int _pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) { struct pthread *curthread = _get_curthread(); int ret; + if (! _kse_isthreaded()) + _kse_setthreaded(1); + + THR_SCHED_LOCK(curthread, curthread); ret = 0; if (oset != NULL) /* Return the current mask: */ - *oset = curthread->tmbx.tm_context.uc_sigmask; + *oset = curthread->sigmask; /* Check if a new signal set was provided by the caller: */ if (set != NULL) { - THR_SCHED_LOCK(curthread, curthread); - /* Process according to what to do: */ switch (how) { /* Block signals: */ case SIG_BLOCK: /* Add signals to the existing mask: */ - SIGSETOR(curthread->tmbx.tm_context.uc_sigmask, *set); + SIGSETOR(curthread->sigmask, *set); break; /* Unblock signals: */ case SIG_UNBLOCK: /* Clear signals from the existing mask: */ - SIGSETNAND(curthread->tmbx.tm_context.uc_sigmask, *set); + SIGSETNAND(curthread->sigmask, *set); break; /* Set the signal process mask: */ case SIG_SETMASK: /* Set the new mask: */ - curthread->tmbx.tm_context.uc_sigmask = *set; + curthread->sigmask = *set; break; /* Trap invalid actions: */ default: /* Return an invalid argument: */ errno = EINVAL; ret = -1; break; } - - if (ret == 0) { - curthread->sigmask = - curthread->tmbx.tm_context.uc_sigmask; - curthread->sigmask_seqno++; - } - + SIG_CANTMASK(curthread->sigmask); THR_SCHED_UNLOCK(curthread, curthread); /* * Run down any pending signals: */ if (ret == 0) _thr_sig_check_pending(curthread); - } + } else + THR_SCHED_UNLOCK(curthread, curthread); return (ret); } Index: head/lib/libpthread/thread/thr_sigpending.c =================================================================== --- head/lib/libpthread/thread/thr_sigpending.c (revision 116976) +++ head/lib/libpthread/thread/thr_sigpending.c (revision 116977) @@ -1,66 +1,71 @@ /* * Copyright (c) 1999 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. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by John Birrell. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include "thr_private.h" __weak_reference(_sigpending, sigpending); int _sigpending(sigset_t *set) { struct pthread *curthread = _get_curthread(); kse_critical_t crit; int ret = 0; /* Check for a null signal set pointer: */ if (set == NULL) { /* Return an invalid argument: */ ret = EINVAL; } else { - *set = curthread->sigpend; + if (!_kse_isthreaded()) + return __sys_sigpending(set); + crit = _kse_critical_enter(); + KSE_SCHED_LOCK(curthread->kse, curthread->kseg); + *set = curthread->sigpend; + KSE_SCHED_UNLOCK(curthread->kse, curthread->kseg); KSE_LOCK_ACQUIRE(curthread->kse, &_thread_signal_lock); SIGSETOR(*set, _thr_proc_sigpending); KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock); _kse_critical_leave(crit); } /* Return the completion status: */ return (ret); } Index: head/lib/libpthread/thread/thr_sigprocmask.c =================================================================== --- head/lib/libpthread/thread/thr_sigprocmask.c (revision 116976) +++ head/lib/libpthread/thread/thr_sigprocmask.c (revision 116977) @@ -1,53 +1,54 @@ /* * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by John Birrell. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include "thr_private.h" __weak_reference(_sigprocmask, sigprocmask); int _sigprocmask(int how, const sigset_t *set, sigset_t *oset) { int ret; - ret = pthread_sigmask(how, set, oset); - if ((ret == 0) && (_kse_isthreaded() == 0)) + if (_kse_isthreaded() == 0) ret = __sys_sigprocmask(how, set, oset); + else + ret = pthread_sigmask(how, set, oset); return (ret); } Index: head/lib/libpthread/thread/thr_sigsuspend.c =================================================================== --- head/lib/libpthread/thread/thr_sigsuspend.c (revision 116976) +++ head/lib/libpthread/thread/thr_sigsuspend.c (revision 116977) @@ -1,87 +1,98 @@ /* * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by John Birrell. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include "thr_private.h" __weak_reference(__sigsuspend, sigsuspend); int _sigsuspend(const sigset_t *set) { struct pthread *curthread = _get_curthread(); int ret = -1; + sigset_t osigmask; + if (!_kse_isthreaded()) + return __sys_sigsuspend(set); + /* Check if a new signal set was provided by the caller: */ if (set != NULL) { THR_LOCK_SWITCH(curthread); + /* Save current sigmask */ + memcpy(&osigmask, &curthread->sigmask, sizeof(osigmask)); /* Change the caller's mask: */ - memcpy(&curthread->tmbx.tm_context.uc_sigmask, - set, sizeof(sigset_t)); + memcpy(&curthread->sigmask, set, sizeof(sigset_t)); THR_SET_STATE(curthread, PS_SIGSUSPEND); /* Wait for a signal: */ _thr_sched_switch_unlocked(curthread); /* Always return an interrupted error: */ errno = EINTR; + THR_SCHED_LOCK(curthread, curthread); /* Restore the signal mask: */ - memcpy(&curthread->tmbx.tm_context.uc_sigmask, - &curthread->sigmask, sizeof(sigset_t)); + memcpy(&curthread->sigmask, &osigmask, sizeof(sigset_t)); + THR_SCHED_UNLOCK(curthread, curthread); + /* + * signal mask is reloaded, need to check if there is + * pending proc signal I can handle. + */ + _thr_sig_check_pending(curthread); } else { /* Return an invalid argument error: */ errno = EINVAL; } /* Return the completion status: */ return (ret); } int __sigsuspend(const sigset_t * set) { struct pthread *curthread = _get_curthread(); int ret; _thr_enter_cancellation_point(curthread); ret = _sigsuspend(set); _thr_leave_cancellation_point(curthread); return (ret); } Index: head/lib/libpthread/thread/thr_sigwait.c =================================================================== --- head/lib/libpthread/thread/thr_sigwait.c (revision 116976) +++ head/lib/libpthread/thread/thr_sigwait.c (revision 116977) @@ -1,175 +1,249 @@ //depot/projects/kse/lib/libpthread/thread/thr_sigwait.c#1 - branch change 15154 (text+ko) /* * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by John Birrell. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include "thr_private.h" -__weak_reference(_sigwait, sigwait); +__weak_reference(__sigwait, sigwait); +__weak_reference(__sigtimedwait, sigtimedwait); +__weak_reference(__sigwaitinfo, sigwaitinfo); -int -_sigwait(const sigset_t *set, int *sig) +static int +lib_sigtimedwait(const sigset_t *set, siginfo_t *info, + const struct timespec * timeout) { struct pthread *curthread = _get_curthread(); int ret = 0; int i; sigset_t tempset, waitset; struct sigaction act; kse_critical_t crit; + siginfo_t siginfo; - _thr_enter_cancellation_point(curthread); + if (!_kse_isthreaded()) { + if (info == NULL) + info = &siginfo; + return __sys_sigtimedwait((sigset_t *)set, info, + (struct timespec *)timeout); + } /* * Specify the thread kernel signal handler. */ act.sa_handler = (void (*) ()) _thr_sig_handler; act.sa_flags = SA_RESTART | SA_SIGINFO; /* Ensure the signal handler cannot be interrupted by other signals: */ - sigfillset(&act.sa_mask); + SIGFILLSET(act.sa_mask); /* * Initialize the set of signals that will be waited on: */ waitset = *set; /* These signals can't be waited on. */ - sigdelset(&waitset, SIGKILL); - sigdelset(&waitset, SIGSTOP); + SIGDELSET(waitset, SIGKILL); + SIGDELSET(waitset, SIGSTOP); - /* - * Check to see if a pending signal is in the wait mask. - * This has to be atomic. - */ - tempset = curthread->sigpend; crit = _kse_critical_enter(); KSE_LOCK_ACQUIRE(curthread->kse, &_thread_signal_lock); - SIGSETOR(tempset, _thr_proc_sigpending); - SIGSETAND(tempset, waitset); - if (SIGNOTEMPTY(tempset)) { - /* Enter a loop to find a pending signal: */ - for (i = 1; i < NSIG; i++) { - if (sigismember (&tempset, i)) - break; - } - /* Clear the pending signal: */ - if (sigismember(&curthread->sigpend, i)) - sigdelset(&curthread->sigpend, i); - else - sigdelset(&_thr_proc_sigpending, i); - - KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock); - _kse_critical_leave(crit); - _thr_leave_cancellation_point(curthread); - /* Return the signal number to the caller: */ - *sig = i; - return (0); - } - /* - * Enter a loop to find the signals that are SIG_DFL. For + * Enter a loop to find the signals that are SIG_DFL. For * these signals we must install a dummy signal handler in * order for the kernel to pass them in to us. POSIX says * that the _application_ must explicitly install a dummy * handler for signals that are SIG_IGN in order to sigwait * on them. Note that SIG_IGN signals are left in the * mask because a subsequent sigaction could enable an * ignored signal. */ - sigemptyset(&tempset); - for (i = 1; i < NSIG; i++) { - if (sigismember(&waitset, i) && + SIGEMPTYSET(tempset); + for (i = 1; i <= _SIG_MAXSIG; i++) { + if (SIGISMEMBER(waitset, i) && (_thread_sigact[i - 1].sa_handler == SIG_DFL)) { - _thread_dfl_count[i]++; - sigaddset(&tempset, i); - if (_thread_dfl_count[i] == 1) { + _thread_dfl_count[i - 1]++; + SIGADDSET(tempset, i); + if (_thread_dfl_count[i - 1] == 1) { if (__sys_sigaction(i, &act, NULL) != 0) - ret = -1; + /* ret = -1 */; } } } - /* Done accessing _thread_dfl_count for now. */ - KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock); - _kse_critical_leave(crit); - if (ret == 0) { - /* - * Save the wait signal mask. The wait signal - * mask is independent of the threads signal mask - * and requires separate storage. - */ - curthread->data.sigwait = &waitset; + if (ret == 0) { + /* Done accessing _thread_dfl_count for now. */ + KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock); + KSE_SCHED_LOCK(curthread->kse, curthread->kseg); + for (i = 1; i <= _SIG_MAXSIG; ++i) { + if (SIGISMEMBER(waitset, i) && + SIGISMEMBER(curthread->sigpend, i)) { + SIGDELSET(curthread->sigpend, i); + *info = curthread->siginfo[i]; + KSE_SCHED_UNLOCK(curthread->kse, + curthread->kseg); + _kse_critical_leave(crit); + return (i); + } + } + curthread->timeout = 0; + _thr_set_timeout(timeout); /* Wait for a signal: */ - THR_LOCK_SWITCH(curthread); + curthread->oldsigmask = curthread->sigmask; + siginfo.si_signo = 0; + curthread->data.sigwaitinfo = &siginfo; + SIGFILLSET(curthread->sigmask); + SIGSETNAND(curthread->sigmask, waitset); THR_SET_STATE(curthread, PS_SIGWAIT); _thr_sched_switch_unlocked(curthread); - /* Return the signal number to the caller: */ - *sig = curthread->signo; + /* + * Return the signal number to the caller: + * XXX Here is race, how about a signal come in before + * we reach here? so we might got an incorrect timeout + * status. + */ + if (siginfo.si_signo > 0) { + if (info) + *info = siginfo; + ret = siginfo.si_signo; + } else { + if (curthread->timeout) + errno = EAGAIN; + ret = -1; + } /* * Probably unnecessary, but since it's in a union struct * we don't know how it could be used in the future. */ - curthread->data.sigwait = NULL; + crit = _kse_critical_enter(); + curthread->data.sigwaitinfo = NULL; + /* + * Relock the array of SIG_DFL wait counts. + */ + KSE_LOCK_ACQUIRE(curthread->kse, &_thread_signal_lock); } - /* - * Relock the array of SIG_DFL wait counts. - */ - crit = _kse_critical_enter(); - KSE_LOCK_ACQUIRE(curthread->kse, &_thread_signal_lock); - /* Restore the sigactions: */ act.sa_handler = SIG_DFL; - for (i = 1; i < NSIG; i++) { - if (sigismember(&tempset, i)) { - _thread_dfl_count[i]--; + for (i = 1; i <= _SIG_MAXSIG; i++) { + if (SIGISMEMBER(tempset, i)) { + _thread_dfl_count[i - 1]--; if ((_thread_sigact[i - 1].sa_handler == SIG_DFL) && - (_thread_dfl_count[i] == 0)) { + (_thread_dfl_count[i - 1] == 0)) { if (__sys_sigaction(i, &act, NULL) != 0) - ret = -1; + /* ret = -1 */ ; } } } /* Done accessing _thread_dfl_count. */ KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock); _kse_critical_leave(crit); + + return (ret); +} + +int +__sigtimedwait(const sigset_t *set, siginfo_t *info, + const struct timespec * timeout) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_enter_cancellation_point(curthread); + ret = lib_sigtimedwait(set, info, timeout); _thr_leave_cancellation_point(curthread); + return (ret); +} - /* Return the completion status: */ +int _sigtimedwait(const sigset_t *set, siginfo_t *info, + const struct timespec * timeout) +{ + return lib_sigtimedwait(set, info, timeout); +} + +int +__sigwaitinfo(const sigset_t *set, siginfo_t *info) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_enter_cancellation_point(curthread); + ret = lib_sigtimedwait(set, info, NULL); + _thr_leave_cancellation_point(curthread); return (ret); } + +int +_sigwaitinfo(const sigset_t *set, siginfo_t *info) +{ + return lib_sigtimedwait(set, info, NULL); +} + +int +__sigwait(const sigset_t *set, int *sig) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_enter_cancellation_point(curthread); + ret = lib_sigtimedwait(set, NULL, NULL); + if (ret > 0) { + *sig = ret; + ret = 0; + } + else + ret = -1; + _thr_leave_cancellation_point(curthread); + return (ret); +} + +int +_sigwait(const sigset_t *set, int *sig) +{ + int ret; + + ret = lib_sigtimedwait(set, NULL, NULL); + if (ret > 0) { + *sig = ret; + ret = 0; + } else { + ret = -1; + } + return (ret); +} +