diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index 780f769e00..491f79486c 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -581,7 +581,7 @@ void Process::finalize() // allow us to take references anymore. ProcFSComponentRegistry::the().unregister_process(*this); - m_dead = true; + m_state.store(State::Dead, AK::MemoryOrder::memory_order_release); { // FIXME: PID/TID BUG @@ -626,6 +626,15 @@ void Process::unblock_waiters(Thread::WaitBlocker::UnblockFlags flags, u8 signal void Process::die() { + auto expected = State::Running; + if (!m_state.compare_exchange_strong(expected, State::Dying, AK::memory_order_acquire)) { + // It's possible that another thread calls this at almost the same time + // as we can't always instantly kill other threads (they may be blocked) + // So if we already were called then other threads should stop running + // momentarily and we only really need to service the first thread + return; + } + // Let go of the TTY, otherwise a slave PTY may keep the master PTY from // getting an EOF when the last process using the slave PTY dies. // If the master PTY owner relies on an EOF to know when to wait() on a diff --git a/Kernel/Process.h b/Kernel/Process.h index 5ce44dcd20..f14e0078a5 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -148,6 +148,12 @@ class Process Process& m_process; }; + enum class State : u8 { + Running = 0, + Dying, + Dead + }; + public: inline static Process* current() { @@ -201,7 +207,8 @@ public: bool should_core_dump() const { return m_should_dump_core; } void set_dump_core(bool dump_core) { m_should_dump_core = dump_core; } - bool is_dead() const { return m_dead; } + bool is_dying() const { return m_state.load(AK::MemoryOrder::memory_order_acquire) != State::Running; } + bool is_dead() const { return m_state.load(AK::MemoryOrder::memory_order_acquire) == State::Dead; } bool is_stopped() const { return m_is_stopped; } bool set_stopped(bool stopped) { return m_is_stopped.exchange(stopped); } @@ -670,7 +677,7 @@ private: mutable RecursiveSpinLock m_thread_list_lock; const bool m_is_kernel_process; - bool m_dead { false }; + Atomic m_state { State::Running }; bool m_profiling { false }; Atomic m_is_stopped { false }; bool m_should_dump_core { false }; diff --git a/Kernel/Syscall.cpp b/Kernel/Syscall.cpp index 04950fb51f..1879e8f9ff 100644 --- a/Kernel/Syscall.cpp +++ b/Kernel/Syscall.cpp @@ -155,6 +155,12 @@ NEVER_INLINE void syscall_handler(TrapFrame* trap) auto current_thread = Thread::current(); VERIFY(current_thread->previous_mode() == Thread::PreviousMode::UserMode); auto& process = current_thread->process(); + if (process.is_dying()) { + // It's possible this thread is just about to make a syscall while another is + // is killing our process. + current_thread->die_if_needed(); + return; + } if (auto tracer = process.tracer(); tracer && tracer->is_tracing_syscalls()) { tracer->set_trace_syscalls(false);