summaryrefslogtreecommitdiff
path: root/src/signal
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2013-08-09 21:03:47 -0400
committerRich Felker <dalias@aerifal.cx>2013-08-09 21:03:47 -0400
commit3c5c5e6f926feea7b823a96c2872885b100fe31f (patch)
treebec398659ee1f88f8150348940c2a7c843c395fe /src/signal
parent65d7aa4dfde9697b93d765f2f736e5f4a38bdfd5 (diff)
downloadmusl-3c5c5e6f926feea7b823a96c2872885b100fe31f.tar.gz
musl-3c5c5e6f926feea7b823a96c2872885b100fe31f.tar.bz2
musl-3c5c5e6f926feea7b823a96c2872885b100fe31f.tar.xz
musl-3c5c5e6f926feea7b823a96c2872885b100fe31f.zip
optimize posix_spawn to avoid spurious sigaction syscalls
the trick here is that sigaction can track for us which signals have ever had a signal handler set for them, and only those signals need to be considered for reset. this tracking mask may have false positives, since it is impossible to remove bits from it without race conditions. false negatives are not possible since the mask is updated with atomic operations prior to making the sigaction syscall. implementation-internal signals are set to SIG_IGN rather than SIG_DFL so that a signal raised in the parent (e.g. calling pthread_cancel on the thread executing pthread_spawn) does not have any chance make it to the child, where it would cause spurious termination by signal. this change reduces the minimum/typical number of syscalls in the child from around 70 to 4 (including execve). this should greatly improve the performance of posix_spawn and other interfaces which use it (popen and system). to facilitate these changes, sigismember is also changed to return 0 rather than -1 for invalid signals, and to return the actual status of implementation-internal signals. POSIX allows but does not require an error on invalid signal numbers, and in fact returning an error tends to confuse applications which wrongly assume the return value of sigismember is boolean.
Diffstat (limited to 'src/signal')
-rw-r--r--src/signal/sigaction.c16
-rw-r--r--src/signal/sigismember.c5
2 files changed, 16 insertions, 5 deletions
diff --git a/src/signal/sigaction.c b/src/signal/sigaction.c
index 1f09bb96..5499bd18 100644
--- a/src/signal/sigaction.c
+++ b/src/signal/sigaction.c
@@ -12,12 +12,26 @@ void __restore(), __restore_rt();
static pthread_t dummy(void) { return 0; }
weak_alias(dummy, __pthread_self_def);
+static unsigned long handler_set[_NSIG/(8*sizeof(long))];
+
+void __get_handler_set(sigset_t *set)
+{
+ memcpy(set, handler_set, sizeof handler_set);
+}
+
int __libc_sigaction(int sig, const struct sigaction *restrict sa, struct sigaction *restrict old)
{
struct k_sigaction ksa, ksa_old;
+ if (sig >= (unsigned)_NSIG) {
+ errno = EINVAL;
+ return -1;
+ }
if (sa) {
- if ((uintptr_t)sa->sa_handler > 1UL)
+ if ((uintptr_t)sa->sa_handler > 1UL) {
+ a_or_l(handler_set+(sig-1)/(8*sizeof(long)),
+ 1UL<<(sig-1)%(8*sizeof(long)));
__pthread_self_def();
+ }
ksa.handler = sa->sa_handler;
ksa.flags = sa->sa_flags | SA_RESTORER;
ksa.restorer = (sa->sa_flags & SA_SIGINFO) ? __restore_rt : __restore;
diff --git a/src/signal/sigismember.c b/src/signal/sigismember.c
index e887b95f..1a22108b 100644
--- a/src/signal/sigismember.c
+++ b/src/signal/sigismember.c
@@ -4,9 +4,6 @@
int sigismember(const sigset_t *set, int sig)
{
unsigned s = sig-1;
- if (s >= 8*sizeof(sigset_t) || sig-32U<3) {
- errno = EINVAL;
- return -1;
- }
+ if (s >= 8*sizeof(sigset_t)) return 0;
return !!(set->__bits[s/8/sizeof *set->__bits] & 1UL<<(s&8*sizeof *set->__bits-1));
}