[Lldb-commits] [lldb] [lldb][Windows] Fix lldb-server exit deadlock with pipe-mode stdio (PR #227273)
Charles Zablit via lldb-commits
lldb-commits at lists.llvm.org
Tue Sep 29 04:10:27 PDT 2026
https://github.com/charles-zablit created https://github.com/llvm/llvm-project/pull/227273
None
>From 496c4dcc75a8adb08e1ff30933e24ec854a4ab73 Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Mon, 28 Sep 2026 21:17:18 +0100
Subject: [PATCH] [lldb][Windows] Fix lldb-server exit deadlock with pipe-mode
stdio
---
lldb/include/lldb/Host/windows/PseudoConsole.h | 8 ++++++++
lldb/source/Host/windows/PseudoConsole.cpp | 7 +++++--
.../Process/Windows/Common/NativeProcessWindows.cpp | 6 ++++++
3 files changed, 19 insertions(+), 2 deletions(-)
diff --git a/lldb/include/lldb/Host/windows/PseudoConsole.h b/lldb/include/lldb/Host/windows/PseudoConsole.h
index b98402922bfb3..3580b3d2c78f5 100644
--- a/lldb/include/lldb/Host/windows/PseudoConsole.h
+++ b/lldb/include/lldb/Host/windows/PseudoConsole.h
@@ -61,6 +61,10 @@ class PseudoConsole {
/// Closes the ConPTY and invalidates its handle, without closing the STDIN
/// and STDOUT pipes. Closing the ConPTY signals EOF to any process currently
/// attached to it.
+ ///
+ /// In pipe mode there is no ConPTY to close: this cancels the read pending
+ /// on the STDOUT pipe and marks the pipes closed, so IsConnected() returns
+ /// false from then on, as it does once a ConPTY is closed.
void Close();
/// Closes the STDIN and STDOUT pipe handles and invalidates them.
@@ -142,6 +146,10 @@ class PseudoConsole {
reinterpret_cast<HANDLE>(static_cast<intptr_t>(-1));
HANDLE m_pipe_child_stdout =
reinterpret_cast<HANDLE>(static_cast<intptr_t>(-1));
+ // Pipe mode: set by Close(). Its CancelIoEx only reaches a read that is
+ // already pending, so a reader has to be able to tell that the pipes were
+ // closed before it starts the next one.
+ std::atomic<bool> m_pipes_closed = false;
Mode m_mode = Mode::None;
std::mutex m_mutex{};
std::condition_variable m_cv{};
diff --git a/lldb/source/Host/windows/PseudoConsole.cpp b/lldb/source/Host/windows/PseudoConsole.cpp
index 3a28adacfea53..0ef3e563a3de6 100644
--- a/lldb/source/Host/windows/PseudoConsole.cpp
+++ b/lldb/source/Host/windows/PseudoConsole.cpp
@@ -176,7 +176,7 @@ llvm::Error PseudoConsole::OpenPseudoConsole(uint16_t req_cols,
bool PseudoConsole::IsConnected() const {
if (m_mode == Mode::Pipe)
- return m_conpty_input != INVALID_HANDLE_VALUE &&
+ return !m_pipes_closed && m_conpty_input != INVALID_HANDLE_VALUE &&
m_conpty_output != INVALID_HANDLE_VALUE;
return m_conpty_handle != INVALID_HANDLE_VALUE &&
m_conpty_input != INVALID_HANDLE_VALUE &&
@@ -188,8 +188,10 @@ void PseudoConsole::Close() {
std::unique_lock<std::mutex> guard(m_mutex);
if (m_conpty_handle != INVALID_HANDLE_VALUE)
kernel32.ClosePseudoConsole(m_conpty_handle);
- if (m_mode == Mode::Pipe && m_conpty_output != INVALID_HANDLE_VALUE)
+ if (m_mode == Mode::Pipe && m_conpty_output != INVALID_HANDLE_VALUE) {
+ m_pipes_closed = true;
CancelIoEx(m_conpty_output, nullptr);
+ }
m_conpty_handle = INVALID_HANDLE_VALUE;
SetStopping(false);
m_cv.notify_all();
@@ -219,6 +221,7 @@ void PseudoConsole::Reset() {
Close();
ClosePseudoConsolePipes();
CloseAnonymousPipes();
+ m_pipes_closed = false;
m_mode = Mode::None;
}
diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
index 849931eaec372..b3470e4817dea 100644
--- a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
@@ -954,6 +954,12 @@ void NativeProcessWindows::StopStdioForwarding() {
if (m_pty)
m_pty->Close();
+ // Close() cancels the read pending on the pipe, but one that the read thread
+ // is about to start would only return at its 5s timeout: EOF cannot come
+ // while the inferior is held at its exit debug event. Wake the thread so that
+ // it sees the closed PTY right away.
+ m_stdio_communication.InterruptRead();
+
if (m_stdio_communication.ReadThreadIsRunning())
m_stdio_communication.JoinReadThread();
More information about the lldb-commits
mailing list