diff options
author | Richard Braun <rbraun@sceen.net> | 2018-01-30 20:44:30 +0100 |
---|---|---|
committer | Richard Braun <rbraun@sceen.net> | 2018-02-07 00:37:41 +0100 |
commit | 2a15d5b978d7dcfd4b27a14e53d7288d71c9b518 (patch) | |
tree | 2022889db04a0b9191bd7e91f643abac3e0de633 /kern | |
parent | 9967e907feda967f237c30430f47357bc91332f5 (diff) |
kern/sleepq: make sleepq_broadcast real-time friendly
Diffstat (limited to 'kern')
-rw-r--r-- | kern/sleepq.c | 38 |
1 files changed, 27 insertions, 11 deletions
diff --git a/kern/sleepq.c b/kern/sleepq.c index 1fd1ac42..44ab53bc 100644 --- a/kern/sleepq.c +++ b/kern/sleepq.c @@ -431,6 +431,16 @@ sleepq_remove_waiter(struct sleepq *sleepq, struct sleepq_waiter *waiter) list_remove(&waiter->node); } +static struct sleepq_waiter * +sleepq_get_last_waiter(struct sleepq *sleepq) +{ + if (list_empty(&sleepq->waiters)) { + return NULL; + } + + return list_last_entry(&sleepq->waiters, struct sleepq_waiter, node); +} + bool sleepq_empty(const struct sleepq *sleepq) { @@ -441,7 +451,7 @@ static int sleepq_wait_common(struct sleepq *sleepq, const char *wchan, bool timed, uint64_t ticks) { - struct sleepq_waiter waiter; + struct sleepq_waiter waiter, *next; struct thread *thread; int error; @@ -469,6 +479,17 @@ sleepq_wait_common(struct sleepq *sleepq, const char *wchan, sleepq_remove_waiter(sleepq, &waiter); + /* + * Chain wake-ups here to prevent broadacasting from walking a list + * with preemption disabled. Note that this doesn't guard against + * the thundering herd effect for condition variables. + */ + next = sleepq_get_last_waiter(sleepq); + + if (next) { + sleepq_waiter_wakeup(next); + } + return error; } @@ -508,18 +529,13 @@ sleepq_broadcast(struct sleepq *sleepq) { struct sleepq_waiter *waiter; - if (sleepq->oldest_waiter == NULL) { + waiter = sleepq->oldest_waiter; + + if (!waiter) { return; } sleepq->oldest_waiter = NULL; - - list_for_each_entry(&sleepq->waiters, waiter, node) { - sleepq_waiter_set_pending_wakeup(waiter); - sleepq_waiter_wakeup(waiter); - - if (waiter == sleepq->oldest_waiter) { - break; - } - } + sleepq_waiter_set_pending_wakeup(waiter); + sleepq_waiter_wakeup(waiter); } |