Skip to content

Commit 5418f63

Browse files
committed
Re-land "[lldb] Synchronize output through the IOHandler"
Add synchronization to the IOHandler to prevent multiple threads from writing concurrently to the output or error stream. A scenario where this could happen is when a thread (the default event thread for example) is using the debugger's asynchronous stream. We would delegate this operation to the IOHandler which might be running on another thread. Until this patch there was nothing to synchronize the two at the IOHandler level. Differential revision: https://reviews.llvm.org/D121500 (cherry picked from commit 8e776bb)
1 parent ef75883 commit 5418f63

File tree

7 files changed

+50
-27
lines changed

7 files changed

+50
-27
lines changed

lldb/include/lldb/Core/IOHandler.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class Debugger;
3434
namespace repro {
3535
class DataRecorder;
3636
}
37-
}
37+
} // namespace lldb_private
3838

3939
namespace curses {
4040
class Application;
@@ -165,11 +165,14 @@ class IOHandler {
165165

166166
virtual void PrintAsync(const char *s, size_t len, bool is_stdout);
167167

168+
std::recursive_mutex &GetOutputMutex() { return m_output_mutex; }
169+
168170
protected:
169171
Debugger &m_debugger;
170172
lldb::FileSP m_input_sp;
171173
lldb::StreamFileSP m_output_sp;
172174
lldb::StreamFileSP m_error_sp;
175+
std::recursive_mutex m_output_mutex;
173176
repro::DataRecorder *m_data_recorder;
174177
Predicate<bool> m_popped;
175178
Flags m_flags;

lldb/include/lldb/Host/Editline.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,8 @@ using namespace line_editor;
154154
class Editline {
155155
public:
156156
Editline(const char *editor_name, FILE *input_file, FILE *output_file,
157-
FILE *error_file, bool color_prompts);
157+
FILE *error_file, std::recursive_mutex &output_mutex,
158+
bool color_prompts);
158159

159160
~Editline();
160161

@@ -402,7 +403,7 @@ class Editline {
402403
std::string m_suggestion_ansi_suffix;
403404

404405
std::size_t m_previous_autosuggestion_size = 0;
405-
std::mutex m_output_mutex;
406+
std::recursive_mutex &m_output_mutex;
406407
};
407408
}
408409

lldb/include/lldb/Interpreter/CommandInterpreter.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -654,7 +654,8 @@ class CommandInterpreter : public Broadcaster,
654654
CommandObject::CommandMap &command_map);
655655

656656
// An interruptible wrapper around the stream output
657-
void PrintCommandOutput(Stream &stream, llvm::StringRef str);
657+
void PrintCommandOutput(IOHandler &io_handler, llvm::StringRef str,
658+
bool is_stdout);
658659

659660
bool EchoCommandNonInteractive(llvm::StringRef line,
660661
const Flags &io_handler_flags) const;

lldb/source/Core/IOHandler.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ void IOHandler::SetPopped(bool b) { m_popped.SetValue(b, eBroadcastOnChange); }
123123
void IOHandler::WaitForPop() { m_popped.WaitForValueEqualTo(true); }
124124

