diff options
Diffstat (limited to 'kern/sleepq.c')
-rw-r--r-- | kern/sleepq.c | 63 |
1 files changed, 28 insertions, 35 deletions
diff --git a/kern/sleepq.c b/kern/sleepq.c index 0d04c6e..44ab53b 100644 --- a/kern/sleepq.c +++ b/kern/sleepq.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Richard Braun. + * Copyright (c) 2017-2018 Richard Braun. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -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; } @@ -503,46 +524,18 @@ sleepq_signal(struct sleepq *sleepq) sleepq_waiter_wakeup(waiter); } -static void -sleepq_wakeup_common(struct sleepq *sleepq) -{ - struct sleepq_waiter *waiter; - - assert(!list_empty(&sleepq->waiters)); - - waiter = list_last_entry(&sleepq->waiters, struct sleepq_waiter, node); - sleepq_waiter_wakeup(waiter); -} - void sleepq_broadcast(struct sleepq *sleepq) { struct sleepq_waiter *waiter; - if (sleepq->oldest_waiter == NULL) { - goto out; - } - - list_for_each_entry(&sleepq->waiters, waiter, node) { - sleepq_waiter_set_pending_wakeup(waiter); - - if (waiter == sleepq->oldest_waiter) { - break; - } - } - - sleepq->oldest_waiter = NULL; - -out: - sleepq_wakeup_common(sleepq); -} + waiter = sleepq->oldest_waiter; -void -sleepq_wakeup(struct sleepq *sleepq) -{ - if (list_empty(&sleepq->waiters)) { + if (!waiter) { return; } - sleepq_wakeup_common(sleepq); + sleepq->oldest_waiter = NULL; + sleepq_waiter_set_pending_wakeup(waiter); + sleepq_waiter_wakeup(waiter); } |