namespace repro {
class DataRecorder;
}
-}
+} // namespace lldb_private
namespace curses {
class Application;
virtual void PrintAsync(const char *s, size_t len, bool is_stdout);
+ std::recursive_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::recursive_mutex m_output_mutex;
repro::DataRecorder *m_data_recorder;
Predicate<bool> m_popped;
Flags m_flags;
class Editline {
public:
Editline(const char *editor_name, FILE *input_file, FILE *output_file,
- FILE *error_file, bool color_prompts);
+ FILE *error_file, std::recursive_mutex &output_mutex,
+ bool color_prompts);
~Editline();
std::string m_suggestion_ansi_suffix;
std::size_t m_previous_autosuggestion_size = 0;
- std::mutex m_output_mutex;
+ std::recursive_mutex &m_output_mutex;
};
}
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_stdout);
bool EchoCommandNonInteractive(llvm::StringRef line,
const Flags &io_handler_flags) const;
void IOHandler::WaitForPop() { m_popped.WaitForValueEqualTo(true); }
void IOHandler::PrintAsync(const char *s, size_t len, bool is_stdout) {
+ std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
lldb::StreamFileSP stream = is_stdout ? m_output_sp : m_error_sp;
stream->Write(s, len);
stream->Flush();
m_input_sp && m_input_sp->GetIsRealTerminal();
if (use_editline) {
- m_editline_up = std::make_unique<Editline>(editline_name, GetInputFILE(),
- GetOutputFILE(), GetErrorFILE(),
- m_color_prompts);
+ m_editline_up = std::make_unique<Editline>(
+ editline_name, GetInputFILE(), GetOutputFILE(), GetErrorFILE(),
+ GetOutputMutex(), m_color_prompts);
m_editline_up->SetIsInputCompleteCallback(
[this](Editline *editline, StringList &lines) {
return this->IsInputCompleteCallback(editline, lines);
void IOHandlerEditline::PrintAsync(const char *s, size_t len, bool is_stdout) {
#if LLDB_ENABLE_LIBEDIT
if (m_editline_up) {
+ std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
lldb::StreamFileSP stream = is_stdout ? m_output_sp : m_error_sp;
m_editline_up->PrintAsync(stream.get(), s, len);
} else
}
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::recursive_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);
if (m_output_file) {
const int term_fd = fileno(m_output_file);
if (term_fd != -1) {
- static std::mutex *g_init_terminal_fds_mutex_ptr = nullptr;
+ static std::recursive_mutex *g_init_terminal_fds_mutex_ptr = nullptr;
static std::set<int> *g_init_terminal_fds_ptr = nullptr;
static llvm::once_flag g_once_flag;
llvm::call_once(g_once_flag, [&]() {
g_init_terminal_fds_mutex_ptr =
- new std::mutex(); // NOTE: Leak to avoid C++ destructor chain issues
+ new std::recursive_mutex(); // NOTE: Leak to avoid C++ destructor
+ // chain issues
g_init_terminal_fds_ptr = new std::set<int>(); // NOTE: Leak to avoid
// C++ destructor chain
// issues
// We must make sure to initialize the terminal a given file descriptor
// only once. If we do this multiple times, we start leaking memory.
- std::lock_guard<std::mutex> guard(*g_init_terminal_fds_mutex_ptr);
+ std::lock_guard<std::recursive_mutex> guard(
+ *g_init_terminal_fds_mutex_ptr);
if (g_init_terminal_fds_ptr->find(term_fd) ==
g_init_terminal_fds_ptr->end()) {
g_init_terminal_fds_ptr->insert(term_fd);
bool Editline::Interrupt() {
bool result = true;
- std::lock_guard<std::mutex> guard(m_output_mutex);
+ std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
if (m_editor_status == EditorStatus::Editing) {
fprintf(m_output_file, "^C\n");
result = m_input_connection.InterruptRead();
bool Editline::Cancel() {
bool result = true;
- std::lock_guard<std::mutex> guard(m_output_mutex);
+ std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
if (m_editor_status == EditorStatus::Editing) {
MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
fprintf(m_output_file, ANSI_CLEAR_BELOW);
m_input_lines = std::vector<EditLineStringType>();
m_input_lines.insert(m_input_lines.begin(), EditLineConstString(""));
- std::lock_guard<std::mutex> guard(m_output_mutex);
+ std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
lldbassert(m_editor_status != EditorStatus::Editing);
if (m_editor_status == EditorStatus::Interrupted) {
m_input_lines = std::vector<EditLineStringType>();
m_input_lines.insert(m_input_lines.begin(), EditLineConstString(""));
- std::lock_guard<std::mutex> guard(m_output_mutex);
+ std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
// Begin the line editing loop
DisplayInput();
SetCurrentLine(0);
}
void Editline::PrintAsync(Stream *stream, const char *s, size_t len) {
- std::lock_guard<std::mutex> guard(m_output_mutex);
+ std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
if (m_editor_status == EditorStatus::Editing) {
MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
fprintf(m_output_file, ANSI_CLEAR_BELOW);
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();
break;
}
}
- chunk_size = stream.Write(data, chunk_size);
+ {
+ std::lock_guard<std::recursive_mutex> 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<std::recursive_mutex> guard(io_handler.GetOutputMutex());
+ if (size > 0)
+ stream->Printf("\n... Interrupted.\n");
+ stream->Flush();
}
bool CommandInterpreter::EchoCommandNonInteractive(
// 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<std::recursive_mutex> guard(io_handler.GetOutputMutex());
io_handler.GetOutputStreamFileSP()->Printf(
"%s%s\n", io_handler.GetPrompt(), line.c_str());
+ }
}
StartHandlingCommand();
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);
}
}
bool IsInputComplete(lldb_private::Editline *editline,
lldb_private::StringList &lines);
+ std::recursive_mutex output_mutex;
std::unique_ptr<lldb_private::Editline> _editline_sp;
PseudoTerminal _pty;
// 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.