summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Kernel/Arch/i386/CPU.h15
-rw-r--r--Kernel/Thread.h12
2 files changed, 24 insertions, 3 deletions
diff --git a/Kernel/Arch/i386/CPU.h b/Kernel/Arch/i386/CPU.h
index 5fa9057bb1..27d7d132f4 100644
--- a/Kernel/Arch/i386/CPU.h
+++ b/Kernel/Arch/i386/CPU.h
@@ -289,6 +289,21 @@ private:
u32 m_flags;
};
+inline bool cli_and_save_interrupt_flag()
+{
+ u32 flags = cpu_flags();
+ cli();
+ return flags & 0x200;
+}
+
+inline void restore_interrupt_flag(bool flag)
+{
+ if (flag)
+ sti();
+ else
+ cli();
+}
+
class InterruptDisabler {
public:
InterruptDisabler()
diff --git a/Kernel/Thread.h b/Kernel/Thread.h
index 6fdcf30b79..0eb939bb89 100644
--- a/Kernel/Thread.h
+++ b/Kernel/Thread.h
@@ -274,11 +274,17 @@ public:
ASSERT(state() == Thread::Running);
ASSERT(m_blocker == nullptr);
- T t(AK::forward<Args>(args)...);
+ // 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;
-
- // Enter blocked state.
set_state(Thread::Blocked);
+ restore_interrupt_flag(saved_if);
// Yield to the scheduler, and wait for us to resume unblocked.
if (beneficiary) {