diff options
| author | nathanw <nathanw@NetBSD.org> | 2003-01-25 00:43:38 +0000 |
|---|---|---|
| committer | nathanw <nathanw@NetBSD.org> | 2003-01-25 00:43:38 +0000 |
| commit | fe8e8dad4a892f0dcca519bce3419e4ca080593f (patch) | |
| tree | 038e715c26cbb1e86b4c8645cf52fa05134b96ec /lib/libpthread | |
| parent | 564fe117cc7d093237e1c99301fd5fc1ead165c7 (diff) | |
More signal rearranging:
- Signal handlers now simply continue executing the current thread,
rather than trying to put themselves back on the queue that they came
from, which was rather fragile. As a result, all callers of
pthread__block() must be prepared to handle spurious wakeups.
- When a signal arrives for a thread that is blocked in the kernel,
note this in another field in pthread_st and set a flag. Process the
signal and set up the trampoline for the handler *after* the thread
unblocks, so that both the trampoline and the returned state from
the kernel are preserved.
- Factor out some code into a pthread__deliver_signal() routine;
the signal-taking code in pthread_sigmask() should be able to use this
soon.
This is still gross, and there are still some terrible MP issues lurking here,
but progress crawls along.
Diffstat (limited to 'lib/libpthread')
| -rw-r--r-- | lib/libpthread/pthread_int.h | 14 | ||||
| -rw-r--r-- | lib/libpthread/pthread_sa.c | 23 | ||||
| -rw-r--r-- | lib/libpthread/pthread_sig.c | 137 |
3 files changed, 79 insertions, 95 deletions
diff --git a/lib/libpthread/pthread_int.h b/lib/libpthread/pthread_int.h index 84b54896cf0..1418fb7d84b 100644 --- a/lib/libpthread/pthread_int.h +++ b/lib/libpthread/pthread_int.h @@ -1,4 +1,4 @@ -/* $NetBSD: pthread_int.h,v 1.3 2003/01/18 18:45:54 christos Exp $ */ +/* $NetBSD: pthread_int.h,v 1.4 2003/01/25 00:43:38 nathanw Exp $ */ /*- * Copyright (c) 2001 The NetBSD Foundation, Inc. @@ -71,6 +71,8 @@ struct pt_alarm_t { struct pthread_st { unsigned int pt_magic; + /* Identifier, for debugging and for preventing recycling. */ + int pt_num; int pt_type; /* normal, upcall, or idle */ int pt_state; /* running, blocked, etc. */ @@ -100,6 +102,7 @@ struct pthread_st { sigset_t pt_sigmask; /* Signals we won't take. */ sigset_t pt_siglist; /* Signals pending for us. */ + sigset_t pt_sigblocked; /* Signals delivered while blocked. */ pthread_spin_t pt_siglock; /* Lock on above */ void * pt_exitval; /* Read by pthread_join() */ @@ -140,9 +143,6 @@ struct pthread_st { */ pthread_spin_t* pt_heldlock; - /* Identifier, for debugging. */ - int pt_num; - /* Thread-specific data */ void* pt_specific[PTHREAD_KEYS_MAX]; @@ -180,7 +180,7 @@ struct pthread_lock_ops { #define PT_FLAG_CS_DISABLED 0x0004 /* Cancellation disabled */ #define PT_FLAG_CS_ASYNC 0x0008 /* Cancellation is async */ #define PT_FLAG_CS_PENDING 0x0010 -#define PT_FLAG_SIGCATCH 0x0020 /* Interrupt sleep on a signal */ +#define PT_FLAG_SIGDEFERRED 0x0020 /* There are signals to take */ #define PT_MAGIC 0x11110001 #define PT_DEAD 0xDEAD0001 @@ -341,7 +341,9 @@ void pthread__locked_switch(pthread_t self, pthread_t next, void pthread__signal_init(void); -void pthread__signal(pthread_t t, int sig, int code); +void pthread__signal(pthread_t self, pthread_t t, int sig, int code); +void pthread__deliver_signal(pthread_t self, pthread_t t, int sig, int code); +void pthread__signal_deferred(pthread_t self, pthread_t t); void pthread__destroy_tsd(pthread_t self); diff --git a/lib/libpthread/pthread_sa.c b/lib/libpthread/pthread_sa.c index bcd67512ffa..4f906e3a92b 100644 --- a/lib/libpthread/pthread_sa.c +++ b/lib/libpthread/pthread_sa.c @@ -1,4 +1,4 @@ -/* $NetBSD: pthread_sa.c,v 1.3 2003/01/18 18:45:56 christos Exp $ */ +/* $NetBSD: pthread_sa.c,v 1.4 2003/01/25 00:43:38 nathanw Exp $ */ /*- * Copyright (c) 2001 The NetBSD Foundation, Inc. @@ -90,7 +90,7 @@ pthread__upcall(int type, struct sa_t *sas[], int ev, int intr, void *arg) { pthread_t t, self, next, intqueue; int first = 1; - int deliversig = 0, runalarms = 0; + int runalarms = 0; siginfo_t *si; PTHREADD_ADD(PTHREADD_UPCALLS); @@ -130,7 +130,6 @@ pthread__upcall(int type, struct sa_t *sas[], int ev, int intr, void *arg) break; case SA_UPCALL_SIGNAL: PTHREADD_ADD(PTHREADD_UP_SIGNAL); - deliversig = 1; break; case SA_UPCALL_SIGEV: PTHREADD_ADD(PTHREADD_UP_SIGEV); @@ -189,15 +188,23 @@ pthread__upcall(int type, struct sa_t *sas[], int ev, int intr, void *arg) * This also means that a thread that was interrupted to take * a signal will be on a run queue, and not in upcall limbo. */ - if (deliversig) { + if (type == SA_UPCALL_SIGNAL) { si = arg; if (ev) - pthread__signal(pthread__sa_id(sas[1]), si->si_signo, - si->si_code); + pthread__signal(self, pthread__sa_id(sas[1]), + si->si_signo, si->si_code); else - pthread__signal(NULL, si->si_signo, si->si_code); + pthread__signal(self, NULL, si->si_signo, si->si_code); + } else if (type == SA_UPCALL_UNBLOCKED) { + /* + * A signal may have been presented to this thread while + * it was in the kernel. + */ + t = pthread__sa_id(sas[1]); + if (t->pt_flags & PT_FLAG_SIGDEFERRED) + pthread__signal_deferred(self, t); } - + /* * At this point everything on our list should be scheduled * (or was an upcall). diff --git a/lib/libpthread/pthread_sig.c b/lib/libpthread/pthread_sig.c index 99bf5cc5136..3b441f6351e 100644 --- a/lib/libpthread/pthread_sig.c +++ b/lib/libpthread/pthread_sig.c @@ -1,4 +1,4 @@ -/* $NetBSD: pthread_sig.c,v 1.4 2003/01/24 17:43:45 jdolecek Exp $ */ +/* $NetBSD: pthread_sig.c,v 1.5 2003/01/25 00:43:38 nathanw Exp $ */ /*- * Copyright (c) 2001 The NetBSD Foundation, Inc. @@ -90,8 +90,8 @@ static pthread_cond_t pt_sigsuspended_cond = PTHREAD_COND_INITIALIZER; static void -pthread__signal_tramp(int, int, struct sigaction *, ucontext_t *, sigset_t *, - int, struct pthread_queue_t *, pthread_spin_t *); +pthread__signal_tramp(int, int, void (*)(int, int, struct sigcontext *), + ucontext_t *, sigset_t *); __strong_alias(__libc_thr_sigsetmask,pthread_sigmask) @@ -167,7 +167,6 @@ __sigsuspend14(const sigset_t *sigmask) } pthread_sigmask(SIG_SETMASK, sigmask, &oldmask); - self->pt_flags |= PT_FLAG_SIGCATCH; self->pt_state = PT_STATE_BLOCKED_QUEUE; self->pt_sleepobj = &pt_sigsuspended_cond; self->pt_sleepq = &pt_sigsuspended; @@ -178,7 +177,6 @@ __sigsuspend14(const sigset_t *sigmask) pthread__block(self, &pt_sigsuspended_lock); pthread__testcancel(self); - self->pt_flags &= ~PT_FLAG_SIGCATCH; pthread_sigmask(SIG_SETMASK, &oldmask, NULL); @@ -353,13 +351,10 @@ pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) * willing thread, if t is null. */ void -pthread__signal(pthread_t t, int sig, int code) +pthread__signal(pthread_t self, pthread_t t, int sig, int code) { - pthread_t self, target, good, okay; - sigset_t oldmask, *maskp; - ucontext_t *uc; + pthread_t target, good, okay; - self = pthread__self(); if (t) { target = t; pthread_spinlock(self, &target->pt_siglock); @@ -455,14 +450,6 @@ pthread__signal(pthread_t t, int sig, int code) * target process is a bug. */ assert(target->pt_state != PT_STATE_RUNNING); - /* - * Prevent anyone else from considering this thread for handling - * more instances of this signal. - */ - oldmask = target->pt_sigmask; - __sigplusset(&target->pt_sigmask, &pt_sigacts[sig].sa_mask); - __sigaddset14(&target->pt_sigmask, sig); - pthread_spinunlock(self, &target->pt_siglock); /* * Holding the state lock blocks out cancellation and any other @@ -483,14 +470,41 @@ pthread__signal(pthread_t t, int sig, int code) case PT_STATE_BLOCKED_SYS: /* * The target is not on a queue at all, and won't run - * again for a while. Try to wake it from its torpor. + * again for a while. Try to wake it from its torpor, then + * mark the signal for later processing. */ + __sigaddset14(&target->pt_sigblocked, sig); + __sigaddset14(&target->pt_sigmask, sig); + target->pt_flags |= PT_FLAG_SIGDEFERRED; + pthread_spinunlock(self, &target->pt_siglock); + pthread_spinunlock(self, &target->pt_statelock); _lwp_wakeup(target->pt_blockedlwp); - break; + return; default: ; } + pthread__deliver_signal(self, target, sig, code); + pthread__sched(self, target); + pthread_spinunlock(self, &target->pt_statelock); +} + +/* Must be called with target's siglock held */ +void +pthread__deliver_signal(pthread_t self, pthread_t target, int sig, int code) +{ + sigset_t oldmask, *maskp; + ucontext_t *uc; + + /* + * Prevent anyone else from considering this thread for handling + * more instances of this signal. + */ + oldmask = target->pt_sigmask; + __sigplusset(&target->pt_sigmask, &pt_sigacts[sig].sa_mask); + __sigaddset14(&target->pt_sigmask, sig); + pthread_spinunlock(self, &target->pt_siglock); + /* * We'd like to just pass oldmask to the * pthread__signal_tramp(), but makecontext() can't reasonably @@ -519,38 +533,36 @@ pthread__signal(pthread_t t, int sig, int code) SDPRINTF(("(makecontext %p): target %p: sig: %d %d uc: %p oldmask: %08x\n", self, target, sig, code, target->pt_uc, maskp->__bits[0])); - makecontext(uc, pthread__signal_tramp, 8, - sig, code, &pt_sigacts[sig], target->pt_uc, maskp, - target->pt_state, target->pt_sleepq, target->pt_sleeplock); + makecontext(uc, pthread__signal_tramp, 5, + sig, code, pt_sigacts[sig].sa_handler, target->pt_uc, maskp); target->pt_uc = uc; - - if (target->pt_state != PT_STATE_BLOCKED_SYS) - pthread__sched(self, target); - pthread_spinunlock(self, &target->pt_statelock); } +void +pthread__signal_deferred(pthread_t self, pthread_t t) +{ + int i; + + pthread_spinlock(self, &t->pt_siglock); + + while ((i = firstsig(&t->pt_sigblocked)) != 0) { + __sigdelset14(&t->pt_sigblocked, i); + pthread__deliver_signal(self, t, i, 0); + } + t->pt_flags &= ~PT_FLAG_SIGDEFERRED; + + pthread_spinunlock(self, &t->pt_siglock); +} static void -pthread__signal_tramp(int sig, int code, struct sigaction *act, - ucontext_t *uc, sigset_t *oldmask, int oldstate, - struct pthread_queue_t *oldsleepq, pthread_spin_t *oldsleeplock) +pthread__signal_tramp(int sig, int code, + void (*handler)(int, int, struct sigcontext *), + ucontext_t *uc, sigset_t *oldmask) { - void (*handler)(int, int, struct sigcontext *); struct pthread__sigcontext psc; - pthread_t self, next; - - self = pthread__self(); - SDPRINTF(("(tramp %p) sig %d uc %p oldmask %08x oldstate %d q %p\n", - self, sig, uc, oldmask->__bits[0], oldstate, oldsleepq)); - - /* - * We should only ever get here if a handler is set. Signal - * actions are process-global; a signal set to SIG_DFL or - * SIG_IGN should be handled in the kernel (by being ignored - * or killing the process) and never get this far. - */ - handler = (void (*)(int, int, struct sigcontext *)) act->sa_handler; + SDPRINTF(("(tramp %p) sig %d uc %p oldmask %08x\n", + pthread__self(), sig, uc, oldmask->__bits[0])); /* * XXX we don't support siginfo here yet. @@ -568,43 +580,6 @@ pthread__signal_tramp(int sig, int code, struct sigaction *act, pthread_sigmask(SIG_SETMASK, &uc->uc_sigmask, NULL); /* - * Go back to whatever queue we were found on, unless SIGCATCH - * is set. When we are continued, the first thing we do will - * be to jump back to the previous context. - */ - if (self->pt_flags & PT_FLAG_SIGCATCH) - _setcontext_u(uc); - - next = pthread__next(self); - next->pt_state = PT_STATE_RUNNING; - pthread_spinlock(self, &self->pt_statelock); - if (oldstate == PT_STATE_RUNNABLE) { - pthread_spinlock(self, &pthread__runqueue_lock); - self->pt_state = PT_STATE_RUNNABLE; - pthread_spinunlock(self, &self->pt_statelock); - PTQ_INSERT_TAIL(&pthread__runqueue, self, pt_runq); - pthread__locked_switch(self, next, &pthread__runqueue_lock); - } else if (oldstate == PT_STATE_BLOCKED_QUEUE) { - pthread_spinlock(self, oldsleeplock); - self->pt_state = PT_STATE_BLOCKED_QUEUE; - self->pt_sleepq = oldsleepq; - self->pt_sleeplock = oldsleeplock; - pthread_spinunlock(self, &self->pt_statelock); - PTQ_INSERT_TAIL(oldsleepq, self, pt_sleep); - pthread__locked_switch(self, next, oldsleeplock); - } else if (oldstate == PT_STATE_BLOCKED_SYS) { - /* - * We weren't really doing anything before. Just go to - * the next thread. - */ - self->pt_state = PT_STATE_BLOCKED_SYS; - pthread_spinunlock(self, &self->pt_statelock); - pthread__switch(self, next); - } else { - /*CONSTCOND*/ - assert(0); - } - /* * Jump back to what we were doing before we were interrupted * by a signal. */ |
