summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/kern/subr_sleepqueue.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sys/kern/subr_sleepqueue.c')
-rw-r--r--freebsd/sys/kern/subr_sleepqueue.c11
1 files changed, 8 insertions, 3 deletions
diff --git a/freebsd/sys/kern/subr_sleepqueue.c b/freebsd/sys/kern/subr_sleepqueue.c
index a42c62f8..d0288d67 100644
--- a/freebsd/sys/kern/subr_sleepqueue.c
+++ b/freebsd/sys/kern/subr_sleepqueue.c
@@ -1111,7 +1111,7 @@ int
sleepq_broadcast(void *wchan, int flags, int pri, int queue)
{
struct sleepqueue *sq;
- struct thread *td;
+ struct thread *td, *tdn;
int wakeup_swapper;
CTR2(KTR_PROC, "sleepq_broadcast(%p, %d)", wchan, flags);
@@ -1123,9 +1123,14 @@ sleepq_broadcast(void *wchan, int flags, int pri, int queue)
KASSERT(sq->sq_type == (flags & SLEEPQ_TYPE),
("%s: mismatch between sleep/wakeup and cv_*", __func__));
- /* Resume all blocked threads on the sleep queue. */
+ /*
+ * Resume all blocked threads on the sleep queue. The last thread will
+ * be given ownership of sq and may re-enqueue itself before
+ * sleepq_resume_thread() returns, so we must cache the "next" queue
+ * item at the beginning of the final iteration.
+ */
wakeup_swapper = 0;
- while ((td = TAILQ_FIRST(&sq->sq_blocked[queue])) != NULL) {
+ TAILQ_FOREACH_SAFE(td, &sq->sq_blocked[queue], td_slpq, tdn) {
thread_lock(td);
wakeup_swapper |= sleepq_resume_thread(sq, td, pri);
thread_unlock(td);