125125
void IOHandler::PrintAsync(const char *s, size_t len, bool is_stdout) {
126+
std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
126127
lldb::StreamFileSP stream = is_stdout ? m_output_sp : m_error_sp;
127128
stream->Write(s, len);
128129
stream->Flush();
@@ -266,9 +267,9 @@ IOHandlerEditline::IOHandlerEditline(
266267
m_input_sp && m_input_sp->GetIsRealTerminal();
267268

268269
if (use_editline) {
269-
m_editline_up = std::make_unique<Editline>(editline_name, GetInputFILE(),
270-
GetOutputFILE(), GetErrorFILE(),
271-
m_color_prompts);
270+
m_editline_up = std::make_unique<Editline>(
271+
editline_name, GetInputFILE(), GetOutputFILE(), GetErrorFILE(),
272+
GetOutputMutex(), m_color_prompts);
272273
m_editline_up->SetIsInputCompleteCallback(
273274
[this](Editline *editline, StringList &lines) {
274275
return this->IsInputCompleteCallback(editline, lines);
@@ -622,6 +623,7 @@ void IOHandlerEditline::GotEOF() {
622623
void IOHandlerEditline::PrintAsync(const char *s, size_t len, bool is_stdout) {
623624
#if LLDB_ENABLE_LIBEDIT
624625
if (m_editline_up) {
626+
std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
625627
lldb::StreamFileSP stream = is_stdout ? m_output_sp : m_error_sp;
626628
m_editline_up->PrintAsync(stream.get(), s, len);
627629
} else

lldb/source/Host/common/Editline.cpp

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1376,10 +1376,12 @@ Editline *Editline::InstanceFor(EditLine *editline) {
13761376
}
13771377

13781378
Editline::Editline(const char *editline_name, FILE *input_file,
1379-
FILE *output_file, FILE *error_file, bool color_prompts)
1379+
FILE *output_file, FILE *error_file,
1380+
std::recursive_mutex &output_mutex, bool color_prompts)
13801381
: m_editor_status(EditorStatus::Complete), m_color_prompts(color_prompts),
13811382
m_input_file(input_file), m_output_file(output_file),
1382-
m_error_file(error_file), m_input_connection(fileno(input_file), false) {
1383+
m_error_file(error_file), m_input_connection(fileno(input_file), false),
1384+
m_output_mutex(output_mutex) {
13831385
// Get a shared history instance
13841386
m_editor_name = (editline_name == nullptr) ? "lldb-tmp" : editline_name;
13851387
m_history_sp = EditlineHistory::GetHistory(m_editor_name);
@@ -1388,20 +1390,22 @@ Editline::Editline(const char *editline_name, FILE *input_file,
13881390
if (m_output_file) {
13891391
const int term_fd = fileno(m_output_file);
13901392
if (term_fd != -1) {
1391-
static std::mutex *g_init_terminal_fds_mutex_ptr = nullptr;
1393+
static std::recursive_mutex *g_init_terminal_fds_mutex_ptr = nullptr;
13921394
static std::set<int> *g_init_terminal_fds_ptr = nullptr;
13931395
static llvm::once_flag g_once_flag;
13941396
llvm::call_once(g_once_flag, [&]() {
13951397
g_init_terminal_fds_mutex_ptr =
1396-
new std::mutex(); // NOTE: Leak to avoid C++ destructor chain issues
1398+
new std::recursive_mutex(); // NOTE: Leak to avoid C++ destructor
1399+
// chain issues
13971400
g_init_terminal_fds_ptr = new std::set<int>(); // NOTE: Leak to avoid
13981401
// C++ destructor chain
13991402
// issues
14001403
});
14011404

14021405
// We must make sure to initialize the terminal a given file descriptor
14031406
// only once. If we do this multiple times, we start leaking memory.
1404-
std::lock_guard<std::mutex> guard(*g_init_terminal_fds_mutex_ptr);
1407+
std::lock_guard<std::recursive_mutex> guard(
1408+
*g_init_terminal_fds_mutex_ptr);
14051409
if (g_init_terminal_fds_ptr->find(term_fd) ==
14061410
g_init_terminal_fds_ptr->end()) {
14071411
g_init_terminal_fds_ptr->insert(term_fd);
@@ -1473,7 +1477,7 @@ uint32_t Editline::GetCurrentLine() { return m_current_line_index; }
14731477

14741478
bool Editline::Interrupt() {
14751479
bool result = true;
1476-
std::lock_guard<std::mutex> guard(m_output_mutex);
1480+
std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
14771481
if (m_editor_status == EditorStatus::Editing) {
14781482
fprintf(m_output_file, "^C\n");
14791483
result = m_input_connection.InterruptRead();
@@ -1484,7 +1488,7 @@ bool Editline::Interrupt() {
14841488

14851489
bool Editline::Cancel() {
14861490
bool result = true;
1487-
std::lock_guard<std::mutex> guard(m_output_mutex);
1491+
std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
14881492
if (m_editor_status == EditorStatus::Editing) {
14891493
MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
14901494
fprintf(m_output_file, ANSI_CLEAR_BELOW);
@@ -1499,7 +1503,7 @@ bool Editline::GetLine(std::string &line, bool &interrupted) {
14991503
m_input_lines = std::vector<EditLineStringType>();
15001504
m_input_lines.insert(m_input_lines.begin(), EditLineConstString(""));
15011505

1502-
std::lock_guard<std::mutex> guard(m_output_mutex);
1506+
std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
15031507

15041508
lldbassert(m_editor_status != EditorStatus::Editing);
15051509
if (m_editor_status == EditorStatus::Interrupted) {
@@ -1544,7 +1548,7 @@ bool Editline::GetLines(int first_line_number, StringList &lines,
15441548
m_input_lines = std::vector<EditLineStringType>();
15451549
m_input_lines.insert(m_input_lines.begin(), EditLineConstString(""));
15461550

1547-
std::lock_guard<std::mutex> guard(m_output_mutex);
1551+
std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
15481552
// Begin the line editing loop
15491553
DisplayInput();
15501554
SetCurrentLine(0);
@@ -1574,7 +1578,7 @@ bool Editline::GetLines(int first_line_number, StringList &lines,
15741578
}
15751579

15761580
void Editline::PrintAsync(Stream *stream, const char *s, size_t len) {
1577-
std::lock_guard<std::mutex> guard(m_output_mutex);
1581+
std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
15781582
if (m_editor_status == EditorStatus::Editing) {
15791583
MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
15801584
fprintf(m_output_file, ANSI_CLEAR_BELOW);

lldb/source/Interpreter/CommandInterpreter.cpp

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2969,8 +2969,12 @@ bool CommandInterpreter::WasInterrupted() const {
29692969
return was_interrupted;
29702970
}
29712971

2972-
void CommandInterpreter::PrintCommandOutput(Stream &stream,
2973-
llvm::StringRef str) {
2972+
void CommandInterpreter::PrintCommandOutput(IOHandler &io_handler,
2973+
llvm::StringRef str,
2974+
bool is_stdout) {
2975+
2976+
lldb::StreamFileSP stream = is_stdout ? io_handler.GetOutputStreamFileSP()
2977+
: io_handler.GetErrorStreamFileSP();
29742978
// Split the output into lines and poll for interrupt requests
29752979
const char *data = str.data();
29762980
size_t size = str.size();
@@ -2983,14 +2987,19 @@ void CommandInterpreter::PrintCommandOutput(Stream &stream,
29832987
break;
29842988
}
29852989
}
2986-
chunk_size = stream.Write(data, chunk_size);
2990+
{
2991+
std::lock_guard<std::recursive_mutex> guard(io_handler.GetOutputMutex());
2992+
chunk_size = stream->Write(data, chunk_size);
2993+
}
29872994
lldbassert(size >= chunk_size);
29882995
data += chunk_size;
29892996
size -= chunk_size;
29902997
}
2991-
if (size > 0) {
2992-
stream.Printf("\n... Interrupted.\n");
2993-
}
2998+
2999+
std::lock_guard<std::recursive_mutex> guard(io_handler.GetOutputMutex());
3000+
if (size > 0)
3001+
stream->Printf("\n... Interrupted.\n");
3002+
stream->Flush();
29943003
}
29953004

29963005
bool CommandInterpreter::EchoCommandNonInteractive(
@@ -3026,9 +3035,11 @@ void CommandInterpreter::IOHandlerInputComplete(IOHandler &io_handler,
30263035
// When using a non-interactive file handle (like when sourcing commands
30273036
// from a file) we need to echo the command out so we don't just see the
30283037
// command output and no command...
3029-
if (EchoCommandNonInteractive(line, io_handler.GetFlags()))
3038+
if (EchoCommandNonInteractive(line, io_handler.GetFlags())) {
3039+
std::lock_guard<std::recursive_mutex> guard(io_handler.GetOutputMutex());
30303040
io_handler.GetOutputStreamFileSP()->Printf(
30313041
"%s%s\n", io_handler.GetPrompt(), line.c_str());
3042+
}
30323043
}
30333044

30343045
StartHandlingCommand();
@@ -3050,13 +3061,13 @@ void CommandInterpreter::IOHandlerInputComplete(IOHandler &io_handler,
30503061

30513062
if (!result.GetImmediateOutputStream()) {
30523063
llvm::StringRef output = result.GetOutputData();
3053-
PrintCommandOutput(*io_handler.GetOutputStreamFileSP(), output);
3064+
PrintCommandOutput(io_handler, output, true);
30543065
}
30553066

30563067
// Now emit the command error text from the command we just executed
30573068
if (!result.GetImmediateErrorStream()) {
30583069
llvm::StringRef error = result.GetErrorData();
3059-
PrintCommandOutput(*io_handler.GetErrorStreamFileSP(), error);
3070+
PrintCommandOutput(io_handler, error, false);
30603071
}
30613072
}
30623073

lldb/unittests/Editline/EditlineTest.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ class EditlineAdapter {
8484
bool IsInputComplete(lldb_private::Editline *editline,
8585
lldb_private::StringList &lines);
8686

87+
std::recursive_mutex output_mutex;
8788
std::unique_ptr<lldb_private::Editline> _editline_sp;
8889

8990
PseudoTerminal _pty;
@@ -118,7 +119,7 @@ EditlineAdapter::EditlineAdapter()
118119
// Create an Editline instance.
119120
_editline_sp.reset(new lldb_private::Editline(
120121
"gtest editor", *_el_secondary_file, *_el_secondary_file,
121-
*_el_secondary_file, false));
122+
*_el_secondary_file, output_mutex, false));
122123
_editline_sp->SetPrompt("> ");
123124

124125
// Hookup our input complete callback.

0 commit comments

Comments
 (0)