[Lldb-commits] [lldb] [lldb] Release output lock across blocking el_wgetc in DisplayCompletions (PR #196686)
Jonas Devlieghere via lldb-commits
lldb-commits at lists.llvm.org
Fri May 8 21:29:55 PDT 2026
https://github.com/JDevlieghere created https://github.com/llvm/llvm-project/pull/196686
DisplayCompletions held m_output_stream_sp->Lock() across the blocking el_wgetc() call used by the "More (Y/n/a)" pager. Because the lock is a recursive_mutex, this worked when Editline::Interrupt() ran on the same thread (the synchronous SIGINT handler), but deadlocks when Interrupt() runs on another thread: it blocks on the lock and can never call InterruptRead() to wake the editor thread.
Mirror the pattern already used by Editline::GetCharacter: drop the lock across the blocking read and reacquire it afterward. The status check and the "^C\n" / "\n" prints stay under the lock.
>From df71b399d04c6c1ac863ff801d055837e07afe4a Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Fri, 8 May 2026 21:28:51 -0700
Subject: [PATCH] [lldb] Release output lock across blocking el_wgetc in
DisplayCompletions
DisplayCompletions held m_output_stream_sp->Lock() across the blocking
el_wgetc() call used by the "More (Y/n/a)" pager. Because the lock is a
recursive_mutex, this worked when Editline::Interrupt() ran on the same
thread (the synchronous SIGINT handler), but deadlocks when Interrupt()
runs on another thread: it blocks on the lock and can never call
InterruptRead() to wake the editor thread.
Mirror the pattern already used by Editline::GetCharacter: drop the lock
across the blocking read and reacquire it afterward. The status check
and the "^C\n" / "\n" prints stay under the lock.
---
lldb/source/Host/common/Editline.cpp | 22 ++++++++++++++++------
1 file changed, 16 insertions(+), 6 deletions(-)
diff --git a/lldb/source/Host/common/Editline.cpp b/lldb/source/Host/common/Editline.cpp
index 39b0a649a7f60..833516b0b2c2d 100644
--- a/lldb/source/Host/common/Editline.cpp
+++ b/lldb/source/Host/common/Editline.cpp
@@ -1054,9 +1054,10 @@ void Editline::DisplayCompletions(
Editline &editline, llvm::ArrayRef<CompletionResult::Completion> results) {
assert(!results.empty());
- LockedStreamFile locked_stream = editline.m_output_stream_sp->Lock();
+ std::optional<LockedStreamFile> locked_stream =
+ editline.m_output_stream_sp->Lock();
- fprintf(locked_stream.GetFile().GetStream(),
+ fprintf(locked_stream->GetFile().GetStream(),
"\n" ANSI_CLEAR_BELOW "Available completions:\n");
/// Account for the current line, the line showing "Available completions"
@@ -1075,14 +1076,20 @@ void Editline::DisplayCompletions(
size_t cur_pos = 0;
while (cur_pos < results.size()) {
cur_pos += PrintCompletion(
- locked_stream.GetFile().GetStream(), results.slice(cur_pos), max_len,
+ locked_stream->GetFile().GetStream(), results.slice(cur_pos), max_len,
editline.GetTerminalWidth(),
all ? std::nullopt : std::optional<size_t>(page_size));
if (cur_pos >= results.size())
break;
- fprintf(locked_stream.GetFile().GetStream(), "More (Y/n/a): ");
+ fprintf(locked_stream->GetFile().GetStream(), "More (Y/n/a): ");
+
+ // Release the output lock across the blocking el_wgetc() so that
+ // Interrupt(), which may run on another thread, can acquire it to wake
+ // up the read.
+ locked_stream.reset();
+
// The type for the output and the type for the parameter are different,
// to allow interoperability with older versions of libedit. The container
// for the reply must be as wide as what our implementation is using,
@@ -1091,14 +1098,17 @@ void Editline::DisplayCompletions(
EditLineGetCharType reply = L'n';
int got_char = el_wgetc(editline.m_editline,
reinterpret_cast<EditLineCharType *>(&reply));
+
+ locked_stream.emplace(editline.m_output_stream_sp->Lock());
+
// Check for a ^C or other interruption.
if (editline.m_editor_status == EditorStatus::Interrupted) {
editline.m_editor_status = EditorStatus::Editing;
- fprintf(locked_stream.GetFile().GetStream(), "^C\n");
+ fprintf(locked_stream->GetFile().GetStream(), "^C\n");
break;
}
- fprintf(locked_stream.GetFile().GetStream(), "\n");
+ fprintf(locked_stream->GetFile().GetStream(), "\n");
if (got_char == -1 || reply == 'n')
break;
if (reply == 'a')
More information about the lldb-commits
mailing list