Serenity Operating System
at master 160 lines 5.9 kB view raw
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/FutexQueue.h> 9#include <Kernel/Thread.h> 10 11namespace Kernel { 12 13FutexQueue::FutexQueue() = default; 14FutexQueue::~FutexQueue() = default; 15 16bool FutexQueue::should_add_blocker(Thread::Blocker& b, void*) 17{ 18 VERIFY(m_lock.is_locked()); 19 VERIFY(b.blocker_type() == Thread::Blocker::Type::Futex); 20 21 VERIFY(m_imminent_waits > 0); 22 m_imminent_waits--; 23 24 if (m_was_removed) { 25 dbgln_if(FUTEXQUEUE_DEBUG, "FutexQueue @ {}: should not block thread {}: was removed", this, b.thread()); 26 return false; 27 } 28 dbgln_if(FUTEXQUEUE_DEBUG, "FutexQueue @ {}: should block thread {}", this, b.thread()); 29 30 return true; 31} 32 33ErrorOr<u32> FutexQueue::wake_n_requeue(u32 wake_count, Function<ErrorOr<FutexQueue*>()> const& get_target_queue, u32 requeue_count, bool& is_empty, bool& is_empty_target) 34{ 35 is_empty_target = false; 36 SpinlockLocker lock(m_lock); 37 38 dbgln_if(FUTEXQUEUE_DEBUG, "FutexQueue @ {}: wake_n_requeue({}, {})", this, wake_count, requeue_count); 39 40 u32 did_wake = 0, did_requeue = 0; 41 unblock_all_blockers_whose_conditions_are_met_locked([&](Thread::Blocker& b, void*, bool& stop_iterating) { 42 VERIFY(b.blocker_type() == Thread::Blocker::Type::Futex); 43 auto& blocker = static_cast<Thread::FutexBlocker&>(b); 44 45 dbgln_if(FUTEXQUEUE_DEBUG, "FutexQueue @ {}: wake_n_requeue unblocking {}", this, blocker.thread()); 46 VERIFY(did_wake < wake_count); 47 if (blocker.unblock()) { 48 if (++did_wake >= wake_count) 49 stop_iterating = true; 50 return true; 51 } 52 return false; 53 }); 54 is_empty = is_empty_and_no_imminent_waits_locked(); 55 if (requeue_count > 0) { 56 auto blockers_to_requeue = do_take_blockers(requeue_count); 57 if (!blockers_to_requeue.is_empty()) { 58 if (auto* target_futex_queue = TRY(get_target_queue())) { 59 dbgln_if(FUTEXQUEUE_DEBUG, "FutexQueue @ {}: wake_n_requeue requeueing {} blockers to {}", this, blockers_to_requeue.size(), target_futex_queue); 60 61 // While still holding m_lock, notify each blocker 62 for (auto& info : blockers_to_requeue) { 63 VERIFY(info.blocker->blocker_type() == Thread::Blocker::Type::Futex); 64 auto& blocker = *static_cast<Thread::FutexBlocker*>(info.blocker); 65 blocker.begin_requeue(); 66 } 67 68 lock.unlock(); 69 did_requeue = blockers_to_requeue.size(); 70 71 SpinlockLocker target_lock(target_futex_queue->m_lock); 72 // Now that we have the lock of the target, append the blockers 73 // and notify them that they completed the move 74 for (auto& info : blockers_to_requeue) { 75 VERIFY(info.blocker->blocker_type() == Thread::Blocker::Type::Futex); 76 auto& blocker = *static_cast<Thread::FutexBlocker*>(info.blocker); 77 blocker.finish_requeue(*target_futex_queue); 78 } 79 target_futex_queue->do_append_blockers(move(blockers_to_requeue)); 80 is_empty_target = target_futex_queue->is_empty_and_no_imminent_waits_locked(); 81 } else { 82 dbgln_if(FUTEXQUEUE_DEBUG, "FutexQueue @ {}: wake_n_requeue could not get target queue to requeue {} blockers", this, blockers_to_requeue.size()); 83 do_append_blockers(move(blockers_to_requeue)); 84 } 85 } 86 } 87 return did_wake + did_requeue; 88} 89 90u32 FutexQueue::wake_n(u32 wake_count, Optional<u32> const& bitset, bool& is_empty) 91{ 92 if (wake_count == 0) { 93 is_empty = false; 94 return 0; // should we assert instead? 95 } 96 SpinlockLocker lock(m_lock); 97 dbgln_if(FUTEXQUEUE_DEBUG, "FutexQueue @ {}: wake_n({})", this, wake_count); 98 u32 did_wake = 0; 99 unblock_all_blockers_whose_conditions_are_met_locked([&](Thread::Blocker& b, void*, bool& stop_iterating) { 100 VERIFY(b.blocker_type() == Thread::Blocker::Type::Futex); 101 auto& blocker = static_cast<Thread::FutexBlocker&>(b); 102 103 dbgln_if(FUTEXQUEUE_DEBUG, "FutexQueue @ {}: wake_n unblocking {}", this, blocker.thread()); 104 VERIFY(did_wake < wake_count); 105 if (bitset.has_value() ? blocker.unblock_bitset(bitset.value()) : blocker.unblock()) { 106 if (++did_wake >= wake_count) 107 stop_iterating = true; 108 return true; 109 } 110 return false; 111 }); 112 is_empty = is_empty_and_no_imminent_waits_locked(); 113 return did_wake; 114} 115 116u32 FutexQueue::wake_all(bool& is_empty) 117{ 118 SpinlockLocker lock(m_lock); 119 dbgln_if(FUTEXQUEUE_DEBUG, "FutexQueue @ {}: wake_all", this); 120 u32 did_wake = 0; 121 unblock_all_blockers_whose_conditions_are_met_locked([&](Thread::Blocker& b, void*, bool&) { 122 VERIFY(b.blocker_type() == Thread::Blocker::Type::Futex); 123 auto& blocker = static_cast<Thread::FutexBlocker&>(b); 124 dbgln_if(FUTEXQUEUE_DEBUG, "FutexQueue @ {}: wake_all unblocking {}", this, blocker.thread()); 125 if (blocker.unblock(true)) { 126 did_wake++; 127 return true; 128 } 129 return false; 130 }); 131 is_empty = is_empty_and_no_imminent_waits_locked(); 132 return did_wake; 133} 134 135bool FutexQueue::is_empty_and_no_imminent_waits_locked() 136{ 137 return m_imminent_waits == 0 && is_empty_locked(); 138} 139 140bool FutexQueue::queue_imminent_wait() 141{ 142 SpinlockLocker lock(m_lock); 143 if (m_was_removed) 144 return false; 145 m_imminent_waits++; 146 return true; 147} 148 149bool FutexQueue::try_remove() 150{ 151 SpinlockLocker lock(m_lock); 152 if (m_was_removed) 153 return false; 154 if (!is_empty_and_no_imminent_waits_locked()) 155 return false; 156 m_was_removed = true; 157 return true; 158} 159 160}