mirror of
https://github.com/SerenityOS/serenity
synced 2024-10-15 12:23:15 +00:00
Kernel: Add a WaitQueue for Thread queueing/waking and use it for Lock
The kernel's Lock class now uses a proper wait queue internally instead of just having everyone wake up regularly to try to acquire the lock. We also keep the donation mechanism, so that whenever someone tries to take the lock and fails, that thread donates the remainder of its timeslice to the current lock holder. After unlocking a Lock, the unlocking thread calls WaitQueue::wake_one, which unblocks the next thread in queue.
This commit is contained in:
parent
cada332e95
commit
f067730f6b
|
@ -1,4 +1,5 @@
|
|||
#include <Kernel/Lock.h>
|
||||
#include <Kernel/Thread.h>
|
||||
|
||||
void Lock::lock()
|
||||
{
|
||||
|
@ -18,8 +19,8 @@ void Lock::lock()
|
|||
return;
|
||||
}
|
||||
m_lock.store(false, AK::memory_order_release);
|
||||
(void)current->donate_remaining_timeslice_and_block<Thread::WaitQueueBlocker>(m_holder, m_name, m_queue);
|
||||
}
|
||||
Scheduler::donate_to(m_holder, m_name);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,10 +37,12 @@ void Lock::unlock()
|
|||
return;
|
||||
}
|
||||
m_holder = nullptr;
|
||||
m_queue.wake_one();
|
||||
m_lock.store(false, AK::memory_order_release);
|
||||
return;
|
||||
}
|
||||
Scheduler::donate_to(m_holder, m_name);
|
||||
// I don't know *who* is using "m_lock", so just yield.
|
||||
Scheduler::yield();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,7 +67,10 @@ bool Lock::unlock_if_locked()
|
|||
}
|
||||
m_holder = nullptr;
|
||||
m_lock.store(false, AK::memory_order_release);
|
||||
m_queue.wake_one();
|
||||
return true;
|
||||
}
|
||||
// I don't know *who* is using "m_lock", so just yield.
|
||||
Scheduler::yield();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/Assertions.h>
|
||||
#include <AK/Types.h>
|
||||
#include <AK/Atomic.h>
|
||||
#include <AK/Types.h>
|
||||
#include <Kernel/Arch/i386/CPU.h>
|
||||
#include <Kernel/KSyms.h>
|
||||
#include <Kernel/Scheduler.h>
|
||||
#include <Kernel/WaitQueue.h>
|
||||
|
||||
class Thread;
|
||||
extern Thread* current;
|
||||
|
@ -29,6 +30,7 @@ private:
|
|||
u32 m_level { 0 };
|
||||
Thread* m_holder { nullptr };
|
||||
const char* m_name { nullptr };
|
||||
WaitQueue m_queue;
|
||||
};
|
||||
|
||||
class Locker {
|
||||
|
@ -46,7 +48,6 @@ private:
|
|||
Lock& m_lock;
|
||||
};
|
||||
|
||||
|
||||
#define LOCKER(lock) Locker locker(lock)
|
||||
|
||||
template<typename T>
|
||||
|
|
|
@ -96,6 +96,7 @@ CXX_OBJS = \
|
|||
VM/RangeAllocator.o \
|
||||
VM/Region.o \
|
||||
VM/VMObject.o \
|
||||
WaitQueue.o \
|
||||
init.o \
|
||||
kprintf.o
|
||||
|
||||
|
|
|
@ -82,9 +82,22 @@ bool Thread::JoinBlocker::should_unblock(Thread& joiner, time_t, long)
|
|||
return !joiner.m_joinee;
|
||||
}
|
||||
|
||||
Thread::WaitQueueBlocker::WaitQueueBlocker(WaitQueue& queue)
|
||||
: m_queue(queue)
|
||||
{
|
||||
m_queue.enqueue(*current);
|
||||
}
|
||||
|
||||
bool Thread::WaitQueueBlocker::should_unblock(Thread&, time_t, long)
|
||||
{
|
||||
// Someone else will have to unblock us by calling wake_one() or wake_all() on the queue.
|
||||
return false;
|
||||
}
|
||||
|
||||
Thread::FileDescriptionBlocker::FileDescriptionBlocker(const FileDescription& description)
|
||||
: m_blocked_description(description)
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
const FileDescription& Thread::FileDescriptionBlocker::blocked_description() const
|
||||
{
|
||||
|
@ -236,7 +249,8 @@ bool Thread::WaitBlocker::should_unblock(Thread& thread, time_t, long)
|
|||
|
||||
Thread::SemiPermanentBlocker::SemiPermanentBlocker(Reason reason)
|
||||
: m_reason(reason)
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
bool Thread::SemiPermanentBlocker::should_unblock(Thread&, time_t, long)
|
||||
{
|
||||
|
|
|
@ -180,6 +180,14 @@ void Thread::yield_without_holding_big_lock()
|
|||
process().big_lock().lock();
|
||||
}
|
||||
|
||||
void Thread::donate_and_yield_without_holding_big_lock(Thread* beneficiary, const char* reason)
|
||||
{
|
||||
bool did_unlock = process().big_lock().unlock_if_locked();
|
||||
Scheduler::donate_to(beneficiary, reason);
|
||||
if (did_unlock)
|
||||
process().big_lock().lock();
|
||||
}
|
||||
|
||||
u64 Thread::sleep(u32 ticks)
|
||||
{
|
||||
ASSERT(state() == Thread::Running);
|
||||
|
|
|
@ -18,6 +18,7 @@ class FileDescription;
|
|||
class Process;
|
||||
class ProcessInspectionHandle;
|
||||
class Region;
|
||||
class WaitQueue;
|
||||
|
||||
enum class ShouldUnblockThread {
|
||||
No = 0,
|
||||
|
@ -110,6 +111,16 @@ public:
|
|||
void*& m_joinee_exit_value;
|
||||
};
|
||||
|
||||
class WaitQueueBlocker final : public Blocker {
|
||||
public:
|
||||
explicit WaitQueueBlocker(WaitQueue&);
|
||||
virtual bool should_unblock(Thread&, time_t now_s, long us) override;
|
||||
virtual const char* state_string() const override { return "WaitQueued"; }
|
||||
|
||||
private:
|
||||
WaitQueue& m_queue;
|
||||
};
|
||||
|
||||
class FileDescriptionBlocker : public Blocker {
|
||||
public:
|
||||
const FileDescription& blocked_description() const;
|
||||
|
@ -257,7 +268,7 @@ public:
|
|||
};
|
||||
|
||||
template<typename T, class... Args>
|
||||
[[nodiscard]] BlockResult block(Args&&... args)
|
||||
[[nodiscard]] BlockResult block_impl(Thread* beneficiary, const char* reason, Args&&... args)
|
||||
{
|
||||
// We should never be blocking a blocked (or otherwise non-active) thread.
|
||||
ASSERT(state() == Thread::Running);
|
||||
|
@ -270,7 +281,11 @@ public:
|
|||
set_state(Thread::Blocked);
|
||||
|
||||
// Yield to the scheduler, and wait for us to resume unblocked.
|
||||
yield_without_holding_big_lock();
|
||||
if (beneficiary) {
|
||||
donate_and_yield_without_holding_big_lock(beneficiary, reason);
|
||||
} else {
|
||||
yield_without_holding_big_lock();
|
||||
}
|
||||
|
||||
// We should no longer be blocked once we woke up
|
||||
ASSERT(state() != Thread::Blocked);
|
||||
|
@ -282,7 +297,19 @@ public:
|
|||
return BlockResult::InterruptedBySignal;
|
||||
|
||||
return BlockResult::WokeNormally;
|
||||
};
|
||||
}
|
||||
|
||||
template<typename T, class... Args>
|
||||
[[nodiscard]] BlockResult block(Args&&... args)
|
||||
{
|
||||
return block_impl<T>(nullptr, nullptr, forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename T, class... Args>
|
||||
[[nodiscard]] BlockResult donate_remaining_timeslice_and_block(Thread* beneficiary, const char* reason, Args&&... args)
|
||||
{
|
||||
return block_impl<T>(beneficiary, reason, forward<Args>(args)...);
|
||||
}
|
||||
|
||||
[[nodiscard]] BlockResult block_until(const char* state_string, Function<bool()>&& condition)
|
||||
{
|
||||
|
@ -398,6 +425,7 @@ private:
|
|||
bool m_should_die { false };
|
||||
|
||||
void yield_without_holding_big_lock();
|
||||
void donate_and_yield_without_holding_big_lock(Thread* beneficiary, const char* reason);
|
||||
};
|
||||
|
||||
HashTable<Thread*>& thread_table();
|
||||
|
|
34
Kernel/WaitQueue.cpp
Normal file
34
Kernel/WaitQueue.cpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
#include <Kernel/Thread.h>
|
||||
#include <Kernel/WaitQueue.h>
|
||||
|
||||
WaitQueue::WaitQueue()
|
||||
{
|
||||
}
|
||||
|
||||
WaitQueue::~WaitQueue()
|
||||
{
|
||||
}
|
||||
|
||||
void WaitQueue::enqueue(Thread& thread)
|
||||
{
|
||||
InterruptDisabler disabler;
|
||||
m_threads.append(&thread);
|
||||
}
|
||||
|
||||
void WaitQueue::wake_one()
|
||||
{
|
||||
InterruptDisabler disabler;
|
||||
if (m_threads.is_empty())
|
||||
return;
|
||||
if (auto* thread = m_threads.take_first())
|
||||
thread->unblock();
|
||||
}
|
||||
|
||||
void WaitQueue::wake_all()
|
||||
{
|
||||
InterruptDisabler disabler;
|
||||
if (m_threads.is_empty())
|
||||
return;
|
||||
while (!m_threads.is_empty())
|
||||
m_threads.take_first()->unblock();
|
||||
}
|
18
Kernel/WaitQueue.h
Normal file
18
Kernel/WaitQueue.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/SinglyLinkedList.h>
|
||||
|
||||
class Thread;
|
||||
|
||||
class WaitQueue {
|
||||
public:
|
||||
WaitQueue();
|
||||
~WaitQueue();
|
||||
|
||||
void enqueue(Thread&);
|
||||
void wake_one();
|
||||
void wake_all();
|
||||
|
||||
private:
|
||||
SinglyLinkedList<Thread*> m_threads;
|
||||
};
|
Loading…
Reference in a new issue