aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hurd/hurdsig.c272
1 files changed, 160 insertions, 112 deletions
diff --git a/hurd/hurdsig.c b/hurd/hurdsig.c
index ebe0308..5ac8c2a 100644
--- a/hurd/hurdsig.c
+++ b/hurd/hurdsig.c
@@ -455,6 +455,30 @@ abort_all_rpcs (int signo, struct machine_thread_all_state *state, int live)
}
}
+/* Wake up any sigsuspend call that is blocking SS->thread. SS must be
+ locked. */
+static void
+wake_sigsuspend (struct hurd_sigstate *ss)
+{
+ error_t err;
+ mach_msg_header_t msg;
+
+ if (ss->suspended == MACH_PORT_NULL)
+ return;
+
+ /* There is a sigsuspend waiting. Tell it to wake up. */
+ 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);
+}
+
struct hurd_signal_preemptor *_hurdsig_preemptors = 0;
sigset_t _hurdsig_preempted_set;
@@ -465,35 +489,18 @@ weak_alias (_hurdsig_preemptors, _hurdsig_preempters)
#define STOPSIGS (sigmask (SIGTTIN) | sigmask (SIGTTOU) \
| sigmask (SIGSTOP) | sigmask (SIGTSTP))
-/* 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)
+/* 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. */
+static int
+post_signal (struct hurd_sigstate *ss,
+ int signo, struct hurd_signal_detail *detail,
+ int untraced, void (*reply) (void))
{
- 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);
- }
-
/* Mark the signal as pending. */
void mark_pending (void)
{
@@ -557,19 +564,23 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
ss_suspended = 1;
}
+ error_t err;
+ sighandler_t handler;
+
if (signo == 0)
{
if (untraced)
- /* This is PTRACE_CONTINUE. */
- resume ();
+ {
+ /* This is PTRACE_CONTINUE. */
+ act = ignore;
+ 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);
@@ -632,7 +643,7 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
suspend ();
__spin_unlock (&ss->lock);
reply ();
- return;
+ return 0;
}
handler = ss->actions[signo].sa_handler;
@@ -876,7 +887,7 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
as a unit. */
crit ? 0 : signo, 1,
&thread_state, &state_changed,
- &reply)
+ reply)
!= MACH_PORT_NULL);
if (crit)
@@ -963,6 +974,9 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
&& signo != SIGILL && signo != SIGTRAP)
ss->actions[signo].sa_handler = SIG_DFL;
+ /* Any sigsuspend call must return after the handler does. */
+ wake_sigsuspend (ss);
+
/* 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,
@@ -976,95 +990,129 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
}
}
- /* 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. */
- {
- /* 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;
- }
+/* Return the set of pending signals in SS which should be delivered. */
+static sigset_t
+pending_signals (struct hurd_sigstate *ss)
+{
+ /* 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. */
+ if (_hurd_stopped || __spin_lock_locked (&ss->critical_section_lock))
+ return 0;
- check_pending_signals:
- untraced = 0;
+ return ss->pending & ~ss->blocked;
+}
- 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;
- }
- }
+/* Post the specified pending signals in SS and return 1. If one of
+ them is traced, abort immediately and return 0. SS must be locked on
+ entry and will be unlocked in all cases. */
+static int
+post_pending (struct hurd_sigstate *ss, sigset_t pending, void (*reply) (void))
+{
+ int signo;
+ struct hurd_signal_detail detail;
- /* 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
+ for (signo = 1; signo < NSIG; ++signo)
+ if (__sigismember (&pending, signo))
{
- /* 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);
- }
+ __sigdelset (&ss->pending, signo);
+ detail = ss->pending_data[signo];
__spin_unlock (&ss->lock);
+
+ /* Will reacquire the lock, except if the signal is traced. */
+ if (! post_signal (ss, signo, &detail, 0, reply))
+ return 0;
}
- }
- /* All pending signals delivered to all threads.
- Now we can send the reply message even for signal 0. */
- reply ();
+ /* No more signals pending; SS->lock is still locked. */
+ __spin_unlock (&ss->lock);
+
+ return 1;
+}
+
+/* Post all the pending signals of all threads and return 1. If a traced
+ signal is encountered, abort immediately and return 0. */
+static int
+post_all_pending_signals (void (*reply) (void))
+{
+ struct hurd_sigstate *ss;
+ sigset_t pending = 0;
+
+ for (;;)
+ {
+ __mutex_lock (&_hurd_siglock);
+ for (ss = _hurd_sigstates; ss != NULL; ss = ss->next)
+ {
+ __spin_lock (&ss->lock);
+
+ pending = pending_signals (ss);
+ if (pending)
+ /* post_pending() below will unlock SS. */
+ break;
+
+ __spin_unlock (&ss->lock);
+ }
+ __mutex_unlock (&_hurd_siglock);
+
+ if (! pending)
+ return 1;
+ if (! post_pending (ss, pending, reply))
+ return 0;
+ }
+}
+
+/* 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)
+{
+ /* 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);
+ }
+
+ if (! post_signal (ss, signo, detail, untraced, reply))
+ return;
+
+ /* The signal was neither fatal nor traced. We still hold SS->lock. */
+ 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 ();
+
+ /* Post any pending signals for this thread. */
+ if (! post_pending (ss, pending_signals (ss), reply))
+ return;
+ }
+ else
+ {
+ /* We need to check for pending signals for all threads. */
+ __spin_unlock (&ss->lock);
+ if (! post_all_pending_signals (reply))
+ return;
+
+ /* 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.