summaryrefslogtreecommitdiff
path: root/hurd/hurdsig.c
diff options
context:
space:
mode:
Diffstat (limited to 'hurd/hurdsig.c')
-rw-r--r--hurd/hurdsig.c280
1 files changed, 217 insertions, 63 deletions
diff --git a/hurd/hurdsig.c b/hurd/hurdsig.c
index 5ac8c2a203..13a81ed189 100644
--- a/hurd/hurdsig.c
+++ b/hurd/hurdsig.c
@@ -46,9 +46,6 @@ mach_port_t _hurd_msgport;
/* Thread listening on it. */
thread_t _hurd_msgport_thread;
-/* Thread which receives task-global signals. */
-thread_t _hurd_sigthread;
-
/* These are set up by _hurdsig_init. */
unsigned long int __hurd_sigthread_stack_base;
unsigned long int __hurd_sigthread_stack_end;
@@ -56,6 +53,9 @@ unsigned long int __hurd_sigthread_stack_end;
/* Linked-list of per-thread signal state. */
struct hurd_sigstate *_hurd_sigstates;
+/* Sigstate for the task-global signals. */
+struct hurd_sigstate *_hurd_global_sigstate;
+
/* Timeout for RPC's after interrupt_operation. */
mach_msg_timeout_t _hurd_interrupted_rpc_timeout = 60000;
@@ -84,7 +84,7 @@ _hurd_thread_sigstate (thread_t thread)
{
ss = malloc (sizeof (*ss));
if (ss == NULL)
- __libc_fatal ("hurd: Can't allocate thread sigstate\n");
+ __libc_fatal ("hurd: Can't allocate sigstate\n");
ss->thread = thread;
__spin_lock_init (&ss->lock);
@@ -98,16 +98,19 @@ _hurd_thread_sigstate (thread_t thread)
ss->intr_port = MACH_PORT_NULL;
ss->context = NULL;
- /* Initialize the sigaction vector from the default signal receiving
- thread's state, and its from the system defaults. */
- if (thread == _hurd_sigthread)
- default_sigaction (ss->actions);
+ if (thread == MACH_PORT_NULL)
+ {
+ /* Process-wide sigstate, use the system defaults. */
+ default_sigaction (ss->actions);
+
+ /* The global sigstate is not added to the _hurd_sigstates list.
+ It is created with _hurd_thread_sigstate (MACH_PORT_NULL)
+ but should be accessed through _hurd_global_sigstate. */
+ }
else
{
- struct hurd_sigstate *s;
- for (s = _hurd_sigstates; s != NULL; s = s->next)
- if (s->thread == _hurd_sigthread)
- break;
+ /* Use the global actions as a default for new threads. */
+ struct hurd_sigstate *s = _hurd_global_sigstate;
if (s)
{
__spin_lock (&s->lock);
@@ -116,15 +119,115 @@ _hurd_thread_sigstate (thread_t thread)
}
else
default_sigaction (ss->actions);
- }
- ss->next = _hurd_sigstates;
- _hurd_sigstates = ss;
+ ss->next = _hurd_sigstates;
+ _hurd_sigstates = ss;
+ }
}
__mutex_unlock (&_hurd_siglock);
return ss;
}
libc_hidden_def (_hurd_thread_sigstate)
+
+/* Destroy a sigstate structure. Called by libpthread just before the
+ * corresponding thread is terminated (the kernel thread port must remain valid
+ * until this function is called.) */
+void
+_hurd_sigstate_delete (thread_t thread)
+{
+ struct hurd_sigstate **ssp, *ss;
+
+ __mutex_lock (&_hurd_siglock);
+ for (ssp = &_hurd_sigstates; *ssp; ssp = &(*ssp)->next)
+ if ((*ssp)->thread == thread)
+ break;
+
+ ss = *ssp;
+ if (ss)
+ *ssp = ss->next;
+
+ __mutex_unlock (&_hurd_siglock);
+ if (ss)
+ free (ss);
+}
+
+/* Make SS a global receiver, with pthread signal semantics. */
+void
+_hurd_sigstate_set_global_rcv (struct hurd_sigstate *ss)
+{
+ assert (ss->thread != MACH_PORT_NULL);
+ ss->actions[0].sa_handler = SIG_IGN;
+}
+
+/* Check whether SS is a global receiver. */
+static int
+sigstate_is_global_rcv (const struct hurd_sigstate *ss)
+{
+ return (_hurd_global_sigstate != NULL)
+ && (ss->actions[0].sa_handler == SIG_IGN);
+}
+libc_hidden_def (_hurd_sigstate_delete)
+
+/* Lock/unlock a hurd_sigstate structure. If the accessors below require
+ it, the global sigstate will be locked as well. */
+void
+_hurd_sigstate_lock (struct hurd_sigstate *ss)
+{
+ if (sigstate_is_global_rcv (ss))
+ __spin_lock (&_hurd_global_sigstate->lock);
+ __spin_lock (&ss->lock);
+}
+void
+_hurd_sigstate_unlock (struct hurd_sigstate *ss)
+{
+ __spin_unlock (&ss->lock);
+ if (sigstate_is_global_rcv (ss))
+ __spin_unlock (&_hurd_global_sigstate->lock);
+}
+libc_hidden_def (_hurd_sigstate_set_global_rcv)
+
+/* Retreive a thread's full set of pending signals, including the global
+ ones if appropriate. SS must be locked. */
+sigset_t
+_hurd_sigstate_pending (const struct hurd_sigstate *ss)
+{
+ sigset_t pending = ss->pending;
+ if (sigstate_is_global_rcv (ss))
+ __sigorset (&pending, &pending, &_hurd_global_sigstate->pending);
+ return pending;
+}
+
+/* Clear a pending signal and return the associated detailed
+ signal information. SS must be locked, and must have signal SIGNO
+ pending, either directly or through the global sigstate. */
+static struct hurd_signal_detail
+sigstate_clear_pending (struct hurd_sigstate *ss, int signo)
+{
+ if (sigstate_is_global_rcv (ss)
+ && __sigismember (&_hurd_global_sigstate->pending, signo))
+ {
+ __sigdelset (&_hurd_global_sigstate->pending, signo);
+ return _hurd_global_sigstate->pending_data[signo];
+ }
+
+ assert (__sigismember (&ss->pending, signo));
+ __sigdelset (&ss->pending, signo);
+ return ss->pending_data[signo];
+}
+libc_hidden_def (_hurd_sigstate_lock)
+libc_hidden_def (_hurd_sigstate_unlock)
+
+/* Retreive a thread's action vector. SS must be locked. */
+struct sigaction *
+_hurd_sigstate_actions (struct hurd_sigstate *ss)
+{
+ if (sigstate_is_global_rcv (ss))
+ return _hurd_global_sigstate->actions;
+ else
+ return ss->actions;
+}
+libc_hidden_def (_hurd_sigstate_pending)
+
/* Signal delivery itself is on this page. */
@@ -219,6 +322,8 @@ static void
abort_thread (struct hurd_sigstate *ss, struct machine_thread_all_state *state,
void (*reply) (void))
{
+ assert (ss->thread != MACH_PORT_NULL);
+
if (!(state->set & THREAD_ABORTED))
{
error_t err = __thread_abort (ss->thread);
@@ -367,7 +472,7 @@ _hurdsig_abort_rpcs (struct hurd_sigstate *ss, int signo, int sigthread,
call above will retry their RPCs unless we clear SS->intr_port.
So we clear it for the thread taking a signal when SA_RESTART is
clear, so that its call returns EINTR. */
- if (! signo || !(ss->actions[signo].sa_flags & SA_RESTART))
+ if (! signo || !(_hurd_sigstate_actions (ss) [signo].sa_flags & SA_RESTART))
ss->intr_port = MACH_PORT_NULL;
}
@@ -490,9 +595,11 @@ weak_alias (_hurdsig_preemptors, _hurdsig_preempters)
| sigmask (SIGSTOP) | sigmask (SIGTSTP))
/* 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
+ the signal is delivered, return SS, locked (or, if SS was originally
+ _hurd_global_sigstate, the sigstate of the actual thread the signal
+ was delivered to). If the signal is being traced, return NULL with
+ SS unlocked. */
+static struct hurd_sigstate *
post_signal (struct hurd_sigstate *ss,
int signo, struct hurd_signal_detail *detail,
int untraced, void (*reply) (void))
@@ -545,8 +652,12 @@ 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))
+ if (act == handle && threads[i] == ss->thread)
+ {
+ /* The thread that will run the handler is kept suspended. */
+ ss_suspended = 1;
+ }
+ else if (threads[i] != _hurd_msgport_thread)
{
err = __thread_resume (threads[i]);
assert_perror (err);
@@ -559,9 +670,6 @@ post_signal (struct hurd_sigstate *ss,
(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;
}
error_t err;
@@ -577,13 +685,43 @@ post_signal (struct hurd_sigstate *ss,
}
/* This call is just to check for pending signals. */
- __spin_lock (&ss->lock);
- return 1;
+ _hurd_sigstate_lock (ss);
+ return ss;
}
thread_state.set = 0; /* We know nothing. */
- __spin_lock (&ss->lock);
+ _hurd_sigstate_lock (ss);
+
+ /* If this is a global signal, try to find a thread ready to accept
+ it right away. This is especially important for untraced signals,
+ since going through the global pending mask would de-untrace them. */
+ if (ss->thread == MACH_PORT_NULL)
+ {
+ struct hurd_sigstate *rss;
+
+ __mutex_lock (&_hurd_siglock);
+ for (rss = _hurd_sigstates; rss != NULL; rss = rss->next)
+ {
+ if (! sigstate_is_global_rcv (rss))
+ continue;
+
+ /* The global sigstate is already locked. */
+ __spin_lock (&rss->lock);
+ if (! __sigismember (&rss->blocked, signo))
+ {
+ ss = rss;
+ break;
+ }
+ __spin_unlock (&rss->lock);
+ }
+ __mutex_unlock (&_hurd_siglock);
+ }
+
+ /* We want the preemptors to be able to update the blocking mask
+ without affecting the delivery of this signal, so we save the
+ current value to test against later. */
+ sigset_t blocked = ss->blocked;
/* Check for a preempted signal. Preempted signals can arrive during
critical sections. */
@@ -641,12 +779,12 @@ post_signal (struct hurd_sigstate *ss,
mark_pending ();
else
suspend ();
- __spin_unlock (&ss->lock);
+ _hurd_sigstate_unlock (ss);
reply ();
- return 0;
+ return NULL;
}
- handler = ss->actions[signo].sa_handler;
+ handler = _hurd_sigstate_actions (ss) [signo].sa_handler;
if (handler == SIG_DFL)
/* Figure out the default action for this signal. */
@@ -739,9 +877,7 @@ post_signal (struct hurd_sigstate *ss,
}
/* Handle receipt of a blocked signal, or any signal while stopped. */
- if (act != ignore /* Signals ignored now are forgotten now. */
- && __sigismember (&ss->blocked, signo)
- || (signo != SIGKILL && _hurd_stopped))
+ if (__sigismember (&blocked, signo) || (signo != SIGKILL && _hurd_stopped))
{
mark_pending ();
act = ignore;
@@ -776,6 +912,7 @@ post_signal (struct hurd_sigstate *ss,
now's the time to set it going. */
if (ss_suspended)
{
+ assert (ss->thread != MACH_PORT_NULL);
err = __thread_resume (ss->thread);
assert_perror (err);
ss_suspended = 0;
@@ -820,6 +957,8 @@ post_signal (struct hurd_sigstate *ss,
struct sigcontext *scp, ocontext;
int wait_for_reply, state_changed;
+ assert (ss->thread != MACH_PORT_NULL);
+
/* Stop the thread and abort its pending RPC operations. */
if (! ss_suspended)
{
@@ -956,23 +1095,25 @@ post_signal (struct hurd_sigstate *ss,
}
}
+ struct sigaction *action = & _hurd_sigstate_actions (ss) [signo];
+
/* Backdoor extra argument to signal handler. */
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);
+ __sigorset (&ss->blocked, &ss->blocked, &action->sa_mask);
/* Also block SIGNO unless we're asked not to. */
- if (! (ss->actions[signo].sa_flags & (SA_RESETHAND | SA_NODEFER)))
+ if (! (action->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
+ if (action->sa_flags & SA_RESETHAND
&& signo != SIGILL && signo != SIGTRAP)
- ss->actions[signo].sa_handler = SIG_DFL;
+ action->sa_handler = SIG_DFL;
/* Any sigsuspend call must return after the handler does. */
wake_sigsuspend (ss);
@@ -990,7 +1131,7 @@ post_signal (struct hurd_sigstate *ss,
}
}
- return 1;
+ return ss;
}
/* Return the set of pending signals in SS which should be delivered. */
@@ -1005,7 +1146,7 @@ pending_signals (struct hurd_sigstate *ss)
if (_hurd_stopped || __spin_lock_locked (&ss->critical_section_lock))
return 0;
- return ss->pending & ~ss->blocked;
+ return _hurd_sigstate_pending (ss) & ~ss->blocked;
}
/* Post the specified pending signals in SS and return 1. If one of
@@ -1017,12 +1158,15 @@ post_pending (struct hurd_sigstate *ss, sigset_t pending, void (*reply) (void))
int signo;
struct hurd_signal_detail detail;
+ /* Make sure SS corresponds to an actual thread, since we assume it won't
+ change in post_signal. */
+ assert (ss->thread != MACH_PORT_NULL);
+
for (signo = 1; signo < NSIG; ++signo)
if (__sigismember (&pending, signo))
{
- __sigdelset (&ss->pending, signo);
- detail = ss->pending_data[signo];
- __spin_unlock (&ss->lock);
+ detail = sigstate_clear_pending (ss, signo);
+ _hurd_sigstate_unlock (ss);
/* Will reacquire the lock, except if the signal is traced. */
if (! post_signal (ss, signo, &detail, 0, reply))
@@ -1030,7 +1174,7 @@ post_pending (struct hurd_sigstate *ss, sigset_t pending, void (*reply) (void))
}
/* No more signals pending; SS->lock is still locked. */
- __spin_unlock (&ss->lock);
+ _hurd_sigstate_unlock (ss);
return 1;
}
@@ -1048,14 +1192,14 @@ post_all_pending_signals (void (*reply) (void))
__mutex_lock (&_hurd_siglock);
for (ss = _hurd_sigstates; ss != NULL; ss = ss->next)
{
- __spin_lock (&ss->lock);
+ _hurd_sigstate_lock (ss);
pending = pending_signals (ss);
if (pending)
/* post_pending() below will unlock SS. */
break;
- __spin_unlock (&ss->lock);
+ _hurd_sigstate_unlock (ss);
}
__mutex_unlock (&_hurd_siglock);
@@ -1088,11 +1232,12 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
assert_perror (err);
}
- if (! post_signal (ss, signo, detail, untraced, reply))
+ ss = post_signal (ss, signo, detail, untraced, reply);
+ if (! ss)
return;
/* The signal was neither fatal nor traced. We still hold SS->lock. */
- if (signo != 0)
+ if (signo != 0 && ss->thread != MACH_PORT_NULL)
{
/* The signal has either been ignored or is now being handled. We can
consider it delivered and reply to the killer. */
@@ -1104,8 +1249,9 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
}
else
{
- /* We need to check for pending signals for all threads. */
- __spin_unlock (&ss->lock);
+ /* If this was a process-wide signal or a poll request, we need
+ to check for pending signals for all threads. */
+ _hurd_sigstate_unlock (ss);
if (! post_all_pending_signals (reply))
return;
@@ -1231,9 +1377,10 @@ _S_msg_sig_post (mach_port_t me,
d.code = sigcode;
d.exc = 0;
- /* Post the signal to the designated signal-receiving thread. This will
- reply when the signal can be considered delivered. */
- _hurd_internal_post_signal (_hurd_thread_sigstate (_hurd_sigthread),
+ /* Post the signal to a global receiver thread (or mark it pending in
+ the global sigstate). This will reply when the signal can be
+ considered delivered. */
+ _hurd_internal_post_signal (_hurd_global_sigstate,
signo, &d, reply_port, reply_port_type,
0); /* Stop if traced. */
@@ -1261,7 +1408,7 @@ _S_msg_sig_post_untraced (mach_port_t me,
/* Post the signal to the designated signal-receiving thread. This will
reply when the signal can be considered delivered. */
- _hurd_internal_post_signal (_hurd_thread_sigstate (_hurd_sigthread),
+ _hurd_internal_post_signal (_hurd_global_sigstate,
signo, &d, reply_port, reply_port_type,
1); /* Untraced flag. */
@@ -1272,8 +1419,8 @@ extern void __mig_init (void *);
#include <mach/task_special_ports.h>
-/* Initialize the message port and _hurd_sigthread and start the signal
- thread. */
+/* Initialize the message port, _hurd_global_sigstate, and start the
+ signal thread. */
void
_hurdsig_init (const int *intarray, size_t intarraysize)
@@ -1296,27 +1443,34 @@ _hurdsig_init (const int *intarray, size_t intarraysize)
MACH_MSG_TYPE_MAKE_SEND);
assert_perror (err);
+ /* Initialize the global signal state. */
+ _hurd_global_sigstate = _hurd_thread_sigstate (MACH_PORT_NULL);
+
+ /* We block all signals, and let actual threads pull them from the
+ pending mask. */
+ __sigfillset(& _hurd_global_sigstate->blocked);
+
/* Initialize the main thread's signal state. */
ss = _hurd_self_sigstate ();
- /* Copy inherited values from our parent (or pre-exec process state)
- into the signal settings of the main thread. */
+ /* Mark it as a process-wide signal receiver. Threads in this set use
+ the common action vector in _hurd_global_sigstate. */
+ _hurd_sigstate_set_global_rcv (ss);
+
+ /* Copy inherited signal settings from our parent (or pre-exec process
+ state) */
if (intarraysize > INIT_SIGMASK)
ss->blocked = intarray[INIT_SIGMASK];
if (intarraysize > INIT_SIGPENDING)
- ss->pending = intarray[INIT_SIGPENDING];
+ _hurd_global_sigstate->pending = intarray[INIT_SIGPENDING];
if (intarraysize > INIT_SIGIGN && intarray[INIT_SIGIGN] != 0)
{
int signo;
for (signo = 1; signo < NSIG; ++signo)
if (intarray[INIT_SIGIGN] & __sigmask(signo))
- ss->actions[signo].sa_handler = SIG_IGN;
+ _hurd_global_sigstate->actions[signo].sa_handler = SIG_IGN;
}
- /* Set the default thread to receive task-global signals
- to this one, the main (first) user thread. */
- _hurd_sigthread = ss->thread;
-
/* Start the signal thread listening on the message port. */
#pragma weak __cthread_fork