summaryrefslogtreecommitdiffstats
path: root/src/sighook.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sighook.c')
-rw-r--r--src/sighook.c55
1 files changed, 38 insertions, 17 deletions
diff --git a/src/sighook.c b/src/sighook.c
index ff5b96f..6d6ff01 100644
--- a/src/sighook.c
+++ b/src/sighook.c
@@ -52,6 +52,8 @@ struct arc {
/** Initialize an arc. */
static void arc_init(struct arc *arc) {
+ bfs_verify(atomic_is_lock_free(&arc->refs));
+
atomic_init(&arc->refs, 0);
arc->ptr = NULL;
@@ -166,6 +168,8 @@ static void *RCU_NULL = &RCU_NULL;
/** Initialize an RCU block. */
static void rcu_init(struct rcu *rcu) {
+ bfs_verify(atomic_is_lock_free(&rcu->active));
+
atomic_init(&rcu->active, 0);
arc_init(&rcu->slots[0]);
arc_init(&rcu->slots[1]);
@@ -323,11 +327,16 @@ static int rcu_sigtable_add(struct rcu *rcu, struct sighook *hook) {
return 0;
}
-/** The global table of signal hooks. */
-static struct rcu rcu_sighooks;
-/** The global table of atsigexit() hooks. */
+/** The sharded table of signal hooks. */
+static struct rcu rcu_sighooks[64];
+/** The table of atsigexit() hooks. */
static struct rcu rcu_exithooks;
+/** Get the table for a particular signal. */
+static struct rcu *sigshard(int sig) {
+ return &rcu_sighooks[sig % countof(rcu_sighooks)];
+}
+
/** Mutex for initialization and RCU writer exclusion. */
static pthread_mutex_t sigmutex = PTHREAD_MUTEX_INITIALIZER;
@@ -475,7 +484,8 @@ static void sigdispatch(int sig, siginfo_t *info, void *context) {
int error = errno;
// Run the normal hooks
- enum sigflags flags = run_hooks(&rcu_sighooks, sig, info);
+ struct rcu *shard = sigshard(sig);
+ enum sigflags flags = run_hooks(shard, sig, info);
// Run the atsigexit() hooks, if we're exiting
if (!(flags & SH_CONTINUE) && is_fatal(sig)) {
@@ -501,8 +511,12 @@ static int siginit(int sig) {
|| sigemptyset(&action.sa_mask) != 0) {
return -1;
}
- rcu_init(&rcu_sighooks);
+
+ for (size_t i = 0; i < countof(rcu_sighooks); ++i) {
+ rcu_init(&rcu_sighooks[i]);
+ }
rcu_init(&rcu_exithooks);
+
initialized = true;
}
@@ -552,7 +566,8 @@ struct sighook *sighook(int sig, sighook_fn *fn, void *arg, enum sigflags flags)
goto done;
}
- ret = sighook_impl(&rcu_sighooks, sig, fn, arg, flags);
+ struct rcu *shard = sigshard(sig);
+ ret = sighook_impl(shard, sig, fn, arg, flags);
done:
mutex_unlock(&sigmutex);
return ret;
@@ -561,32 +576,38 @@ done:
struct sighook *atsigexit(sighook_fn *fn, void *arg) {
mutex_lock(&sigmutex);
- struct sighook *ret = NULL;
-
for (size_t i = 0; i < countof(FATAL_SIGNALS); ++i) {
- if (siginit(FATAL_SIGNALS[i]) != 0) {
- goto done;
- }
+ // Ignore errors; atsigexit() is best-effort anyway and things
+ // like sanitizer runtimes or valgrind may reserve signals for
+ // their own use
+ siginit(FATAL_SIGNALS[i]);
}
#ifdef SIGRTMIN
for (int i = SIGRTMIN; i <= SIGRTMAX; ++i) {
- if (siginit(i) != 0) {
- goto done;
- }
+ siginit(i);
}
#endif
- ret = sighook_impl(&rcu_exithooks, 0, fn, arg, 0);
-done:
+ struct sighook *ret = sighook_impl(&rcu_exithooks, 0, fn, arg, 0);
mutex_unlock(&sigmutex);
return ret;
}
void sigunhook(struct sighook *hook) {
+ if (!hook) {
+ return;
+ }
+
mutex_lock(&sigmutex);
- struct rcu *rcu = hook->sig ? &rcu_sighooks : &rcu_exithooks;
+ struct rcu *rcu;
+ if (hook->sig) {
+ rcu = sigshard(hook->sig);
+ } else {
+ rcu = &rcu_exithooks;
+ }
+
struct sigtable *table = rcu_peek(rcu);
bfs_verify(sigtable_del(table, hook) == 0);