Index: lldb/include/lldb/Core/IOHandler.h =================================================================== --- lldb/include/lldb/Core/IOHandler.h +++ lldb/include/lldb/Core/IOHandler.h @@ -165,11 +165,14 @@ virtual void PrintAsync(const char *s, size_t len, bool is_stdout); + std::mutex &GetOutputMutex() { return m_output_mutex; } + protected: Debugger &m_debugger; lldb::FileSP m_input_sp; lldb::StreamFileSP m_output_sp; lldb::StreamFileSP m_error_sp; + std::mutex m_output_mutex; repro::DataRecorder *m_data_recorder; Predicate m_popped; Flags m_flags; Index: lldb/include/lldb/Host/Editline.h =================================================================== --- lldb/include/lldb/Host/Editline.h +++ lldb/include/lldb/Host/Editline.h @@ -154,7 +154,7 @@ class Editline { public: Editline(const char *editor_name, FILE *input_file, FILE *output_file, - FILE *error_file, bool color_prompts); + FILE *error_file, std::mutex &output_mutex, bool color_prompts); ~Editline(); @@ -402,7 +402,7 @@ std::string m_suggestion_ansi_suffix; std::size_t m_previous_autosuggestion_size = 0; - std::mutex m_output_mutex; + std::mutex &m_output_mutex; }; } Index: lldb/include/lldb/Interpreter/CommandInterpreter.h =================================================================== --- lldb/include/lldb/Interpreter/CommandInterpreter.h +++ lldb/include/lldb/Interpreter/CommandInterpreter.h @@ -655,7 +655,8 @@ const CommandObject::CommandMap &command_map); // An interruptible wrapper around the stream output - void PrintCommandOutput(Stream &stream, llvm::StringRef str); + void PrintCommandOutput(IOHandler &io_handler, llvm::StringRef str, + bool is_stdout0); bool EchoCommandNonInteractive(llvm::StringRef line, const Flags &io_handler_flags) const; Index: lldb/source/Core/IOHandler.cpp =================================================================== --- lldb/source/Core/IOHandler.cpp +++ lldb/source/Core/IOHandler.cpp @@ -123,6 +123,7 @@ void IOHandler::WaitForPop() { m_popped.WaitForValueEqualTo(true); } void IOHandler::PrintAsync(const char *s, size_t len, bool is_stdout) { + std::lock_guard guard(m_output_mutex); lldb::StreamFileSP stream = is_stdout ? m_output_sp : m_error_sp; stream->Write(s, len); stream->Flush(); @@ -266,9 +267,9 @@ m_input_sp && m_input_sp->GetIsRealTerminal(); if (use_editline) { - m_editline_up = std::make_unique(editline_name, GetInputFILE(), - GetOutputFILE(), GetErrorFILE(), - m_color_prompts); + m_editline_up = std::make_unique( + editline_name, GetInputFILE(), GetOutputFILE(), GetErrorFILE(), + GetOutputMutex(), m_color_prompts); m_editline_up->SetIsInputCompleteCallback( [this](Editline *editline, StringList &lines) { return this->IsInputCompleteCallback(editline, lines); @@ -619,6 +620,7 @@ void IOHandlerEditline::PrintAsync(const char *s, size_t len, bool is_stdout) { #if LLDB_ENABLE_LIBEDIT if (m_editline_up) { + std::lock_guard guard(m_output_mutex); lldb::StreamFileSP stream = is_stdout ? m_output_sp : m_error_sp; m_editline_up->PrintAsync(stream.get(), s, len); } else Index: lldb/source/Host/common/Editline.cpp =================================================================== --- lldb/source/Host/common/Editline.cpp +++ lldb/source/Host/common/Editline.cpp @@ -1376,10 +1376,12 @@ } Editline::Editline(const char *editline_name, FILE *input_file, - FILE *output_file, FILE *error_file, bool color_prompts) + FILE *output_file, FILE *error_file, + std::mutex &output_mutex, bool color_prompts) : m_editor_status(EditorStatus::Complete), m_color_prompts(color_prompts), m_input_file(input_file), m_output_file(output_file), - m_error_file(error_file), m_input_connection(fileno(input_file), false) { + m_error_file(error_file), m_input_connection(fileno(input_file), false), + m_output_mutex(output_mutex) { // Get a shared history instance m_editor_name = (editline_name == nullptr) ? "lldb-tmp" : editline_name; m_history_sp = EditlineHistory::GetHistory(m_editor_name); Index: lldb/source/Interpreter/CommandInterpreter.cpp =================================================================== --- lldb/source/Interpreter/CommandInterpreter.cpp +++ lldb/source/Interpreter/CommandInterpreter.cpp @@ -2975,8 +2975,12 @@ return was_interrupted; } -void CommandInterpreter::PrintCommandOutput(Stream &stream, - llvm::StringRef str) { +void CommandInterpreter::PrintCommandOutput(IOHandler &io_handler, + llvm::StringRef str, + bool is_stdout) { + + lldb::StreamFileSP stream = is_stdout ? io_handler.GetOutputStreamFileSP() + : io_handler.GetErrorStreamFileSP(); // Split the output into lines and poll for interrupt requests const char *data = str.data(); size_t size = str.size(); @@ -2989,15 +2993,19 @@ break; } } - chunk_size = stream.Write(data, chunk_size); + { + std::lock_guard guard(io_handler.GetOutputMutex()); + chunk_size = stream->Write(data, chunk_size); + } lldbassert(size >= chunk_size); data += chunk_size; size -= chunk_size; } - if (size > 0) { - stream.Printf("\n... Interrupted.\n"); - } - stream.Flush(); + + std::lock_guard guard(io_handler.GetOutputMutex()); + if (size > 0) + stream->Printf("\n... Interrupted.\n"); + stream->Flush(); } bool CommandInterpreter::EchoCommandNonInteractive( @@ -3033,9 +3041,11 @@ // When using a non-interactive file handle (like when sourcing commands // from a file) we need to echo the command out so we don't just see the // command output and no command... - if (EchoCommandNonInteractive(line, io_handler.GetFlags())) + if (EchoCommandNonInteractive(line, io_handler.GetFlags())) { + std::lock_guard guard(io_handler.GetOutputMutex()); io_handler.GetOutputStreamFileSP()->Printf( "%s%s\n", io_handler.GetPrompt(), line.c_str()); + } } StartHandlingCommand(); @@ -3057,13 +3067,13 @@ if (!result.GetImmediateOutputStream()) { llvm::StringRef output = result.GetOutputData(); - PrintCommandOutput(*io_handler.GetOutputStreamFileSP(), output); + PrintCommandOutput(io_handler, output, true); } // Now emit the command error text from the command we just executed if (!result.GetImmediateErrorStream()) { llvm::StringRef error = result.GetErrorData(); - PrintCommandOutput(*io_handler.GetErrorStreamFileSP(), error); + PrintCommandOutput(io_handler, error, false); } } Index: lldb/unittests/Editline/EditlineTest.cpp =================================================================== --- lldb/unittests/Editline/EditlineTest.cpp +++ lldb/unittests/Editline/EditlineTest.cpp @@ -84,6 +84,7 @@ bool IsInputComplete(lldb_private::Editline *editline, lldb_private::StringList &lines); + std::mutex output_mutex; std::unique_ptr _editline_sp; PseudoTerminal _pty; @@ -118,7 +119,7 @@ // Create an Editline instance. _editline_sp.reset(new lldb_private::Editline( "gtest editor", *_el_secondary_file, *_el_secondary_file, - *_el_secondary_file, false)); + *_el_secondary_file, output_mutex, false)); _editline_sp->SetPrompt("> "); // Hookup our input complete callback.