Skip to content

Commit 8e776bb

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
1 parent 57f03db commit 8e776bb

File tree

7 files changed

+50
-28
lines changed

7 files changed

+50
-28
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
@@ -655,7 +655,8 @@ class CommandInterpreter : public Broadcaster,
655655
const CommandObject::CommandMap &command_map);
656656

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

660661
bool EchoCommandNonInteractive(llvm::StringRef line,
661662
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);
@@ -619,6 +620,7 @@ void IOHandlerEditline::GotEOF() {
619620
void IOHandlerEditline::PrintAsync(const char *s, size_t len, bool is_stdout) {
620621
#if LLDB_ENABLE_LIBEDIT
621622
if (m_editline_up) {
623+
std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
622624
lldb::StreamFileSP stream = is_stdout ? m_output_sp : m_error_sp;
623625
m_editline_up->PrintAsync(stream.get(), s, len);
624626
} 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 & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2975,8 +2975,12 @@ bool CommandInterpreter::WasInterrupted() const {
29752975
return was_interrupted;
29762976
}
29772977

2978-
void CommandInterpreter::PrintCommandOutput(Stream &stream,
2979-
llvm::StringRef str) {
2978+
void CommandInterpreter::PrintCommandOutput(IOHandler &io_handler,
2979+
llvm::StringRef str,
2980+
bool is_stdout) {
2981+
2982+
lldb::StreamFileSP stream = is_stdout ? io_handler.GetOutputStreamFileSP()
2983+
: io_handler.GetErrorStreamFileSP();
29802984
// Split the output into lines and poll for interrupt requests
29812985
const char *data = str.data();
29822986
size_t size = str.size();
@@ -2989,15 +2993,19 @@ void CommandInterpreter::PrintCommandOutput(Stream &stream,
29892993
break;
29902994
}
29912995
}
2992-
chunk_size = stream.Write(data, chunk_size);
2996+
{
2997+
std::lock_guard<std::recursive_mutex> guard(io_handler.GetOutputMutex());
2998+
chunk_size = stream->Write(data, chunk_size);
2999+
}
29933000
lldbassert(size >= chunk_size);
29943001
data += chunk_size;
29953002
size -= chunk_size;
29963003
}
2997-
if (size > 0) {
2998-
stream.Printf("\n... Interrupted.\n");
2999-
}
3000-
stream.Flush();
3004+
3005+
std::lock_guard<std::recursive_mutex> guard(io_handler.GetOutputMutex());
3006+
if (size > 0)
3007+
stream->Printf("\n... Interrupted.\n");
3008+
stream->Flush();
30013009
}
30023010

30033011
bool CommandInterpreter::EchoCommandNonInteractive(
@@ -3033,9 +3041,11 @@ void CommandInterpreter::IOHandlerInputComplete(IOHandler &io_handler,
30333041
// When using a non-interactive file handle (like when sourcing commands
30343042
// from a file) we need to echo the command out so we don't just see the
30353043
// command output and no command...
3036-
if (EchoCommandNonInteractive(line, io_handler.GetFlags()))
3044+
if (EchoCommandNonInteractive(line, io_handler.GetFlags())) {
3045+
std::lock_guard<std::recursive_mutex> guard(io_handler.GetOutputMutex());
30373046
io_handler.GetOutputStreamFileSP()->Printf(
30383047
"%s%s\n", io_handler.GetPrompt(), line.c_str());
3048+
}
30393049
}
30403050

30413051
StartHandlingCommand();
@@ -3057,13 +3067,13 @@ void CommandInterpreter::IOHandlerInputComplete(IOHandler &io_handler,
30573067

30583068
if (!result.GetImmediateOutputStream()) {
30593069
llvm::StringRef output = result.GetOutputData();
3060-
PrintCommandOutput(*io_handler.GetOutputStreamFileSP(), output);
3070+
PrintCommandOutput(io_handler, output, true);
30613071
}
30623072

30633073
// Now emit the command error text from the command we just executed
30643074
if (!result.GetImmediateErrorStream()) {
30653075
llvm::StringRef error = result.GetErrorData();
3066-
PrintCommandOutput(*io_handler.GetErrorStreamFileSP(), error);
3076+
PrintCommandOutput(io_handler, error, false);
30673077
}
30683078
}
30693079

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;
@@ -117,7 +118,7 @@ EditlineAdapter::EditlineAdapter()
117118
// Create an Editline instance.
118119
_editline_sp.reset(new lldb_private::Editline(
119120
"gtest editor", *_el_secondary_file, *_el_secondary_file,
120-
*_el_secondary_file, false));
121+
*_el_secondary_file, output_mutex, false));
121122
_editline_sp->SetPrompt("> ");
122123

123124
// Hookup our input complete callback.

0 commit comments

Comments
 (0)