/* * Copyright (c) 1996, 1998, 1999, 2000, 2001 University of Utah and the Flux Group. * All rights reserved. * * This file is part of the Flux OSKit. The OSKit is free software, also known * as "open source;" you can redistribute it and/or modify it under the terms * of the GNU General Public License (GPL), version 2, as published by the Free * Software Foundation (FSF). To explore alternate licensing terms, contact * the University of Utah at csl-dist@cs.utah.edu or +1-801-585-3271. * * The OSKit is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GPL for more details. You should have * received a copy of the GPL along with the OSKit; see the file COPYING. If * not, write to the FSF, 59 Temple Place #330, Boston, MA 02111-1307, USA. */ /* * Posix signals, or something approximating them. The goal is to get * the obvious stuff nearly correct, but not go overboard. */ #include #include #include #include #include "pthread_cond.h" #include "pthread_signal.h" /* * The sigactions array. Protected by its own lock. */ static struct sigaction sigactions[NSIG]; static pthread_lock_t sigactions_lock = PTHREAD_LOCK_INITIALIZER; /* * There is a global (or "process") set of pending signals. * kill() and sigqueue() affect the process pending set. */ static sigset_t sigpending; /* * A queue of all threads waiting in sigwait. */ static queue_head_t sigwaiters; /* * An array of queues of pending signals posted with sigqueue(). */ static queue_head_t sigqueued[NSIG]; /* * We avoid malloc in interrupt handlers by preallocating the queue * entries for sigqueued above. */ static queue_head_t sigqueue_free; /* * Lock to protect the global signal stuff. */ static pthread_lock_t siglock = PTHREAD_LOCK_INITIALIZER; /* * Prototypes. */ int pthread_kill_locked(pthread_thread_t *pthread, int signo); void really_deliver_signal(int sig, siginfo_t *code,struct sigcontext *scp); void oskit_deliver_async_signal(int sig); void oskit_deliver_process_signal(int sig); int pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) { pthread_thread_t *pthread = CURPTHREAD(); int err = 0; assert(pthread); /* siglock and pthread siglock are taken from an interrupt handler */ assert_interrupts_enabled(); disable_interrupts(); pthread_lock(&pthread->siglock); if (oset) *oset = pthread->sigmask; if (set) { switch (how) { case SIG_BLOCK: pthread->sigmask |= *set; break; case SIG_UNBLOCK: pthread->sigmask &= ~*set; break; case SIG_SETMASK: pthread->sigmask = *set; break; default: err = EINVAL; } } /* * Look for process pending signals that are unblocked, and deliver. */ pthread_lock(&siglock); while (sigpending & ~pthread->sigmask) { int sig = ffs(sigpending & ~pthread->sigmask); /* Call with siglock and thread siglock locked */ oskit_deliver_process_signal(sig); } pthread_unlock(&siglock); /* * Look for pthread pending signals that are unblocked, and deliver. */ while (pthread->sigpending & ~pthread->sigmask) { int sig = ffs(pthread->sigpending & ~pthread->sigmask); /* Call with thread siglock locked */ oskit_deliver_async_signal(sig); } pthread_unlock(&pthread->siglock); enable_interrupts(); return err; } /* * This can be called out of an interrupt handler, say from an alarm * expiration. Therefore, must not take the pthread lock. We use a * separate lock to protect the signal state. */ int pthread_kill(pthread_t tid, int signo) { pthread_thread_t *pthread; int enabled; /* Error check? Sure! */ if (!signo) return 0; if (signo < 0 || signo >= NSIG) return EINVAL; if ((pthread = tidtothread(tid)) == NULL_THREADPTR) return EINVAL; /* siglock and pthread siglock are taken from an interrupt handler */ enabled = save_disable_interrupts(); pthread_lock(&pthread->siglock); /* * pthread_kill_locked() will take care of unlocking. */ pthread_kill_locked(pthread, signo); /* * If not in an interrupt, use this opportunity to deliver * pending unblocked signals to the current thread. */ if (! IN_AN_INTERRUPT()) { pthread = CURPTHREAD(); SIGCHECK(pthread); } restore_interrupt_enable(enabled); return 0; } /* * This does all the work. * * Should be called with interrupts disabled, and siglock and pthread * siglock are held. * Returns with the locks released (caller must restore interrupts). */ int pthread_kill_locked(pthread_thread_t *pthread, int signo) { /* * Look at the process sigactions. If the "process" is ignoring * the signal, then the signal is not placed in the pending list. */ pthread_lock(&sigactions_lock); if (sigactions[signo].sa_handler == SIG_IGN) { pthread_unlock(&pthread->siglock); pthread_unlock(&sigactions_lock); return 0; } /* Don't need the global siglock anymore. */ pthread_unlock(&sigactions_lock); /* * First have to check for sigwaiting, since that overrides sigmask. */ pthread_lock(&pthread->waitlock); if ((pthread->waitflags & THREAD_WS_SIGWAIT) && sigismember(&pthread->sigwaiting, signo)) { sigaddset(&pthread->sigpending, signo); sigemptyset(&pthread->sigwaiting); pthread_unlock(&pthread->siglock); /* pthread waitlock will be released. */ pthread_wakeup_locked(pthread); return 0; } pthread_unlock(&pthread->waitlock); /* * Add the signal to list of pending signals for the target thread. */ sigaddset(&pthread->sigpending, signo); /* * If the signal is currently blocked, then do nothing else. * It will be noticed when the signal is unblocked. */ if (sigismember(&pthread->sigmask, signo)) { pthread_unlock(&pthread->siglock); return 0; } /* * Is the current thread sending itself a signal? This is okay, as * long as its not from within an interrupt handler, say, from an * alarm expiration. The caller is going to look for pending signals * to the current thread. * * If this *is* from within an interrupt, must wait until later * since delivering it now is really the wrong way to go. Lets * schedule a soft interrupt, and get the thread switched out. * The signal will be delivered when it comes back in. */ if (pthread == CURPTHREAD()) { if (IN_AN_INTERRUPT()) { softint_request(SOFTINT_ASYNCREQ); } pthread_unlock(&pthread->siglock); return 0; } /* Don't need the pthread siglock anymore; waking up the thread */ pthread_unlock(&pthread->siglock); /* * Below here, we muck with the waitflags, which are accessed * from interrupt handlers. Interrupts are already disabled, * so take the thread waitlock. */ pthread_lock(&pthread->waitlock); /* * If the thread is in an osenv_sleep(), issue a wakeup. * The thread will be allowed to return through the sleep, to * be caught sometime later. This allows driver state to be * cleaned up before the thread is actually signaled. */ if (pthread->waitflags & THREAD_WS_OSENVSLEEP) { pthread_unlock(&pthread->waitlock); osenv_wakeup(pthread->sleeprec, OSENV_SLEEP_CANCELED); return 0; } /* * If the thread is THREAD_WS_SLEEPING, then restart it. */ if (pthread->waitflags & THREAD_WS_SLEEPING) { /* pthread waitlock will be released. */ pthread_wakeup_locked(pthread); return 0; } /* * If the thread is THREAD_WS_CONDWAIT, then restart it. The wrinkle * is a race condition between the time the thread is taken off * the condition queue and the time the thread state is changed. */ if (pthread->waitflags & THREAD_WS_CONDWAIT) { struct pthread_cond_impl *pimpl = pthread->waitcond->impl; pthread_lock(&(pimpl->lock)); /* * The thread was still on the Q, so its safe to change * its state to reflect that it is not longer waiting * on the condition. * * If the thread was not on the Q, we caught the race, * and do not have to do anything. */ if (pthread_remove_fromQ(&(pimpl->waiters), pthread)) { pthread->waitflags &= ~THREAD_WS_CONDWAIT; pthread->waitcond = 0; pthread_unlock(&(pimpl->lock)); pthread_unlock(&pthread->waitlock); pthread_sched_setrunnable(pthread); return 0; } pthread_unlock(&(pimpl->lock)); pthread_unlock(&pthread->waitlock); return 0; } /* * IPC Wait. */ if (pthread->waitflags & THREAD_WS_IPCWAIT_FLAG) { /* pthread waitlock will be released. */ pthread_ipc_cancel(pthread); return 0; } /* * Done with waitflags. */ pthread_unlock(&pthread->waitlock); /* * Must be running on another CPU. Must wait for it to be noticed. */ return 0; } /* * The rest of the Posix signal code. This stuff replaces the corresponding * code in the Posix library, since I don't see how it can be done unless * its all in one place. */ /* * sigaction */ int sigaction(int sig, const struct sigaction *act, struct sigaction *oact) { if (sig < 0 || sig >= NSIG) return errno = EINVAL, -1; assert_preemption_enabled(); /* siglock and pthread siglock are taken from an interrupt handler */ assert_interrupts_enabled(); disable_interrupts(); pthread_lock(&sigactions_lock); if (oact) *oact = sigactions[sig]; if (act) sigactions[sig] = *act; pthread_unlock(&sigactions_lock); /* * If the action for this signal is being set to SIG_IGN or SIG_DFL, * and that signal is process pending, then clear it. */ pthread_lock(&siglock); if (act && (act->sa_handler == SIG_IGN || act->sa_handler == SIG_DFL)) { while (! queue_empty(&sigqueued[sig])) { sigqueue_thingie_t *pthingie; queue_remove_first(&sigqueued[sig], pthingie, sigqueue_thingie_t *, chain); queue_enter(&sigqueue_free, pthingie, sigqueue_thingie_t *, chain); } sigdelset(&sigpending, sig); } pthread_unlock(&siglock); enable_interrupts(); return 0; } /* * sigprocmask. In a multithreaded program, this is just pthread_sigmask */ int sigprocmask(int how, const sigset_t *set, sigset_t *oset) { return pthread_sigmask(how, set, oset); } /* * raise. In a multithreaded program, raise is pthread_kill on itself. */ int raise(int sig) { return pthread_kill(pthread_self(), sig); } /* * kill. What does it mean to kill() in a multithreaded program? The POSIX * spec says that a signal sent to a "process" shall be delivered to only * one thread. If no thread has that signal unblocked, then the first * thread to unblock the signal is the lucky winner. Well, that means we * need to have a global sigpending to record process pending signals. */ int kill(pid_t pid, int signo) { pthread_thread_t *pthread; int enabled, i; struct sigaction act; extern pthread_lock_t pthread_create_lock; /* Error check? Sure! */ if (!signo) return 0; /* siglock and pthread siglock are taken from an interrupt handler */ enabled = save_disable_interrupts(); /* * Look at the process sigactions. If the "process" is ignoring * the signal, then the signal is not placed in the pending list. */ pthread_lock(&sigactions_lock); act = sigactions[signo]; pthread_unlock(&sigactions_lock); if (act.sa_handler == SIG_IGN) { restore_interrupt_enable(enabled); return 0; } /* * Take and hold the global siglock. This is needed to prevent * a race with sigwait. */ pthread_lock(&siglock); /* * Kill does not queue. If the signal is already pending, this * one is tossed. */ if (sigismember(&sigpending, signo)) { pthread_unlock(&siglock); restore_interrupt_enable(enabled); return 0; } /* * Make the signal process pending. */ sigaddset(&sigpending, signo); /* * Look through the threads in sigwait to see if any of them * is waiting for the signal. This is done as a separate pass * since the value of the pthread sigmask is ignored (threads * in sigwait will have blocked the signals being waited for). */ queue_iterate(&sigwaiters, pthread, pthread_thread_t *, chain) { assert(pthread); pthread_lock(&pthread->siglock); pthread_lock(&pthread->waitlock); if ((pthread->waitflags & THREAD_WS_SIGWAIT) && sigismember(&pthread->sigwaiting, signo)) { /* * Bingo. Make the signal thread pending and * wake it up. */ sigaddset(&pthread->sigpending, signo); sigemptyset(&pthread->sigwaiting); pthread_unlock(&pthread->siglock); /* pthread waitlock will be released. */ pthread_wakeup_locked(pthread); pthread_unlock(&siglock); restore_interrupt_enable(enabled); return 0; } pthread_unlock(&pthread->waitlock); pthread_unlock(&pthread->siglock); } /* * No threads in sigwait. Too bad. Must find another thread to * deliver it to. */ pthread_lock(&pthread_create_lock); for (i = 1; i < THREADS_MAX_THREAD; i++) { if ((pthread = threads_tidtothread[i]) != NULL_THREADPTR) { pthread_lock(&pthread->siglock); if (! sigismember(&pthread->sigmask, signo)) { pthread_unlock(&pthread_create_lock); /* Thread siglock will be released. */ pthread_kill_locked(pthread, signo); break; } pthread_unlock(&pthread->siglock); } } pthread_unlock(&pthread_create_lock); pthread_unlock(&siglock); /* * If not in an interrupt, use this opportunity to deliver * pending unblocked signals to the current thread. */ if (! IN_AN_INTERRUPT()) { pthread = CURPTHREAD(); SIGCHECK(pthread); } restore_interrupt_enable(enabled); return 0; } /* * sigqueue. */ int sigqueue(pid_t pid, int signo, const union sigval value) { return sigqueue_internal(pid, signo, value, SI_QUEUE); } /* * sigqueue internal */ int sigqueue_internal(pid_t pid, int signo, const union sigval value, int code) { pthread_thread_t *pthread; int enabled; int i; sigqueue_thingie_t *pthingie = NULL; struct sigaction act; extern pthread_lock_t pthread_create_lock; /* Error check? Sure! */ if (!signo) return 0; /* siglock and pthread siglock are taken from an interrupt handler */ enabled = save_disable_interrupts(); /* * Look at the process sigactions. If the "process" is ignoring * the signal, then the signal is not placed in the pending list. */ pthread_lock(&sigactions_lock); act = sigactions[signo]; pthread_unlock(&sigactions_lock); if (act.sa_handler == SIG_IGN) { restore_interrupt_enable(enabled); return 0; } /* * Take and hold the global siglock. This is needed to prevent * a race with sigwait. */ pthread_lock(&siglock); /* * If the flags does not include SA_SIGINFO, and there is already * a signal pending, this new one is dropped. */ if ((! (act.sa_flags & SA_SIGINFO)) && sigismember(&sigpending, signo)) { pthread_unlock(&siglock); restore_interrupt_enable(enabled); return 0; } /* * Gotta have space for the new signal. */ if (queue_empty(&sigqueue_free)) { pthread_unlock(&siglock); restore_interrupt_enable(enabled); return EAGAIN; } /* * Create a queue entry. */ queue_remove_first(&sigqueue_free, pthingie, sigqueue_thingie_t *, chain); pthingie->info.si_signo = signo; pthingie->info.si_code = code; pthingie->info.si_value = value; /* * Queue the signal on the process. */ queue_enter(&sigqueued[signo], pthingie, sigqueue_thingie_t *, chain); sigaddset(&sigpending, signo); /* * Look through the threads in sigwait to see if any of them * is waiting for the signal. This is done as a separate pass * since the value of the pthread sigmask is ignored (threads * in sigwait will have blocked the signals being waited for). * If we find one, wakeup that thread. Note that POSIX says that * if multiple threads are sigwaiting for the same signal number, * exactly one thread is woken up. The problem is how to maintain * the FIFO order, and how to prevent lost signals in the case that * a thread calls sigwait before the woken thread runs and gets it. */ queue_iterate(&sigwaiters, pthread, pthread_thread_t *, chain) { pthread_lock(&pthread->siglock); pthread_lock(&pthread->waitlock); if ((pthread->waitflags & THREAD_WS_SIGWAIT) && sigismember(&pthread->sigwaiting, signo)) { /* * Bingo. Wake it up. */ sigemptyset(&pthread->sigwaiting); pthread_unlock(&pthread->siglock); /* pthread waitlock will be released. */ pthread_wakeup_locked(pthread); pthread_unlock(&siglock); restore_interrupt_enable(enabled); return 0; } pthread_unlock(&pthread->waitlock); pthread_unlock(&pthread->siglock); } /* * Need to find a thread to deliver the signal to. Look for the * first thread that is not blocking the signal, and send it the * signal. It is my opinion that any program that is using sigwait, * and has not blocked signals in all of its threads, is bogus. The * same is true if the program is not using sigwait, and has the * signal unblocked in more than one thread. * Why? You might wake up a thread, but not have an actual queue * entry left by the time it runs again and looks, since another * thread could call sigwait and get that queue entry, or if there * are multiple threads that can take the signal, one thread could * get all the entries. This could result in an interrupted thread, * but with no signal to deliver. Well, not much to do about it. * Lets just queue the signal for the process, and let the chips * fall where they may. */ pthread_lock(&pthread_create_lock); for (i = 1; i < THREADS_MAX_THREAD; i++) { if ((pthread = threads_tidtothread[i]) != NULL_THREADPTR) { pthread_lock(&pthread->siglock); if (! sigismember(&pthread->sigmask, signo)) { pthread_unlock(&pthread_create_lock); /* Thread siglock will be released. */ pthread_kill_locked(pthread, signo); break; } pthread_unlock(&pthread->siglock); } } pthread_unlock(&pthread_create_lock); pthread_unlock(&siglock); /* * If not in an interrupt, use this opportunity to deliver * pending unblocked signals to the current thread. */ if (! IN_AN_INTERRUPT()) { pthread = CURPTHREAD(); SIGCHECK(pthread); } restore_interrupt_enable(enabled); return 0; } /* * Sigwait. Sigwait overrides the state of the pthread sigmask and the global * sigactions. The caller *must* block the set of signals in "set", before * calling sigwait, otherwise the behaviour is undefined (which means that * the caller will take an async signal anyway, and sigwait will return EINTR. */ static oskit_error_t oskit_sigwait_internal(const sigset_t *set, siginfo_t *info, const oskit_timespec_t *timeout) { pthread_thread_t *pthread = CURPTHREAD(); int thissig, rval; pthread_testcancel(); assert_preemption_enabled(); /* siglock and pthread siglock are taken from an interrupt handler */ assert_interrupts_enabled(); disable_interrupts(); /* * First check for process pending signals. Must take and hold * the global siglock to prevent races with kill() and sigqueue(). */ pthread_lock(&pthread->siglock); pthread_lock(&siglock); if (sigpending & *set) { sigqueue_thingie_t *pthingie; thissig = ffs(sigpending & *set); /* * Sent with kill(). Using sigwait and kill is Bogus! */ if (queue_empty(&sigqueued[thissig])) { info->si_signo = thissig; info->si_code = SI_USER; info->si_value.sival_int = 0; sigdelset(&pthread->sigpending, thissig); sigdelset(&sigpending, thissig); pthread_unlock(&siglock); pthread_unlock(&pthread->siglock); enable_interrupts(); return 0; } /* * Grab the first queue entry. */ queue_remove_first(&sigqueued[thissig], pthingie, sigqueue_thingie_t *, chain); /* * If that was the last one, reset the process sigpending. */ if (queue_empty(&sigqueued[thissig])) sigdelset(&sigpending, thissig); sigdelset(&pthread->sigpending, thissig); /* * Copy the information and free the queue entry. */ info->si_signo = pthingie->info.si_signo; info->si_code = pthingie->info.si_code; info->si_value.sival_int = pthingie->info.si_value.sival_int; queue_enter(&sigqueue_free, pthingie, sigqueue_thingie_t *, chain); pthread_unlock(&siglock); pthread_unlock(&pthread->siglock); enable_interrupts(); return 0; } /* * Now check for pthread pending signals. */ if (pthread->sigpending & *set) { thissig = ffs(pthread->sigpending & *set); info->si_signo = thissig; info->si_code = SI_USER; info->si_value.sival_int = 0; sigdelset(&pthread->sigpending, thissig); pthread_unlock(&siglock); pthread_unlock(&pthread->siglock); enable_interrupts(); return 0; } /* * For timed wait, if nothing is available and the timeout value * is zero, its an error. */ if (timeout && timeout->tv_sec == 0 && timeout->tv_nsec == 0) { pthread_unlock(&siglock); pthread_unlock(&pthread->siglock); enable_interrupts(); return EAGAIN; } /* * Grab the wait lock and set the sigwaiting mask. Once that is done, * release the thread siglock; Another thread can try and wake this * thread up as a result of seeing it in sigwait, but the actual * wakeup will be delayed until the waitlock is released in the switch * code. */ pthread_lock(&pthread->waitlock); pthread->sigwaiting = *set; pthread_unlock(&pthread->siglock); /* * Add this thread to the list of threads in sigwait. Once that is * done, it is safe to release the global siglock, which will allow * another thread to scan the sigwaiters list. As above, it might * find a thread in sigwait, but it will not be able to wake it up * until the waitlock is released in the switch code. */ queue_enter(&sigwaiters, pthread, pthread_thread_t *, chain); pthread_unlock(&siglock); /* and block */ rval = oskit_pthread_sleep_withflags(THREAD_WS_SIGWAIT, timeout); pthread_lock(&pthread->siglock); pthread->sigwaiting = 0; /* * Remove from the list of threads in sigwait. */ pthread_lock(&siglock); queue_remove(&sigwaiters, pthread, pthread_thread_t *, chain); /* * Check for cancelation. * * XXX hacky, need to drop all our locks in case the cancelation * happens. */ pthread_unlock(&siglock); pthread_unlock(&pthread->siglock); pthread_testcancel(); /* * While the locks are down, check for the timed-out case. */ if (rval == ETIMEDOUT) { enable_interrupts(); return EAGAIN; } pthread_lock(&pthread->siglock); pthread_lock(&siglock); /* * Look for a wakeup to deliver a queued signal. This would come * either from kill() or from sigqueue(). */ if (sigpending & *set) { sigqueue_thingie_t *pthingie; thissig = ffs(sigpending & *set); /* * Sent with kill(). Using sigwait and kill is Bogus! */ if (queue_empty(&sigqueued[thissig])) { info->si_signo = thissig; info->si_code = SI_USER; info->si_value.sival_int = 0; sigdelset(&sigpending, thissig); pthread_unlock(&siglock); pthread_unlock(&pthread->siglock); enable_interrupts(); return 0; } /* * Grab the first queue entry. */ queue_remove_first(&sigqueued[thissig], pthingie, sigqueue_thingie_t *, chain); /* * If that was the last one, reset the process sigpending. */ if (queue_empty(&sigqueued[thissig])) sigdelset(&sigpending, thissig); /* * Copy the information and free the queue entry. */ info->si_signo = pthingie->info.si_signo; info->si_code = pthingie->info.si_code; info->si_value.sival_int = pthingie->info.si_value.sival_int; queue_enter(&sigqueue_free, pthingie, sigqueue_thingie_t *, chain); pthread_unlock(&siglock); pthread_unlock(&pthread->siglock); enable_interrupts(); return 0; } /* * Well, at the moment I am going to assume that if this thread * wakes up, and there is no signal pending in the waitset, the * thread wait was interrupted for some other reason. Return EINTR. */ if (! (pthread->sigpending & *set)) { pthread_unlock(&siglock); pthread_unlock(&pthread->siglock); enable_interrupts(); return EINTR; } /* * Otherwise, get the first signal and return it. */ thissig = ffs(pthread->sigpending & *set); info->si_signo = thissig; info->si_code = SI_USER; info->si_value.sival_int = 0; sigdelset(&pthread->sigpending, thissig); pthread_unlock(&siglock); pthread_unlock(&pthread->siglock); enable_interrupts(); return 0; } /* * Sigwait. */ int sigwait(const sigset_t *set, int *sig) { siginfo_t info; oskit_error_t rc; memset(&info, 0, sizeof(info)); rc = oskit_sigwait_internal(set, &info, 0); if (rc) return rc; *sig = info.si_signo; return 0; } /* * Sigwaitinfo. */ int sigwaitinfo(const sigset_t *set, siginfo_t *info) { oskit_error_t rc; rc = oskit_sigwait_internal(set, info, 0); if (rc) return rc; return 0; } /* * Sigtimedwait. */ int sigtimedwait(const sigset_t *set, siginfo_t *info, const struct oskit_timespec *timeout) { oskit_error_t rc; if (! timeout) return EINVAL; rc = oskit_sigwait_internal(set, info, timeout); if (rc) return rc; return 0; } /* * XXX we could probably implement this */ int sigsuspend(const sigset_t *sigmask) { return ENOSYS; } /* * Internal stuff. */ /* * Deliver a signal generated from a trap. This is the upcall from the * machine dependent code that created the sigcontext structure from * the trap state. */ void oskit_libc_sendsig(int sig, int code, struct sigcontext *scp) { pthread_thread_t *pthread = CURPTHREAD(); siginfo_t siginfo; int enabled; siginfo.si_signo = sig; siginfo.si_code = SI_EXCEP; siginfo.si_value.sival_int = code; /* * Need to call really_deliver_signal() with interrupts blocked * and the pthread siglock held. */ enabled = save_disable_interrupts(); pthread_lock(&pthread->siglock); really_deliver_signal(sig, &siginfo, scp); pthread_unlock(&pthread->siglock); restore_interrupt_enable(enabled); } /* * Deliver an asynchronous signal. This must be called with interrupts * blocked and the pthread siglock held. */ void oskit_deliver_async_signal(int sig) { struct sigcontext sc; siginfo_t siginfo; /* create a stub sigcontext_t. */ bzero(&sc, sizeof(sc)); siginfo.si_signo = sig; siginfo.si_code = SI_USER; siginfo.si_value.sival_int = 0; really_deliver_signal(sig, &siginfo, &sc); } /* * Deliver a process signals. This must be called with interrupts * blocked and the siglock and pthread siglock held. */ void oskit_deliver_process_signal(int sig) { struct sigcontext sc; siginfo_t siginfo; sigqueue_thingie_t *pthingie; /* create a stub sigcontext_t. */ bzero(&sc, sizeof(sc)); /* * Sent with kill(). Using sigwait and kill is Bogus! */ if (queue_empty(&sigqueued[sig])) { siginfo.si_signo = sig; siginfo.si_code = SI_USER; siginfo.si_value.sival_int = 0; sigdelset(&sigpending, sig); goto deliver; } /* * Grab the first queue entry. */ queue_remove_first(&sigqueued[sig], pthingie, sigqueue_thingie_t *, chain); /* * If that was the last one, reset the process sigpending. */ if (queue_empty(&sigqueued[sig])) sigdelset(&sigpending, sig); /* * Copy the information and free the queue entry. */ siginfo.si_signo = pthingie->info.si_signo; siginfo.si_code = pthingie->info.si_code; siginfo.si_value.sival_int = pthingie->info.si_value.sival_int; queue_enter(&sigqueue_free, pthingie, sigqueue_thingie_t *, chain); /* * Release the global siglock for the delivery. */ deliver: pthread_unlock(&siglock); really_deliver_signal(sig, &siginfo, &sc); /* * Reacquire since the caller expects it. */ pthread_lock(&siglock); } /* * Deliver any pending signals. Called out of the context switch code * when a thread switches in, and there are pending signals. * * Interrupts are blocked and the thread siglock is locked. */ void oskit_deliver_pending_signals(void) { pthread_thread_t *pthread = CURPTHREAD(); /* * Look for process pending signals that are unblocked, and deliver. */ pthread_lock(&siglock); while (sigpending & ~pthread->sigmask) { int sig = ffs(sigpending & ~pthread->sigmask); /* Call with siglock and thread siglock locked */ oskit_deliver_process_signal(sig); } pthread_unlock(&siglock); /* * Now deliver any pthread pending signals that are left. */ while (pthread->sigpending & ~pthread->sigmask) { int sig = ffs(pthread->sigpending & ~pthread->sigmask); /* Call at splhigh and thread locked */ oskit_deliver_async_signal(sig); } } /* * Actually deliver the signal to the thread. At this point the signal * is going to be delivered, so it no longer matters if it is blocked. */ void really_deliver_signal(int sig, siginfo_t *info, struct sigcontext *scp) { pthread_thread_t *pthread = CURPTHREAD(); sigset_t sigmask, oldmask; struct sigaction act; int enabled; assert_interrupts_disabled(); save_preemption_enable(enabled); pthread_lock(&sigactions_lock); act = sigactions[sig]; pthread_unlock(&sigactions_lock); /* * Ignored? */ if (act.sa_handler == SIG_IGN || act.sa_handler == SIG_ERR) return; if (act.sa_handler == SIG_DFL) { /* Default action for all signals is termination */ if (info->si_code == SI_EXCEP) { printf("TID %p: exception, code=0x%x\n", pthread->tid, info->si_value.sival_int); sigcontext_dump(scp); } panic("Sendsig: Signal %d caught but no handler", sig); } /* * Set the signal mask for calling the handler. */ oldmask = sigmask = pthread->sigmask; sigaddset(&sigmask, sig); sigmask |= act.sa_mask; sigdelset(&pthread->sigpending, sig); pthread->sigmask = sigmask; pthread_unlock(&pthread->siglock); enable_interrupts(); enable_preemption(); /* * and call the handler ... */ if (act.sa_flags & SA_SIGINFO) act.sa_sigaction(sig, info, (void *) scp); else ((void (*)(int, int, struct sigcontext *))act.sa_handler) (sig, info->si_value.sival_int, scp); disable_interrupts(); restore_preemption_enable(enabled); pthread_lock(&pthread->siglock); pthread->sigmask = oldmask; /* * and return with thread siglock locked at splhigh. */ } void pthread_init_signals() { int i; sigqueue_thingie_t *pthingie; extern void libc_sendsig_init(void); queue_init(&sigwaiters); queue_init(&sigqueue_free); /* Initialize the default signal actions. */ for (i = 0; i < NSIG; i++) sigactions[i].sa_handler = SIG_DFL; /* Initialize the signal queue headers. */ for (i = 0; i < NSIG; i++) queue_init(&sigqueued[i]); /* Create a free list of queue thingies. */ if ((pthingie = (sigqueue_thingie_t *) smalloc(sizeof(sigqueue_thingie_t) * SIGQUEUE_MAX)) == NULL) panic("pthread_init_signals: Out of memory"); for (i = 0; i < SIGQUEUE_MAX; i++) { queue_enter(&sigqueue_free, pthingie, sigqueue_thingie_t *, chain); pthingie++; } libc_sendsig_init(); } void signals_init(void) { /* Called by libc. Initialization was already done above. */ }