diff options
Diffstat (limited to 'src/sighook.c')
-rw-r--r-- | src/sighook.c | 55 |
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); |