>From c5ef997a7f01c3bcc406f37e5b4739f6f5d08f0a Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Sat, 4 Sep 2010 13:24:21 +0000 Subject: [PATCH 1/8] _hurd_internal_post_signal: Split into more functions * hurd/hurdsig.c (_hurd_internal_post_signal): Use inner functions instead of gotos for the overall flow control. This patch should change nothing besides replacing gotos with function calls and return statements. Note that the "signo == 0" test at the beginning of post_signal, is now done on every call. However, only the first call ever has a zero signo, so this does not change the overall behavior. --- hurd/hurdsig.c | 47 +++++++++++++++++++++++++++++++++++------------ 1 files changed, 35 insertions(+), 12 deletions(-) diff --git a/hurd/hurdsig.c b/hurd/hurdsig.c index 4cb5e3c..c139805 100644 --- a/hurd/hurdsig.c +++ b/hurd/hurdsig.c @@ -455,60 +455,66 @@ weak_alias (_hurdsig_preemptors, _hurdsig_preempters) /* Deliver a signal. SS is not locked. */ void _hurd_internal_post_signal (struct hurd_sigstate *ss, int signo, struct hurd_signal_detail *detail, mach_port_t reply_port, mach_msg_type_name_t reply_port_type, int untraced) { error_t err; struct machine_thread_all_state thread_state; enum { stop, ignore, core, term, handle } act; sighandler_t handler; sigset_t pending; int ss_suspended; /* Reply to this sig_post message. */ __typeof (__msg_sig_post_reply) *reply_rpc = (untraced ? __msg_sig_post_untraced_reply : __msg_sig_post_reply); void reply (void) { error_t err; if (reply_port == MACH_PORT_NULL) return; err = (*reply_rpc) (reply_port, reply_port_type, 0); reply_port = MACH_PORT_NULL; if (err != MACH_SEND_INVALID_DEST) /* Ignore dead reply port. */ assert_perror (err); } +/* Actual delivery of a single signal. Called with SS unlocked. When + the signal is delivered, return 1 with SS locked. If the signal is + being traced, return 0 with SS unlocked. */ +int post_signal (void) +{ + /* Mark the signal as pending. */ void mark_pending (void) { __sigaddset (&ss->pending, signo); /* Save the details to be given to the handler when SIGNO is unblocked. */ ss->pending_data[signo] = *detail; } /* Suspend the process with SIGNO. */ void suspend (void) { /* Stop all other threads and mark ourselves stopped. */ __USEPORT (PROC, ({ /* Hold the siglock while stopping other threads to be sure it is not held by another thread afterwards. */ __mutex_lock (&_hurd_siglock); __proc_dostop (port, _hurd_msgport_thread); __mutex_unlock (&_hurd_siglock); abort_all_rpcs (signo, &thread_state, 1); reply (); __proc_mark_stop (port, signo, detail->code); })); _hurd_stopped = 1; } /* Resume the process after a suspension. */ void resume (void) { /* Resume the process from being stopped. */ @@ -526,65 +532,63 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss, assert_perror (err); for (i = 0; i < nthreads; ++i) { if (threads[i] != _hurd_msgport_thread && (act != handle || threads[i] != ss->thread)) { err = __thread_resume (threads[i]); assert_perror (err); } err = __mach_port_deallocate (__mach_task_self (), threads[i]); assert_perror (err); } __vm_deallocate (__mach_task_self (), (vm_address_t) threads, nthreads * sizeof *threads); _hurd_stopped = 0; if (act == handle) /* The thread that will run the handler is already suspended. */ ss_suspended = 1; } if (signo == 0) { if (untraced) /* This is PTRACE_CONTINUE. */ resume (); /* This call is just to check for pending signals. */ __spin_lock (&ss->lock); - goto check_pending_signals; + return 1; } - post_signal: - thread_state.set = 0; /* We know nothing. */ __spin_lock (&ss->lock); /* Check for a preempted signal. Preempted signals can arrive during critical sections. */ { inline sighandler_t try_preemptor (struct hurd_signal_preemptor *pe) { /* PE cannot be null. */ do { if (HURD_PREEMPT_SIGNAL_P (pe, signo, detail->code)) { if (pe->preemptor) { sighandler_t handler = (*pe->preemptor) (pe, ss, &signo, detail); if (handler != SIG_ERR) return handler; } else return pe->handler; } pe = pe->next; } while (pe != 0); return SIG_ERR; } handler = ss->preemptors ? try_preemptor (ss->preemptors) : SIG_ERR; @@ -593,61 +597,61 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss, { __mutex_lock (&_hurd_siglock); handler = try_preemptor (_hurdsig_preemptors); __mutex_unlock (&_hurd_siglock); } } ss_suspended = 0; if (handler == SIG_IGN) /* Ignore the signal altogether. */ act = ignore; else if (handler != SIG_ERR) /* Run the preemption-provided handler. */ act = handle; else { /* No preemption. Do normal handling. */ if (!untraced && __sigismember (&_hurdsig_traced, signo)) { /* We are being traced. Stop to tell the debugger of the signal. */ if (_hurd_stopped) /* Already stopped. Mark the signal as pending; when resumed, we will notice it and stop again. */ mark_pending (); else suspend (); __spin_unlock (&ss->lock); reply (); - return; + return 0; } handler = ss->actions[signo].sa_handler; if (handler == SIG_DFL) /* Figure out the default action for this signal. */ switch (signo) { case 0: /* A sig_post msg with SIGNO==0 is sent to tell us to check for pending signals. */ act = ignore; break; case SIGTTIN: case SIGTTOU: case SIGSTOP: case SIGTSTP: act = stop; break; case SIGCONT: case SIGIO: case SIGURG: case SIGCHLD: case SIGWINCH: act = ignore; break; case SIGQUIT: @@ -935,146 +939,165 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss, scp->sc_error = detail->error; /* Block requested signals while running the handler. */ scp->sc_mask = ss->blocked; __sigorset (&ss->blocked, &ss->blocked, &ss->actions[signo].sa_mask); /* Also block SIGNO unless we're asked not to. */ if (! (ss->actions[signo].sa_flags & (SA_RESETHAND | SA_NODEFER))) __sigaddset (&ss->blocked, signo); /* Reset to SIG_DFL if requested. SIGILL and SIGTRAP cannot be automatically reset when delivered; the system silently enforces this restriction. */ if (ss->actions[signo].sa_flags & SA_RESETHAND && signo != SIGILL && signo != SIGTRAP) ss->actions[signo].sa_handler = SIG_DFL; /* Start the thread running the handler (or possibly waiting for an RPC reply before running the handler). */ err = __thread_set_state (ss->thread, MACHINE_THREAD_STATE_FLAVOR, (natural_t *) &thread_state.basic, MACHINE_THREAD_STATE_COUNT); assert_perror (err); err = __thread_resume (ss->thread); assert_perror (err); thread_state.set = 0; /* Everything we know is now wrong. */ break; } } - /* The signal has either been ignored or is now being handled. We can - consider it delivered and reply to the killer. */ - reply (); + return 1; +} - /* We get here unless the signal was fatal. We still hold SS->lock. - Check for pending signals, and loop to post them. */ - { +/* Try to find a non-blocked pending signal and deliver it. Called with + SS locked. If a signal is delivered, return 1 and leave SS locked. + If the signal is traced, or if none can be found, return 0 with + SS unlocked. */ +int check_pending_signal (void) +{ /* Return nonzero if SS has any signals pending we should worry about. We don't worry about any pending signals if we are stopped, nor if SS is in a critical section. We are guaranteed to get a sig_post message before any of them become deliverable: either the SIGCONT signal, or a sig_post with SIGNO==0 as an explicit poll when the thread finishes its critical section. */ inline int signals_pending (void) { if (_hurd_stopped || __spin_lock_locked (&ss->critical_section_lock)) return 0; return pending = ss->pending & ~ss->blocked; } - check_pending_signals: untraced = 0; if (signals_pending ()) { for (signo = 1; signo < NSIG; ++signo) if (__sigismember (&pending, signo)) { deliver_pending: __sigdelset (&ss->pending, signo); *detail = ss->pending_data[signo]; __spin_unlock (&ss->lock); - goto post_signal; + + return post_signal (); } } /* No pending signals left undelivered for this thread. If we were sent signal 0, we need to check for pending signals for all threads. */ if (signo == 0) { __spin_unlock (&ss->lock); __mutex_lock (&_hurd_siglock); for (ss = _hurd_sigstates; ss != NULL; ss = ss->next) { __spin_lock (&ss->lock); for (signo = 1; signo < NSIG; ++signo) if (__sigismember (&ss->pending, signo) && (!__sigismember (&ss->blocked, signo) /* We "deliver" immediately pending blocked signals whose action might be to ignore, so that if ignored they are dropped right away. */ || ss->actions[signo].sa_handler == SIG_IGN || ss->actions[signo].sa_handler == SIG_DFL)) { mutex_unlock (&_hurd_siglock); goto deliver_pending; } __spin_unlock (&ss->lock); } __mutex_unlock (&_hurd_siglock); } else { /* No more signals pending; SS->lock is still locked. Wake up any sigsuspend call that is blocking SS->thread. */ if (ss->suspended != MACH_PORT_NULL) { /* There is a sigsuspend waiting. Tell it to wake up. */ error_t err; mach_msg_header_t msg; msg.msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_MAKE_SEND, 0); msg.msgh_remote_port = ss->suspended; msg.msgh_local_port = MACH_PORT_NULL; /* These values do not matter. */ msg.msgh_id = 8675309; /* Jenny, Jenny. */ ss->suspended = MACH_PORT_NULL; err = __mach_msg (&msg, MACH_SEND_MSG, sizeof msg, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); assert_perror (err); } __spin_unlock (&ss->lock); } + + return 0; } + + if (! post_signal ()) + return; + + if (signo != 0) + { + /* The signal has either been ignored or is now being handled. We can + consider it delivered and reply to the killer. */ + reply (); + } + + /* We get here unless the signal was fatal. We still hold SS->lock. + Check for pending signals, and loop to post them. */ + while (check_pending_signal ()); + /* All pending signals delivered to all threads. Now we can send the reply message even for signal 0. */ reply (); } /* Decide whether REFPORT enables the sender to send us a SIGNO signal. Returns zero if so, otherwise the error code to return to the sender. */ static error_t signal_allowed (int signo, mach_port_t refport) { if (signo < 0 || signo >= NSIG) return EINVAL; if (refport == __mach_task_self ()) /* Can send any signal. */ goto win; /* Avoid needing to check for this below. */ if (refport == MACH_PORT_NULL) return EPERM; switch (signo) { case SIGINT: case SIGQUIT: case SIGTSTP: case SIGHUP: case SIGINFO: case SIGTTIN: -- 1.7.2.3