diff options
Diffstat (limited to 'lib/libpthread/pthreads/signal.c')
| -rw-r--r-- | lib/libpthread/pthreads/signal.c | 426 |
1 files changed, 426 insertions, 0 deletions
diff --git a/lib/libpthread/pthreads/signal.c b/lib/libpthread/pthreads/signal.c new file mode 100644 index 000000000000..b181b84dac14 --- /dev/null +++ b/lib/libpthread/pthreads/signal.c @@ -0,0 +1,426 @@ +/* ==== signal.c ============================================================ + * Copyright (c) 1993, 1994 by Chris Provenzano, proven@mit.edu + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Chris Provenzano. + * 4. The name of Chris Provenzano may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY CHRIS PROVENZANO ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL CHRIS PROVENZANO BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Description : Queue functions. + * + * 1.00 93/07/21 proven + * -Started coding this file. + */ + +#include <pthread.h> +#include <signal.h> + +/* + * Time which select in fd_kern_wait() will sleep. + * If there are no threads to run we sleep for an hour or until + * we get an interrupt or an fd thats awakens. To make sure we + * don't miss an interrupt this variable gets reset too zero in + * sig_handler_real(). + */ +struct timeval __fd_kern_wait_timeout = { 0, 0 }; + +/* + * Global for user-kernel lock, and blocked signals + */ +static volatile sigset_t sig_to_tryagain; +static volatile sigset_t sig_to_process; +static volatile int kernel_lock = 0; +static volatile int sig_count = 0; + +static void sig_handler(int signal); +static void set_thread_timer(); +void sig_prevent(void); +void sig_resume(void); + +/* ========================================================================== + * context_switch() + * + * This routine saves the current state of the running thread gets + * the next thread to run and restores it's state. To allow different + * processors to work with this routine, I allow the machdep_restore_state() + * to either return or have it return from machdep_save_state with a value + * other than 0, this is for implementations which use setjmp/longjmp. + */ +static void context_switch() +{ + struct pthread **current, *next, *last; + semaphore *lock; + int count; + + /* save state of current thread */ + if (machdep_save_state()) { + return; + } + + last = pthread_run; + if (pthread_run = pthread_queue_deq(&pthread_current_queue)) { + /* restore state of new current thread */ + machdep_restore_state(); + return; + } + + /* Poll all the kernel fds */ + fd_kern_poll(); + +context_switch_reschedule:; + /* + * Go through the reschedule list once, this is the only place + * that goes through the queue without using the queue routines. + * + * But first delete the current queue. + */ + pthread_queue_init(&pthread_current_queue); + current = &(pthread_link_list); + count = 0; + + while (*current) { + switch((*current)->state) { + case PS_RUNNING: + pthread_queue_enq(&pthread_current_queue, *current); + current = &((*current)->pll); + count++; + break; + case PS_DEAD: + /* Cleanup thread, unless we're using the stack */ + if (((*current)->flags & PF_DETACHED) && (*current != last)) { + next = (*current)->pll; + lock = &((*current)->lock); + if (SEMAPHORE_TEST_AND_SET(lock)) { + /* Couldn't cleanup this time, try again later */ + current = &((*current)->pll); + } else { + if (!((*current)->attr.stackaddr_attr)) { + free (machdep_pthread_cleanup(&((*current)->machdep_data))); + } + free (*current); + *current = next; + } + } else { + current = &((*current)->pll); + } + break; + default: + /* Should be on a different queue. Ignore. */ + current = &((*current)->pll); + count++; + break; + } + } + + /* Are there any threads to run */ + if (pthread_run = pthread_queue_deq(&pthread_current_queue)) { + /* restore state of new current thread */ + machdep_restore_state(); + return; + } + + /* Are there any threads at all */ + if (count) { + /* + * Do a wait, timeout is set to a hour unless we get an interrupt + * before the select in wich case it polls and returns. + */ + fd_kern_wait(); + + /* Check for interrupts, but ignore SIGVTALR */ + sigdelset(&sig_to_process, SIGVTALRM); + + if (sig_to_process) { + /* Process interrupts */ + sig_handler(0); + } + + goto context_switch_reschedule; + + } + exit(0); +} + +/* ========================================================================== + * sig_handler_pause() + * + * Wait until a signal is sent to the process. + */ +void sig_handler_pause() +{ + sigset_t sig_to_block, sig_to_pause; + + sigfillset(&sig_to_block); + sigemptyset(&sig_to_pause); + sigprocmask(SIG_BLOCK, &sig_to_block, NULL); + if (!sig_to_process) { + sigsuspend(&sig_to_pause); + } + sigprocmask(SIG_UNBLOCK, &sig_to_block, NULL); +} + +/* ========================================================================== + * context_switch_done() + * + * This routine does all the things that are necessary after a context_switch() + * calls the machdep_restore_state(). DO NOT put this in the context_switch() + * routine because sometimes the machdep_restore_state() doesn't return + * to context_switch() but instead ends up in machdep_thread_start() or + * some such routine, which will need to call this routine and + * sig_check_and_resume(). + */ +void context_switch_done() +{ + sigdelset(&sig_to_process, SIGVTALRM); + set_thread_timer(); +} + +/* ========================================================================== + * set_thread_timer() + * + * Assums kernel is locked. + */ +static void set_thread_timer() +{ + static int last_sched_attr = SCHED_RR; + + switch (pthread_run->attr.sched_attr) { + case SCHED_RR: + machdep_set_thread_timer(&(pthread_run->machdep_data)); + break; + case SCHED_FIFO: + if (last_sched_attr != SCHED_FIFO) { + machdep_unset_thread_timer(); + } + break; + case SCHED_IO: + if ((last_sched_attr != SCHED_IO) && (!sig_count)) { + machdep_set_thread_timer(&(pthread_run->machdep_data)); + } + break; + default: + machdep_set_thread_timer(&(pthread_run->machdep_data)); + break; + } + last_sched_attr = pthread_run->attr.sched_attr; +} + +/* ========================================================================== + * sig_handler() + * + * Assumes the kernel is locked. + */ +static void sig_handler(int sig) +{ + + /* + * First check for old signals, do one pass through and don't + * check any twice. + */ + if (sig_to_tryagain) { + if (sigismember(&sig_to_tryagain, SIGALRM)) { + switch (sleep_wakeup()) { + case 1: + /* Do the default action, no threads were sleeping */ + case OK: + /* Woke up a sleeping thread */ + sigdelset(&sig_to_tryagain, SIGALRM); + break; + case NOTOK: + /* Couldn't get appropriate locks, try again later */ + break; + } + } else { + PANIC(); + } + } + + /* + * NOW, process signal that just came in, plus any pending on the + * signal mask. All of these must be resolved. + */ + +sig_handler_top:; + + switch(sig) { + case 0: + break; + case SIGVTALRM: + if (sig_count) { + sigset_t sigall; + + sig_count = 0; + + /* Unblock all signals */ + sigemptyset(&sigall); + sigprocmask(SIG_SETMASK, &sigall, NULL); + } + context_switch(); + context_switch_done(); + break; + case SIGALRM: + sigdelset(&sig_to_process, SIGALRM); + switch (sleep_wakeup()) { + case 1: + /* Do the default action, no threads were sleeping */ + case OK: + /* Woke up a sleeping thread */ + break; + case NOTOK: + /* Couldn't get appropriate locks, try again later */ + sigaddset(&sig_to_tryagain, SIGALRM); + break; + } + break; + default: + PANIC(); + } + + /* Determine if there are any other signals */ + if (sig_to_process) { + for (sig = 1; sig <= SIGMAX; sig++) { + if (sigismember(&sig_to_process, sig)) { + + /* goto sig_handler_top */ + goto sig_handler_top; + } + } + } +} + +/* ========================================================================== + * sig_handler_real() + * + * On a multi-processor this would need to use the test and set instruction + * otherwise the following will work. + */ +void sig_handler_real(int sig) +{ + if (kernel_lock) { + __fd_kern_wait_timeout.tv_sec = 0; + sigaddset(&sig_to_process, sig); + return; + } + sig_prevent(); + sig_count++; + sig_handler(sig); + sig_resume(); +} + +/* ========================================================================== + * sig_handler_fake() + */ +void sig_handler_fake(int sig) +{ + if (kernel_lock) { + /* Currently this should be impossible */ + PANIC(); + } + sig_prevent(); + sig_handler(sig); + sig_resume(); +} + +/* ========================================================================== + * reschedule() + * + * This routine assumes that the caller is the current pthread, pthread_run + * and that it has a lock on itself and that it wants to reschedule itself. + */ +void reschedule(enum pthread_state state) +{ + semaphore *plock; + + if (kernel_lock) { + /* Currently this should be impossible */ + PANIC(); + } + sig_prevent(); + pthread_run->state = state; + SEMAPHORE_RESET((plock = &(pthread_run->lock))); + sig_handler(SIGVTALRM); + sig_resume(); +} + +/* ========================================================================== + * sig_prevent() + */ +void sig_prevent(void) +{ + kernel_lock++; +} + +/* ========================================================================== + * sig_resume() + */ +void sig_resume() +{ + kernel_lock--; +} + +/* ========================================================================== + * sig_check_and_resume() + */ +void sig_check_and_resume() +{ + /* Some routine name that is yet to be determined. */ + + /* Only bother if we are truely unlocking the kernel */ + while (!(--kernel_lock)) { + + /* Assume sigset_t is not a struct or union */ + if (sig_to_process) { + kernel_lock++; + sig_handler(0); + } else { + break; + } + } +} + +/* ========================================================================== + * sig_init() + * + * SIGVTALRM (NOT POSIX) needed for thread timeslice timeouts. + * Since it's not POSIX I will replace it with a + * virtual timer for threads. + * SIGALRM (IS POSIX) so some special handling will be + * necessary to fake SIGALRM signals + */ +void sig_init(void) +{ + int sig_to_init[] = { SIGVTALRM, SIGALRM, 0 }; + int i; + + /* Initialize only the necessary signals */ + + for (i = 0; sig_to_init[i]; i++) { + if (signal(sig_to_init[i], sig_handler_real)) { + PANIC(); + } + } +} + |
