mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 18:28:12 +00:00
Kernel: Use a dedicated thread state for wait-queued threads
Instead of using the generic block mechanism, wait-queued threads now go into the special Queued state. This fixes an issue where signal dispatch would unblock a wait-queued thread (because signal dispatch unblocks blocked threads) and cause confusion since the thread only expected to be awoken by the queue.
This commit is contained in:
parent
7126a42d4d
commit
5859e16e53
5 changed files with 42 additions and 59 deletions
|
@ -11,6 +11,7 @@
|
|||
#include <Kernel/Scheduler.h>
|
||||
#include <Kernel/UnixTypes.h>
|
||||
#include <Kernel/VM/Region.h>
|
||||
#include <Kernel/WaitQueue.h>
|
||||
#include <LibC/fd_set.h>
|
||||
|
||||
class Alarm;
|
||||
|
@ -84,6 +85,7 @@ public:
|
|||
Dead,
|
||||
Stopped,
|
||||
Blocked,
|
||||
Queued,
|
||||
};
|
||||
|
||||
class Blocker {
|
||||
|
@ -111,16 +113,6 @@ 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;
|
||||
|
@ -268,30 +260,18 @@ public:
|
|||
};
|
||||
|
||||
template<typename T, class... Args>
|
||||
[[nodiscard]] BlockResult block_impl(Thread* beneficiary, const char* reason, Args&&... args)
|
||||
[[nodiscard]] BlockResult block(Args&&... args)
|
||||
{
|
||||
// We should never be blocking a blocked (or otherwise non-active) thread.
|
||||
ASSERT(state() == Thread::Running);
|
||||
ASSERT(m_blocker == nullptr);
|
||||
|
||||
// NOTE: We disable interrupts here to avoid the situation where a WaitQueueBlocker
|
||||
// adds the current thread to a WaitQueue, and then someone wakes up before
|
||||
// we set the state to Blocked decides to wake the queue. They would find
|
||||
// unblocked threads in a wait queue, which would not be good. We can't go
|
||||
// into Blocked state earlier, since that would prevent this thread from
|
||||
// getting scheduled.
|
||||
auto saved_if = cli_and_save_interrupt_flag();
|
||||
T t(forward<Args>(args)...);
|
||||
m_blocker = &t;
|
||||
set_state(Thread::Blocked);
|
||||
restore_interrupt_flag(saved_if);
|
||||
|
||||
// Yield to the scheduler, and wait for us to resume unblocked.
|
||||
if (beneficiary) {
|
||||
donate_and_yield_without_holding_big_lock(beneficiary, reason);
|
||||
} else {
|
||||
yield_without_holding_big_lock();
|
||||
}
|
||||
yield_without_holding_big_lock();
|
||||
|
||||
// We should no longer be blocked once we woke up
|
||||
ASSERT(state() != Thread::Blocked);
|
||||
|
@ -305,26 +285,31 @@ public:
|
|||
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)
|
||||
{
|
||||
return block<ConditionBlocker>(state_string, move(condition));
|
||||
}
|
||||
|
||||
void wait_on(WaitQueue& queue)
|
||||
void wait_on(WaitQueue& queue, Thread* beneficiary = nullptr, const char* reason = nullptr)
|
||||
{
|
||||
(void)block<WaitQueueBlocker>(queue);
|
||||
bool did_unlock = unlock_process_if_locked();
|
||||
cli();
|
||||
set_state(State::Queued);
|
||||
queue.enqueue(*current);
|
||||
// Yield and wait for the queue to wake us up again.
|
||||
if (beneficiary)
|
||||
Scheduler::donate_to(beneficiary, reason);
|
||||
else
|
||||
Scheduler::yield();
|
||||
// We've unblocked, relock the process if needed and carry on.
|
||||
if (did_unlock)
|
||||
relock_process();
|
||||
}
|
||||
|
||||
void wake_from_queue()
|
||||
{
|
||||
ASSERT(state() == State::Queued);
|
||||
set_state(State::Runnable);
|
||||
}
|
||||
|
||||
void unblock();
|
||||
|
@ -400,6 +385,9 @@ private:
|
|||
|
||||
private:
|
||||
friend class SchedulerData;
|
||||
bool unlock_process_if_locked();
|
||||
void relock_process();
|
||||
|
||||
String backtrace_impl() const;
|
||||
Process& m_process;
|
||||
int m_tid { -1 };
|
||||
|
@ -436,7 +424,6 @@ 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();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue