diff options
-rw-r--r-- | src/sighook.c | 20 | ||||
-rw-r--r-- | src/sighook.h | 2 |
2 files changed, 21 insertions, 1 deletions
diff --git a/src/sighook.c b/src/sighook.c index a294206..6fd4c86 100644 --- a/src/sighook.c +++ b/src/sighook.c @@ -254,6 +254,8 @@ struct sighook { sighook_fn *fn; /** An argument to pass to the function. */ void *arg; + /** Flag for SH_ONESHOT. */ + atomic bool armed; /** The RCU pointer to this hook. */ struct rcu *self; @@ -402,6 +404,21 @@ fail: abort(); } +/** Check whether we should run a hook. */ +static bool should_run(int sig, struct sighook *hook) { + if (hook->sig != sig && hook->sig != 0) { + return false; + } + + if (hook->flags & SH_ONESHOT) { + if (!exchange(&hook->armed, false, relaxed)) { + return false; + } + } + + return true; +} + /** Find any matching hooks and run them. */ static enum sigflags run_hooks(struct siglist *list, int sig, siginfo_t *info) { enum sigflags ret = 0; @@ -409,7 +426,7 @@ static enum sigflags run_hooks(struct siglist *list, int sig, siginfo_t *info) { struct arc *slot = NULL; struct sighook *hook = rcu_read(&list->head, &slot); while (hook) { - if (hook->sig == sig || hook->sig == 0) { + if (should_run(sig, hook)) { hook->fn(sig, info, hook->arg); ret |= hook->flags; } @@ -522,6 +539,7 @@ static struct sighook *sighook_impl(int sig, sighook_fn *fn, void *arg, enum sig hook->flags = flags; hook->fn = fn; hook->arg = arg; + atomic_init(&hook->armed, true); struct siglist *list = siglist(sig); sigpush(list, hook); diff --git a/src/sighook.h b/src/sighook.h index 3bece21..87bba4e 100644 --- a/src/sighook.h +++ b/src/sighook.h @@ -21,6 +21,8 @@ struct sighook; enum sigflags { /** Suppress the default action for this signal. */ SH_CONTINUE = 1 << 0, + /** Only run this hook once. */ + SH_ONESHOT = 1 << 1, }; /** |