Serenity Operating System
1/*
2 * Copyright (c) 2020, the SerenityOS developers.
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <Kernel/Debug.h>
8#include <Kernel/Thread.h>
9#include <Kernel/WaitQueue.h>
10
11namespace Kernel {
12
13bool WaitQueue::should_add_blocker(Thread::Blocker& b, void*)
14{
15 VERIFY(m_lock.is_locked());
16 VERIFY(b.blocker_type() == Thread::Blocker::Type::Queue);
17 if (m_wake_requested) {
18 m_wake_requested = false;
19 dbgln_if(WAITQUEUE_DEBUG, "WaitQueue @ {}: do not block thread {}", this, b.thread());
20 return false;
21 }
22 dbgln_if(WAITQUEUE_DEBUG, "WaitQueue @ {}: should block thread {}", this, b.thread());
23 return true;
24}
25
26u32 WaitQueue::wake_one()
27{
28 u32 did_wake = 0;
29 SpinlockLocker lock(m_lock);
30 dbgln_if(WAITQUEUE_DEBUG, "WaitQueue @ {}: wake_one", this);
31 bool did_unblock_one = unblock_all_blockers_whose_conditions_are_met_locked([&](Thread::Blocker& b, void*, bool& stop_iterating) {
32 VERIFY(b.blocker_type() == Thread::Blocker::Type::Queue);
33 auto& blocker = static_cast<Thread::WaitQueueBlocker&>(b);
34 dbgln_if(WAITQUEUE_DEBUG, "WaitQueue @ {}: wake_one unblocking {}", this, blocker.thread());
35 if (blocker.unblock()) {
36 stop_iterating = true;
37 did_wake = 1;
38 return true;
39 }
40 return false;
41 });
42 m_wake_requested = !did_unblock_one;
43 dbgln_if(WAITQUEUE_DEBUG, "WaitQueue @ {}: wake_one woke {} threads", this, did_wake);
44 return did_wake;
45}
46
47u32 WaitQueue::wake_n(u32 wake_count)
48{
49 if (wake_count == 0)
50 return 0; // should we assert instead?
51 SpinlockLocker lock(m_lock);
52 dbgln_if(WAITQUEUE_DEBUG, "WaitQueue @ {}: wake_n({})", this, wake_count);
53 u32 did_wake = 0;
54
55 bool did_unblock_some = unblock_all_blockers_whose_conditions_are_met_locked([&](Thread::Blocker& b, void*, bool& stop_iterating) {
56 VERIFY(b.blocker_type() == Thread::Blocker::Type::Queue);
57 auto& blocker = static_cast<Thread::WaitQueueBlocker&>(b);
58 dbgln_if(WAITQUEUE_DEBUG, "WaitQueue @ {}: wake_n unblocking {}", this, blocker.thread());
59 VERIFY(did_wake < wake_count);
60 if (blocker.unblock()) {
61 if (++did_wake >= wake_count)
62 stop_iterating = true;
63 return true;
64 }
65 return false;
66 });
67 m_wake_requested = !did_unblock_some;
68 dbgln_if(WAITQUEUE_DEBUG, "WaitQueue @ {}: wake_n({}) woke {} threads", this, wake_count, did_wake);
69 return did_wake;
70}
71
72u32 WaitQueue::wake_all()
73{
74 SpinlockLocker lock(m_lock);
75
76 dbgln_if(WAITQUEUE_DEBUG, "WaitQueue @ {}: wake_all", this);
77 u32 did_wake = 0;
78
79 bool did_unblock_any = unblock_all_blockers_whose_conditions_are_met_locked([&](Thread::Blocker& b, void*, bool&) {
80 VERIFY(b.blocker_type() == Thread::Blocker::Type::Queue);
81 auto& blocker = static_cast<Thread::WaitQueueBlocker&>(b);
82
83 dbgln_if(WAITQUEUE_DEBUG, "WaitQueue @ {}: wake_all unblocking {}", this, blocker.thread());
84
85 if (blocker.unblock()) {
86 did_wake++;
87 return true;
88 }
89 return false;
90 });
91 m_wake_requested = !did_unblock_any;
92 dbgln_if(WAITQUEUE_DEBUG, "WaitQueue @ {}: wake_all woke {} threads", this, did_wake);
93 return did_wake;
94}
95
96}