Skip to content

Commit 75b7f70

Browse files
committed
sighook: Make sigunhook() O(1)
1 parent a9a2709 commit 75b7f70

1 file changed

Lines changed: 51 additions & 41 deletions

File tree

src/sighook.c

Lines changed: 51 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -240,31 +240,52 @@ struct sighook {
240240
sighook_fn *fn;
241241
/** An argument to pass to the function. */
242242
void *arg;
243+
244+
/** The RCU pointer to this hook. */
245+
struct rcu *self;
243246
/** The next hook in the list. */
244247
struct rcu next;
245248
};
246249

247-
/** Add a hook to a linked list. */
248-
static void sigpush(struct rcu *rcu, struct sighook *hook) {
249-
struct sighook *next = rcu_peek(rcu);
250-
rcu_init(&hook->next, next);
251-
rcu_update(rcu, hook);
250+
/**
251+
* An RCU-protected linked list of signal hooks.
252+
*/
253+
struct siglist {
254+
/** The first hook in the list. */
255+
struct rcu head;
256+
/** &last->next */
257+
struct rcu *tail;
258+
};
259+
260+
/** Initialize a siglist. */
261+
static void siglist_init(struct siglist *list) {
262+
rcu_init(&list->head, NULL);
263+
list->tail = &list->head;
264+
}
265+
266+
/** Append a hook to a linked list. */
267+
static void sigpush(struct siglist *list, struct sighook *hook) {
268+
hook->self = list->tail;
269+
list->tail = &hook->next;
270+
rcu_init(&hook->next, NULL);
271+
rcu_update(hook->self, hook);
252272
}
253273

254274
/** Remove a hook from the linked list. */
255-
static void sigpop(struct rcu *rcu, struct sighook *hook) {
275+
static void sigpop(struct siglist *list, struct sighook *hook) {
256276
struct sighook *next = rcu_peek(&hook->next);
257-
rcu_update(rcu, next);
277+
rcu_update(hook->self, next);
278+
if (next) {
279+
next->self = hook->self;
280+
}
258281
}
259282

260283
/** The lists of signal hooks. */
261-
static struct rcu rcu_sighooks[64];
262-
/** The list of atsigexit() hooks. */
263-
static struct rcu rcu_exithooks;
284+
static struct siglist sighooks[64];
264285

265286
/** Get the hook list for a particular signal. */
266-
static struct rcu *siglist(int sig) {
267-
return &rcu_sighooks[sig % countof(rcu_sighooks)];
287+
static struct siglist *siglist(int sig) {
288+
return &sighooks[sig % countof(sighooks)];
268289
}
269290

270291
/** Mutex for initialization and RCU writer exclusion. */
@@ -365,11 +386,11 @@ static noreturn void reraise(int sig) {
365386
}
366387

367388
/** Find any matching hooks and run them. */
368-
static enum sigflags run_hooks(struct rcu *rcu, int sig, siginfo_t *info) {
389+
static enum sigflags run_hooks(struct siglist *list, int sig, siginfo_t *info) {
369390
enum sigflags ret = 0;
370391

371392
struct arc *slot = NULL;
372-
struct sighook *hook = rcu_read(rcu, &slot);
393+
struct sighook *hook = rcu_read(&list->head, &slot);
373394
while (hook) {
374395
if (hook->sig == sig || hook->sig == 0) {
375396
hook->fn(sig, info, hook->arg);
@@ -407,12 +428,13 @@ static void sigdispatch(int sig, siginfo_t *info, void *context) {
407428
int error = errno;
408429

409430
// Run the normal hooks
410-
struct rcu *rcu = siglist(sig);
411-
enum sigflags flags = run_hooks(rcu, sig, info);
431+
struct siglist *list = siglist(sig);
432+
enum sigflags flags = run_hooks(list, sig, info);
412433

413434
// Run the atsigexit() hooks, if we're exiting
414435
if (!(flags & SH_CONTINUE) && is_fatal(sig)) {
415-
run_hooks(&rcu_exithooks, sig, info);
436+
list = siglist(0);
437+
run_hooks(list, sig, info);
416438
reraise(sig);
417439
}
418440

@@ -435,10 +457,9 @@ static int siginit(int sig) {
435457
return -1;
436458
}
437459

438-
for (size_t i = 0; i < countof(rcu_sighooks); ++i) {
439-
rcu_init(&rcu_sighooks[i], NULL);
460+
for (size_t i = 0; i < countof(sighooks); ++i) {
461+
siglist_init(&sighooks[i]);
440462
}
441-
rcu_init(&rcu_exithooks, NULL);
442463

443464
initialized = true;
444465
}
@@ -462,7 +483,7 @@ static int siginit(int sig) {
462483
}
463484

464485
/** Shared sighook()/atsigexit() implementation. */
465-
static struct sighook *sighook_impl(struct rcu *rcu, int sig, sighook_fn *fn, void *arg, enum sigflags flags) {
486+
static struct sighook *sighook_impl(int sig, sighook_fn *fn, void *arg, enum sigflags flags) {
466487
struct sighook *hook = ALLOC(struct sighook);
467488
if (!hook) {
468489
return NULL;
@@ -473,21 +494,21 @@ static struct sighook *sighook_impl(struct rcu *rcu, int sig, sighook_fn *fn, vo
473494
hook->fn = fn;
474495
hook->arg = arg;
475496

476-
sigpush(rcu, hook);
497+
struct siglist *list = siglist(sig);
498+
sigpush(list, hook);
477499
return hook;
478500
}
479501

480502
struct sighook *sighook(int sig, sighook_fn *fn, void *arg, enum sigflags flags) {
503+
bfs_assert(sig > 0);
504+
481505
mutex_lock(&sigmutex);
482506

483507
struct sighook *ret = NULL;
484-
if (siginit(sig) != 0) {
485-
goto done;
508+
if (siginit(sig) == 0) {
509+
ret = sighook_impl(sig, fn, arg, flags);
486510
}
487511

488-
struct rcu *rcu = siglist(sig);
489-
ret = sighook_impl(rcu, sig, fn, arg, flags);
490-
done:
491512
mutex_unlock(&sigmutex);
492513
return ret;
493514
}
@@ -508,7 +529,7 @@ struct sighook *atsigexit(sighook_fn *fn, void *arg) {
508529
}
509530
#endif
510531

511-
struct sighook *ret = sighook_impl(&rcu_exithooks, 0, fn, arg, 0);
532+
struct sighook *ret = sighook_impl(0, fn, arg, 0);
512533
mutex_unlock(&sigmutex);
513534
return ret;
514535
}
@@ -520,19 +541,8 @@ void sigunhook(struct sighook *hook) {
520541

521542
mutex_lock(&sigmutex);
522543

523-
struct rcu *rcu;
524-
if (hook->sig) {
525-
rcu = siglist(hook->sig);
526-
} else {
527-
rcu = &rcu_exithooks;
528-
}
529-
530-
struct sighook *node = rcu_peek(rcu);
531-
while (node != hook) {
532-
rcu = &node->next;
533-
node = rcu_peek(rcu);
534-
}
535-
sigpop(rcu, hook);
544+
struct siglist *list = siglist(hook->sig);
545+
sigpop(list, hook);
536546

537547
mutex_unlock(&sigmutex);
538548

0 commit comments

Comments
 (0)