[Lldb-commits] [lldb] [lldb] Synchronize the debugger's stdout and stderr streams (PR #126630)

Jonas Devlieghere via lldb-commits lldb-commits at lists.llvm.org
Wed Feb 12 08:52:31 PST 2025


https://github.com/JDevlieghere updated https://github.com/llvm/llvm-project/pull/126630

>From 55cc3a52ade13d0a0fdf115d9ce32b32c659fac1 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Tue, 11 Feb 2025 19:54:35 -0800
Subject: [PATCH] [lldb] Synchronize the debugger's stdout and stderr streams

This patch improves the synchronization of the debugger's output and
error streams using two new abstractions: LockableStreamFile and
LockedStreamFile.

 - LockableStreamFile is a wrapper around a StreamFile and a mutex.
   Client cannot use the StreamFile without calling Lock, which returns
   a LockedStreamFile.

 - LockedStreamFile is an RAII object that locks the stream for the
   duration of its existence.  As long as you hold on to the returned
   object you are permitted to write to the stream. The destruction of
   the object automatically flush the output stream.

(cherry picked from commit d7b670430b918af5ab1501293c35158870955dc9)
---
 lldb/include/lldb/Core/Debugger.h             |  16 +--
 lldb/include/lldb/Core/IOHandler.h            |  33 ++---
 lldb/include/lldb/Host/Editline.h             |  14 +-
 lldb/include/lldb/Host/StreamFile.h           |  46 +++++++
 .../lldb/Interpreter/ScriptInterpreter.h      |  18 ++-
 lldb/include/lldb/Target/ThreadPlanTracer.h   |   2 +-
 lldb/include/lldb/lldb-forward.h              |   2 +
 lldb/source/API/SBDebugger.cpp                |  12 +-
 .../CommandObjectBreakpointCommand.cpp        |  10 +-
 .../source/Commands/CommandObjectCommands.cpp |  62 +++++----
 .../Commands/CommandObjectExpression.cpp      |  26 ++--
 lldb/source/Commands/CommandObjectTarget.cpp  |  35 ++---
 lldb/source/Commands/CommandObjectType.cpp    | 122 ++++++++++--------
 .../CommandObjectWatchpointCommand.cpp        |  14 +-
 lldb/source/Core/Debugger.cpp                 |  26 ++--
 lldb/source/Core/IOHandler.cpp                |  91 ++++++-------
 lldb/source/Core/IOHandlerCursesGUI.cpp       |   4 +-
 lldb/source/Expression/REPL.cpp               |  10 +-
 lldb/source/Host/common/Editline.cpp          | 114 +++++++++-------
 .../source/Interpreter/CommandInterpreter.cpp |  26 ++--
 lldb/source/Interpreter/ScriptInterpreter.cpp |  17 ++-
 .../DynamicLoaderDarwinKernel.cpp             |  44 ++++---
 .../UBSan/InstrumentationRuntimeUBSan.cpp     |   2 -
 .../Utility/ReportRetriever.cpp               |   4 +-
 .../Lua/ScriptInterpreterLua.cpp              |  24 +++-
 .../Python/ScriptInterpreterPython.cpp        |  35 ++---
 lldb/source/Target/ThreadPlanTracer.cpp       |  40 +++---
 lldb/unittests/Editline/EditlineTest.cpp      |  10 +-
 28 files changed, 489 insertions(+), 370 deletions(-)

diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h
index d7751ca045bb2..202f148e117c1 100644
--- a/lldb/include/lldb/Core/Debugger.h
+++ b/lldb/include/lldb/Core/Debugger.h
@@ -133,15 +133,15 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
 
   lldb::FileSP GetInputFileSP() { return m_input_file_sp; }
 
-  lldb::StreamFileSP GetOutputStreamSP() { return m_output_stream_sp; }
+  lldb::LockableStreamFileSP GetOutputStreamSP() { return m_output_stream_sp; }
 
-  lldb::StreamFileSP GetErrorStreamSP() { return m_error_stream_sp; }
+  lldb::LockableStreamFileSP GetErrorStreamSP() { return m_error_stream_sp; }
 
   File &GetInputFile() { return *m_input_file_sp; }
 
-  File &GetOutputFile() { return m_output_stream_sp->GetFile(); }
+  File &GetOutputFile() { return m_output_stream_sp->GetUnlockedFile(); }
 
-  File &GetErrorFile() { return m_error_stream_sp->GetFile(); }
+  File &GetErrorFile() { return m_error_stream_sp->GetUnlockedFile(); }
 
   repro::DataRecorder *GetInputRecorder();
 
@@ -202,8 +202,8 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
   // If any of the streams are not set, set them to the in/out/err stream of
   // the top most input reader to ensure they at least have something
   void AdoptTopIOHandlerFilesIfInvalid(lldb::FileSP &in,
-                                       lldb::StreamFileSP &out,
-                                       lldb::StreamFileSP &err);
+                                       lldb::LockableStreamFileSP &out,
+                                       lldb::LockableStreamFileSP &err);
 
   /// Run the given IO handler and return immediately.
   void RunIOHandlerAsync(const lldb::IOHandlerSP &reader_sp,
@@ -689,8 +689,8 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
 
   // these should never be NULL
   lldb::FileSP m_input_file_sp;
-  lldb::StreamFileSP m_output_stream_sp;
-  lldb::StreamFileSP m_error_stream_sp;
+  lldb::LockableStreamFileSP m_output_stream_sp;
+  lldb::LockableStreamFileSP m_error_stream_sp;
 
   /// Used for shadowing the input file when capturing a reproducer.
   repro::DataRecorder *m_input_recorder;
diff --git a/lldb/include/lldb/Core/IOHandler.h b/lldb/include/lldb/Core/IOHandler.h
index cb14d72413209..bd32a8ee0f898 100644
--- a/lldb/include/lldb/Core/IOHandler.h
+++ b/lldb/include/lldb/Core/IOHandler.h
@@ -58,8 +58,9 @@ class IOHandler {
   IOHandler(Debugger &debugger, IOHandler::Type type);
 
   IOHandler(Debugger &debugger, IOHandler::Type type,
-            const lldb::FileSP &input_sp, const lldb::StreamFileSP &output_sp,
-            const lldb::StreamFileSP &error_sp, uint32_t flags);
+            const lldb::FileSP &input_sp,
+            const lldb::LockableStreamFileSP &output_sp,
+            const lldb::LockableStreamFileSP &error_sp, uint32_t flags);
 
   virtual ~IOHandler();
 
@@ -117,17 +118,11 @@ class IOHandler {
 
   int GetErrorFD();
 
-  FILE *GetInputFILE();
-
-  FILE *GetOutputFILE();
-
-  FILE *GetErrorFILE();
-
   lldb::FileSP GetInputFileSP();
 
-  lldb::StreamFileSP GetOutputStreamFileSP();
+  lldb::LockableStreamFileSP GetOutputStreamFileSP();
 
-  lldb::StreamFileSP GetErrorStreamFileSP();
+  lldb::LockableStreamFileSP GetErrorStreamFileSP();
 
   Debugger &GetDebugger() { return m_debugger; }
 
@@ -160,14 +155,11 @@ class IOHandler {
 
   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;
+  lldb::LockableStreamFileSP m_output_sp;
+  lldb::LockableStreamFileSP m_error_sp;
   Predicate<bool> m_popped;
   Flags m_flags;
   Type m_type;
@@ -335,8 +327,8 @@ class IOHandlerEditline : public IOHandler {
 
   IOHandlerEditline(Debugger &debugger, IOHandler::Type type,
                     const lldb::FileSP &input_sp,
-                    const lldb::StreamFileSP &output_sp,
-                    const lldb::StreamFileSP &error_sp, uint32_t flags,
+                    const lldb::LockableStreamFileSP &output_sp,
+                    const lldb::LockableStreamFileSP &error_sp, uint32_t flags,
                     const char *editline_name, // Used for saving history files
                     llvm::StringRef prompt, llvm::StringRef continuation_prompt,
                     bool multi_line, bool color,
@@ -350,9 +342,10 @@ class IOHandlerEditline : public IOHandler {
                     IOHandlerDelegate &) = delete;
 
   IOHandlerEditline(Debugger &, IOHandler::Type, const lldb::FileSP &,
-                    const lldb::StreamFileSP &, const lldb::StreamFileSP &,
-                    uint32_t, const char *, const char *, const char *, bool,
-                    bool, uint32_t, IOHandlerDelegate &) = delete;
+                    const lldb::LockableStreamFileSP &,
+                    const lldb::LockableStreamFileSP &, uint32_t, const char *,
+                    const char *, const char *, bool, bool, uint32_t,
+                    IOHandlerDelegate &) = delete;
 
   ~IOHandlerEditline() override;
 
diff --git a/lldb/include/lldb/Host/Editline.h b/lldb/include/lldb/Host/Editline.h
index 27b863870090c..ac3bef0e26c7d 100644
--- a/lldb/include/lldb/Host/Editline.h
+++ b/lldb/include/lldb/Host/Editline.h
@@ -34,6 +34,7 @@
 #include <sstream>
 #include <vector>
 
+#include "lldb/Host/StreamFile.h"
 #include "lldb/lldb-private.h"
 
 #if !defined(_WIN32) && !defined(__ANDROID__)
@@ -151,8 +152,9 @@ using namespace line_editor;
 /// facility.  Both single- and multi-line editing are supported.
 class Editline {
 public:
-  Editline(const char *editor_name, FILE *input_file, FILE *output_file,
-           FILE *error_file, bool color, std::recursive_mutex &output_mutex);
+  Editline(const char *editor_name, FILE *input_file,
+           lldb::LockableStreamFileSP output_stream_sp,
+           lldb::LockableStreamFileSP error_stream_sp, bool color);
 
   ~Editline();
 
@@ -237,7 +239,8 @@ class Editline {
   /// Prompts for and reads a multi-line batch of user input.
   bool GetLines(int first_line_number, StringList &lines, bool &interrupted);
 
-  void PrintAsync(Stream *stream, const char *s, size_t len);
+  void PrintAsync(lldb::LockableStreamFileSP stream_sp, const char *s,
+                  size_t len);
 
   /// Convert the current input lines into a UTF8 StringList
   StringList GetInputAsStringList(int line_count = UINT32_MAX);
@@ -392,8 +395,8 @@ class Editline {
   volatile std::sig_atomic_t m_terminal_size_has_changed = 0;
   std::string m_editor_name;
   FILE *m_input_file;
-  FILE *m_output_file;
-  FILE *m_error_file;
+  lldb::LockableStreamFileSP m_output_stream_sp;
+  lldb::LockableStreamFileSP m_error_stream_sp;
   ConnectionFileDescriptor m_input_connection;
 
   IsInputCompleteCallbackType m_is_input_complete_callback;
@@ -411,7 +414,6 @@ class Editline {
   std::string m_suggestion_ansi_suffix;
 
   std::size_t m_previous_autosuggestion_size = 0;
-  std::recursive_mutex &m_output_mutex;
 };
 }
 
diff --git a/lldb/include/lldb/Host/StreamFile.h b/lldb/include/lldb/Host/StreamFile.h
index 2c96e13565a00..9df98a406a458 100644
--- a/lldb/include/lldb/Host/StreamFile.h
+++ b/lldb/include/lldb/Host/StreamFile.h
@@ -13,9 +13,11 @@
 #include "lldb/Utility/Stream.h"
 #include "lldb/lldb-defines.h"
 #include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-forward.h"
 
 #include <cstdint>
 #include <cstdio>
+#include <memory>
 
 namespace lldb_private {
 
@@ -52,6 +54,50 @@ class StreamFile : public Stream {
   const StreamFile &operator=(const StreamFile &) = delete;
 };
 
+class LockableStreamFile;
+class LockedStreamFile : public StreamFile {
+public:
+  ~LockedStreamFile() { Flush(); }
+
+private:
+  LockedStreamFile(std::shared_ptr<File> file, std::recursive_mutex &mutex)
+      : StreamFile(file), m_guard(mutex) {}
+
+  friend class LockableStreamFile;
+
+  std::lock_guard<std::recursive_mutex> m_guard;
+};
+
+class LockableStreamFile {
+public:
+  LockableStreamFile(std::shared_ptr<StreamFile> stream_file_sp)
+      : m_file_sp(stream_file_sp->GetFileSP()) {}
+  LockableStreamFile(StreamFile &stream_file)
+      : m_file_sp(stream_file.GetFileSP()) {}
+  LockableStreamFile(FILE *fh, bool transfer_ownership)
+      : m_file_sp(std::make_shared<NativeFile>(fh, transfer_ownership)) {}
+  LockableStreamFile(std::shared_ptr<File> file_sp) : m_file_sp(file_sp) {}
+
+  LockedStreamFile Lock() { return LockedStreamFile(m_file_sp, m_mutex); }
+
+  /// Unsafe accessors to get the underlying File without a lock. Exists for
+  /// legacy reasons.
+  /// @{
+  File &GetUnlockedFile() { return *m_file_sp; }
+  std::shared_ptr<File> GetUnlockedFileSP() { return m_file_sp; }
+  /// @}
+
+  std::recursive_mutex &GetMutex() { return m_mutex; }
+
+protected:
+  std::shared_ptr<File> m_file_sp;
+  std::recursive_mutex m_mutex;
+
+private:
+  LockableStreamFile(const LockableStreamFile &) = delete;
+  const LockableStreamFile &operator=(const LockableStreamFile &) = delete;
+};
+
 } // namespace lldb_private
 
 #endif // LLDB_HOST_STREAMFILE_H
diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
index 2c2bd6f232e09..8ae9dc0b72019 100644
--- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h
+++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
@@ -116,8 +116,12 @@ class ScriptInterpreterIORedirect {
   ~ScriptInterpreterIORedirect();
 
   lldb::FileSP GetInputFile() const { return m_input_file_sp; }
-  lldb::FileSP GetOutputFile() const { return m_output_file_sp->GetFileSP(); }
-  lldb::FileSP GetErrorFile() const { return m_error_file_sp->GetFileSP(); }
+  lldb::FileSP GetOutputFile() const {
+    return m_output_file_sp->GetUnlockedFileSP();
+  }
+  lldb::FileSP GetErrorFile() const {
+    return m_error_file_sp->GetUnlockedFileSP();
+  }
 
   /// Flush our output and error file handles.
   void Flush();
@@ -128,8 +132,8 @@ class ScriptInterpreterIORedirect {
   ScriptInterpreterIORedirect(Debugger &debugger, CommandReturnObject *result);
 
   lldb::FileSP m_input_file_sp;
-  lldb::StreamFileSP m_output_file_sp;
-  lldb::StreamFileSP m_error_file_sp;
+  lldb::LockableStreamFileSP m_output_file_sp;
+  lldb::LockableStreamFileSP m_error_file_sp;
   ThreadedCommunication m_communication;
   bool m_disconnect;
 };
@@ -478,7 +482,7 @@ class ScriptInterpreter : public PluginInterface {
     dest.clear();
     return false;
   }
-  
+
   virtual StructuredData::ObjectSP
   GetOptionsForCommandObject(StructuredData::GenericSP cmd_obj_sp) {
     return {};
@@ -488,9 +492,9 @@ class ScriptInterpreter : public PluginInterface {
   GetArgumentsForCommandObject(StructuredData::GenericSP cmd_obj_sp) {
     return {};
   }
-  
+
   virtual bool SetOptionValueForCommandObject(
-      StructuredData::GenericSP cmd_obj_sp, ExecutionContext *exe_ctx, 
+      StructuredData::GenericSP cmd_obj_sp, ExecutionContext *exe_ctx,
       llvm::StringRef long_option, llvm::StringRef value) {
     return false;
   }
diff --git a/lldb/include/lldb/Target/ThreadPlanTracer.h b/lldb/include/lldb/Target/ThreadPlanTracer.h
index a6fd2f031dc22..7c0d5b21feba0 100644
--- a/lldb/include/lldb/Target/ThreadPlanTracer.h
+++ b/lldb/include/lldb/Target/ThreadPlanTracer.h
@@ -56,7 +56,7 @@ class ThreadPlanTracer {
   Process &m_process;
   lldb::tid_t m_tid;
 
-  Stream *GetLogStream();
+  lldb::StreamSP GetLogStream();
 
   virtual void Log();
 
diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h
index fc7456a4b9a32..cda55ef06e549 100644
--- a/lldb/include/lldb/lldb-forward.h
+++ b/lldb/include/lldb/lldb-forward.h
@@ -215,6 +215,7 @@ class StoppointCallbackContext;
 class Stream;
 class StreamFile;
 class StreamString;
+class LockableStreamFile;
 class StringList;
 class StringTableReader;
 class StructuredDataImpl;
@@ -432,6 +433,7 @@ typedef std::unique_ptr<lldb_private::StackFrameRecognizerManager>
 typedef std::shared_ptr<lldb_private::StopInfo> StopInfoSP;
 typedef std::shared_ptr<lldb_private::Stream> StreamSP;
 typedef std::shared_ptr<lldb_private::StreamFile> StreamFileSP;
+typedef std::shared_ptr<lldb_private::LockableStreamFile> LockableStreamFileSP;
 typedef std::shared_ptr<lldb_private::StringSummaryFormat>
     StringTypeSummaryImplSP;
 typedef std::unique_ptr<lldb_private::StructuredDataImpl> StructuredDataImplUP;
diff --git a/lldb/source/API/SBDebugger.cpp b/lldb/source/API/SBDebugger.cpp
index bf19d2ff8333c..7b7e6ea238419 100644
--- a/lldb/source/API/SBDebugger.cpp
+++ b/lldb/source/API/SBDebugger.cpp
@@ -509,14 +509,14 @@ SBFile SBDebugger::GetInputFile() {
 FILE *SBDebugger::GetOutputFileHandle() {
   LLDB_INSTRUMENT_VA(this);
   if (m_opaque_sp)
-    return m_opaque_sp->GetOutputStreamSP()->GetFile().GetStream();
+    return m_opaque_sp->GetOutputStreamSP()->GetUnlockedFile().GetStream();
   return nullptr;
 }
 
 SBFile SBDebugger::GetOutputFile() {
   LLDB_INSTRUMENT_VA(this);
   if (m_opaque_sp)
-    return SBFile(m_opaque_sp->GetOutputStreamSP()->GetFileSP());
+    return SBFile(m_opaque_sp->GetOutputStreamSP()->GetUnlockedFileSP());
   return SBFile();
 }
 
@@ -524,7 +524,7 @@ FILE *SBDebugger::GetErrorFileHandle() {
   LLDB_INSTRUMENT_VA(this);
 
   if (m_opaque_sp)
-    return m_opaque_sp->GetErrorStreamSP()->GetFile().GetStream();
+    return m_opaque_sp->GetErrorStreamSP()->GetUnlockedFile().GetStream();
   return nullptr;
 }
 
@@ -532,7 +532,7 @@ SBFile SBDebugger::GetErrorFile() {
   LLDB_INSTRUMENT_VA(this);
   SBFile file;
   if (m_opaque_sp)
-    return SBFile(m_opaque_sp->GetErrorStreamSP()->GetFileSP());
+    return SBFile(m_opaque_sp->GetErrorStreamSP()->GetUnlockedFileSP());
   return SBFile();
 }
 
@@ -573,8 +573,8 @@ void SBDebugger::HandleCommand(const char *command) {
 
     sb_interpreter.HandleCommand(command, result, false);
 
-    result.PutError(m_opaque_sp->GetErrorStreamSP()->GetFileSP());
-    result.PutOutput(m_opaque_sp->GetOutputStreamSP()->GetFileSP());
+    result.PutError(m_opaque_sp->GetErrorStreamSP()->GetUnlockedFileSP());
+    result.PutOutput(m_opaque_sp->GetOutputStreamSP()->GetUnlockedFileSP());
 
     if (!m_opaque_sp->GetAsyncExecution()) {
       SBProcess process(GetCommandInterpreter().GetProcess());
diff --git a/lldb/source/Commands/CommandObjectBreakpointCommand.cpp b/lldb/source/Commands/CommandObjectBreakpointCommand.cpp
index ac2db5973effa..a913ed5fa12b3 100644
--- a/lldb/source/Commands/CommandObjectBreakpointCommand.cpp
+++ b/lldb/source/Commands/CommandObjectBreakpointCommand.cpp
@@ -193,10 +193,12 @@ are no syntax errors may indicate that a function was declared but never called.
   Options *GetOptions() override { return &m_all_options; }
 
   void IOHandlerActivated(IOHandler &io_handler, bool interactive) override {
-    StreamFileSP output_sp(io_handler.GetOutputStreamFileSP());
-    if (output_sp && interactive) {
-      output_sp->PutCString(g_reader_instructions);
-      output_sp->Flush();
+    if (interactive) {
+      if (lldb::LockableStreamFileSP output_sp =
+              io_handler.GetOutputStreamFileSP()) {
+        LockedStreamFile locked_stream = output_sp->Lock();
+        locked_stream.PutCString(g_reader_instructions);
+      }
     }
   }
 
diff --git a/lldb/source/Commands/CommandObjectCommands.cpp b/lldb/source/Commands/CommandObjectCommands.cpp
index f069b2feb5cb7..dd841cb5cb4cc 100644
--- a/lldb/source/Commands/CommandObjectCommands.cpp
+++ b/lldb/source/Commands/CommandObjectCommands.cpp
@@ -11,6 +11,7 @@
 #include "CommandObjectRegexCommand.h"
 #include "lldb/Core/Debugger.h"
 #include "lldb/Core/IOHandler.h"
+#include "lldb/Host/StreamFile.h"
 #include "lldb/Interpreter/CommandHistory.h"
 #include "lldb/Interpreter/CommandInterpreter.h"
 #include "lldb/Interpreter/CommandOptionArgumentTable.h"
@@ -792,12 +793,15 @@ a number follows 'f':"
 
 protected:
   void IOHandlerActivated(IOHandler &io_handler, bool interactive) override {
-    StreamFileSP output_sp(io_handler.GetOutputStreamFileSP());
-    if (output_sp && interactive) {
-      output_sp->PutCString("Enter one or more sed substitution commands in "
-                            "the form: 's/<regex>/<subst>/'.\nTerminate the "
-                            "substitution list with an empty line.\n");
-      output_sp->Flush();
+    if (interactive) {
+      if (lldb::LockableStreamFileSP output_sp =
+              io_handler.GetOutputStreamFileSP()) {
+        LockedStreamFile locked_stream = output_sp->Lock();
+        locked_stream.PutCString(
+            "Enter one or more sed substitution commands in "
+            "the form: 's/<regex>/<subst>/'.\nTerminate the "
+            "substitution list with an empty line.\n");
+      }
     }
   }
 
@@ -2377,16 +2381,18 @@ class CommandObjectCommandsScriptAdd : public CommandObjectParsed,
   };
 
   void IOHandlerActivated(IOHandler &io_handler, bool interactive) override {
-    StreamFileSP output_sp(io_handler.GetOutputStreamFileSP());
-    if (output_sp && interactive) {
-      output_sp->PutCString(g_python_command_instructions);
-      output_sp->Flush();
+    if (interactive) {
+      if (lldb::LockableStreamFileSP output_sp =
+              io_handler.GetOutputStreamFileSP()) {
+        LockedStreamFile locked_stream = output_sp->Lock();
+        locked_stream.PutCString(g_python_command_instructions);
+      }
     }
   }
 
   void IOHandlerInputComplete(IOHandler &io_handler,
                               std::string &data) override {
-    StreamFileSP error_sp = io_handler.GetErrorStreamFileSP();
+    LockableStreamFileSP error_sp = io_handler.GetErrorStreamFileSP();
 
     ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
     if (interpreter) {
@@ -2396,9 +2402,10 @@ class CommandObjectCommandsScriptAdd : public CommandObjectParsed,
         std::string funct_name_str;
         if (interpreter->GenerateScriptAliasFunction(lines, funct_name_str)) {
           if (funct_name_str.empty()) {
-            error_sp->Printf("error: unable to obtain a function name, didn't "
-                             "add python command.\n");
-            error_sp->Flush();
+            LockedStreamFile locked_stream = error_sp->Lock();
+            locked_stream.Printf(
+                "error: unable to obtain a function name, didn't "
+                "add python command.\n");
           } else {
             // everything should be fine now, let's add this alias
 
@@ -2409,33 +2416,36 @@ class CommandObjectCommandsScriptAdd : public CommandObjectParsed,
               Status error = m_interpreter.AddUserCommand(
                   m_cmd_name, command_obj_sp, m_overwrite);
               if (error.Fail()) {
-                error_sp->Printf("error: unable to add selected command: '%s'",
-                                 error.AsCString());
-                error_sp->Flush();
+                LockedStreamFile locked_stream = error_sp->Lock();
+                locked_stream.Printf(
+                    "error: unable to add selected command: '%s'",
+                    error.AsCString());
               }
             } else {
               llvm::Error llvm_error = m_container->LoadUserSubcommand(
                   m_cmd_name, command_obj_sp, m_overwrite);
               if (llvm_error) {
-                error_sp->Printf("error: unable to add selected command: '%s'",
-                               llvm::toString(std::move(llvm_error)).c_str());
-                error_sp->Flush();
+                LockedStreamFile locked_stream = error_sp->Lock();
+                locked_stream.Printf(
+                    "error: unable to add selected command: '%s'",
+                    llvm::toString(std::move(llvm_error)).c_str());
               }
             }
           }
         } else {
-          error_sp->Printf(
+          LockedStreamFile locked_stream = error_sp->Lock();
+          locked_stream.Printf(
               "error: unable to create function, didn't add python command\n");
-          error_sp->Flush();
         }
       } else {
-        error_sp->Printf("error: empty function, didn't add python command\n");
-        error_sp->Flush();
+        LockedStreamFile locked_stream = error_sp->Lock();
+        locked_stream.Printf(
+            "error: empty function, didn't add python command\n");
       }
     } else {
-      error_sp->Printf(
+      LockedStreamFile locked_stream = error_sp->Lock();
+      locked_stream.Printf(
           "error: script interpreter missing, didn't add python command\n");
-      error_sp->Flush();
     }
 
     io_handler.SetIsDone(true);
diff --git a/lldb/source/Commands/CommandObjectExpression.cpp b/lldb/source/Commands/CommandObjectExpression.cpp
index 13491b5c79442..4879f3aa50097 100644
--- a/lldb/source/Commands/CommandObjectExpression.cpp
+++ b/lldb/source/Commands/CommandObjectExpression.cpp
@@ -12,6 +12,7 @@
 #include "lldb/Expression/REPL.h"
 #include "lldb/Expression/UserExpression.h"
 #include "lldb/Host/OptionParser.h"
+#include "lldb/Host/StreamFile.h"
 #include "lldb/Interpreter/CommandInterpreter.h"
 #include "lldb/Interpreter/CommandOptionArgumentTable.h"
 #include "lldb/Interpreter/CommandReturnObject.h"
@@ -22,6 +23,7 @@
 #include "lldb/Target/Target.h"
 #include "lldb/Utility/DiagnosticsRendering.h"
 #include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-forward.h"
 #include "lldb/lldb-private-enumerations.h"
 
 using namespace lldb;
@@ -500,19 +502,16 @@ bool CommandObjectExpression::EvaluateExpression(llvm::StringRef expr,
 void CommandObjectExpression::IOHandlerInputComplete(IOHandler &io_handler,
                                                      std::string &line) {
   io_handler.SetIsDone(true);
-  StreamFileSP output_sp = io_handler.GetOutputStreamFileSP();
-  StreamFileSP error_sp = io_handler.GetErrorStreamFileSP();
+  LockedStreamFile locked_output_stream =
+      io_handler.GetOutputStreamFileSP()->Lock();
+  LockedStreamFile locked_error_stream =
+      io_handler.GetErrorStreamFileSP()->Lock();
 
   CommandReturnObject return_obj(
       GetCommandInterpreter().GetDebugger().GetUseColor());
-  EvaluateExpression(line.c_str(), *output_sp, *error_sp, return_obj);
-
-  if (output_sp)
-    output_sp->Flush();
-  if (error_sp) {
-    *error_sp << return_obj.GetErrorString();
-    error_sp->Flush();
-  }
+  EvaluateExpression(line.c_str(), locked_output_stream, locked_error_stream,
+                     return_obj);
+  locked_output_stream << return_obj.GetErrorString();
 }
 
 bool CommandObjectExpression::IOHandlerIsInputComplete(IOHandler &io_handler,
@@ -544,11 +543,10 @@ void CommandObjectExpression::GetMultilineExpression() {
                             1, // Show line numbers starting at 1
                             *this));
 
-  StreamFileSP output_sp = io_handler_sp->GetOutputStreamFileSP();
-  if (output_sp) {
-    output_sp->PutCString(
+  if (LockableStreamFileSP output_sp = io_handler_sp->GetOutputStreamFileSP()) {
+    LockedStreamFile locked_stream = output_sp->Lock();
+    locked_stream.PutCString(
         "Enter expressions, then terminate with an empty line to evaluate:\n");
-    output_sp->Flush();
   }
   debugger.RunIOHandlerAsync(io_handler_sp);
 }
diff --git a/lldb/source/Commands/CommandObjectTarget.cpp b/lldb/source/Commands/CommandObjectTarget.cpp
index da50fe04fa2b6..b95eaa6bc56ef 100644
--- a/lldb/source/Commands/CommandObjectTarget.cpp
+++ b/lldb/source/Commands/CommandObjectTarget.cpp
@@ -57,6 +57,7 @@
 #include "lldb/Utility/Timer.h"
 #include "lldb/ValueObject/ValueObjectVariable.h"
 #include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-forward.h"
 #include "lldb/lldb-private-enumerations.h"
 
 #include "clang/Frontend/CompilerInstance.h"
@@ -4918,11 +4919,13 @@ Filter Options:
 
 protected:
   void IOHandlerActivated(IOHandler &io_handler, bool interactive) override {
-    StreamFileSP output_sp(io_handler.GetOutputStreamFileSP());
-    if (output_sp && interactive) {
-      output_sp->PutCString(
-          "Enter your stop hook command(s).  Type 'DONE' to end.\n");
-      output_sp->Flush();
+    if (interactive) {
+      if (lldb::LockableStreamFileSP output_sp =
+              io_handler.GetOutputStreamFileSP()) {
+        LockedStreamFile locked_stream = output_sp->Lock();
+        locked_stream.PutCString(
+            "Enter your stop hook command(s).  Type 'DONE' to end.\n");
+      }
     }
   }
 
@@ -4930,12 +4933,12 @@ Filter Options:
                               std::string &line) override {
     if (m_stop_hook_sp) {
       if (line.empty()) {
-        StreamFileSP error_sp(io_handler.GetErrorStreamFileSP());
-        if (error_sp) {
-          error_sp->Printf("error: stop hook #%" PRIu64
-                           " aborted, no commands.\n",
-                           m_stop_hook_sp->GetID());
-          error_sp->Flush();
+        if (lldb::LockableStreamFileSP error_sp =
+                io_handler.GetErrorStreamFileSP()) {
+          LockedStreamFile locked_stream = error_sp->Lock();
+          locked_stream.Printf("error: stop hook #%" PRIu64
+                               " aborted, no commands.\n",
+                               m_stop_hook_sp->GetID());
         }
         GetTarget().UndoCreateStopHook(m_stop_hook_sp->GetID());
       } else {
@@ -4944,11 +4947,11 @@ Filter Options:
             static_cast<Target::StopHookCommandLine *>(m_stop_hook_sp.get());
 
         hook_ptr->SetActionFromString(line);
-        StreamFileSP output_sp(io_handler.GetOutputStreamFileSP());
-        if (output_sp) {
-          output_sp->Printf("Stop hook #%" PRIu64 " added.\n",
-                            m_stop_hook_sp->GetID());
-          output_sp->Flush();
+        if (lldb::LockableStreamFileSP output_sp =
+                io_handler.GetOutputStreamFileSP()) {
+          LockedStreamFile locked_stream = output_sp->Lock();
+          locked_stream.Printf("Stop hook #%" PRIu64 " added.\n",
+                               m_stop_hook_sp->GetID());
         }
       }
       m_stop_hook_sp.reset();
diff --git a/lldb/source/Commands/CommandObjectType.cpp b/lldb/source/Commands/CommandObjectType.cpp
index e4c6e374446e8..41630b61c2f0e 100644
--- a/lldb/source/Commands/CommandObjectType.cpp
+++ b/lldb/source/Commands/CommandObjectType.cpp
@@ -14,6 +14,7 @@
 #include "lldb/DataFormatters/FormatClasses.h"
 #include "lldb/Host/Config.h"
 #include "lldb/Host/OptionParser.h"
+#include "lldb/Host/StreamFile.h"
 #include "lldb/Interpreter/CommandInterpreter.h"
 #include "lldb/Interpreter/CommandObject.h"
 #include "lldb/Interpreter/CommandOptionArgumentTable.h"
@@ -32,6 +33,7 @@
 #include "lldb/Utility/ConstString.h"
 #include "lldb/Utility/RegularExpression.h"
 #include "lldb/Utility/StringList.h"
+#include "lldb/lldb-forward.h"
 
 #include "llvm/ADT/STLExtras.h"
 
@@ -167,16 +169,17 @@ class CommandObjectTypeSummaryAdd : public CommandObjectParsed,
         "for\n"
         "        internal_dict: an LLDB support object not to be used\"\"\"\n";
 
-    StreamFileSP output_sp(io_handler.GetOutputStreamFileSP());
-    if (output_sp && interactive) {
-      output_sp->PutCString(g_summary_addreader_instructions);
-      output_sp->Flush();
+    if (interactive) {
+      if (LockableStreamFileSP output_sp = io_handler.GetOutputStreamFileSP()) {
+        LockedStreamFile locked_stream = output_sp->Lock();
+        locked_stream.PutCString(g_summary_addreader_instructions);
+      }
     }
   }
 
   void IOHandlerInputComplete(IOHandler &io_handler,
                               std::string &data) override {
-    StreamFileSP error_sp = io_handler.GetErrorStreamFileSP();
+    LockableStreamFileSP error_sp = io_handler.GetErrorStreamFileSP();
 
 #if LLDB_ENABLE_PYTHON
     ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
@@ -197,9 +200,10 @@ class CommandObjectTypeSummaryAdd : public CommandObjectParsed,
             if (interpreter->GenerateTypeScriptFunction(lines,
                                                         funct_name_str)) {
               if (funct_name_str.empty()) {
-                error_sp->Printf("unable to obtain a valid function name from "
-                                 "the script interpreter.\n");
-                error_sp->Flush();
+                LockedStreamFile locked_stream = error_sp->Lock();
+                locked_stream.Printf(
+                    "unable to obtain a valid function name from "
+                    "the script interpreter.\n");
               } else {
                 // now I have a valid function name, let's add this as script
                 // for every type in the list
@@ -216,8 +220,8 @@ class CommandObjectTypeSummaryAdd : public CommandObjectParsed,
                              options->m_match_type, options->m_category,
                              &error);
                   if (error.Fail()) {
-                    error_sp->Printf("error: %s", error.AsCString());
-                    error_sp->Flush();
+                    LockedStreamFile locked_stream = error_sp->Lock();
+                    locked_stream.Printf("error: %s", error.AsCString());
                   }
                 }
 
@@ -228,41 +232,42 @@ class CommandObjectTypeSummaryAdd : public CommandObjectParsed,
                     CommandObjectTypeSummaryAdd::AddNamedSummary(
                         options->m_name, script_format, &error);
                     if (error.Fail()) {
-                      error_sp->Printf("error: %s", error.AsCString());
-                      error_sp->Flush();
+                      LockedStreamFile locked_stream = error_sp->Lock();
+                      locked_stream.Printf("error: %s", error.AsCString());
                     }
                   } else {
-                    error_sp->Printf("error: %s", error.AsCString());
-                    error_sp->Flush();
+                    LockedStreamFile locked_stream = error_sp->Lock();
+                    locked_stream.Printf("error: %s", error.AsCString());
                   }
                 } else {
                   if (error.AsCString()) {
-                    error_sp->Printf("error: %s", error.AsCString());
-                    error_sp->Flush();
+                    LockedStreamFile locked_stream = error_sp->Lock();
+                    locked_stream.Printf("error: %s", error.AsCString());
                   }
                 }
               }
             } else {
-              error_sp->Printf("error: unable to generate a function.\n");
-              error_sp->Flush();
+              LockedStreamFile locked_stream = error_sp->Lock();
+              locked_stream.Printf("error: unable to generate a function.\n");
             }
           } else {
-            error_sp->Printf("error: no script interpreter.\n");
-            error_sp->Flush();
+            LockedStreamFile locked_stream = error_sp->Lock();
+            locked_stream.Printf("error: no script interpreter.\n");
           }
         } else {
-          error_sp->Printf("error: internal synchronization information "
-                           "missing or invalid.\n");
-          error_sp->Flush();
+          LockedStreamFile locked_stream = error_sp->Lock();
+          locked_stream.Printf("error: internal synchronization information "
+                               "missing or invalid.\n");
         }
       } else {
-        error_sp->Printf("error: empty function, didn't add python command.\n");
-        error_sp->Flush();
+        LockedStreamFile locked_stream = error_sp->Lock();
+        locked_stream.Printf(
+            "error: empty function, didn't add python command.\n");
       }
     } else {
-      error_sp->Printf(
+      LockedStreamFile locked_stream = error_sp->Lock();
+      locked_stream.Printf(
           "error: script interpreter missing, didn't add python command.\n");
-      error_sp->Flush();
     }
 #endif
     io_handler.SetIsDone(true);
@@ -404,16 +409,17 @@ class CommandObjectTypeSynthAdd : public CommandObjectParsed,
   }
 
   void IOHandlerActivated(IOHandler &io_handler, bool interactive) override {
-    StreamFileSP output_sp(io_handler.GetOutputStreamFileSP());
-    if (output_sp && interactive) {
-      output_sp->PutCString(g_synth_addreader_instructions);
-      output_sp->Flush();
+    if (interactive) {
+      if (LockableStreamFileSP output_sp = io_handler.GetOutputStreamFileSP()) {
+        LockedStreamFile locked_stream = output_sp->Lock();
+        locked_stream.PutCString(g_synth_addreader_instructions);
+      }
     }
   }
 
   void IOHandlerInputComplete(IOHandler &io_handler,
                               std::string &data) override {
-    StreamFileSP error_sp = io_handler.GetErrorStreamFileSP();
+    LockableStreamFileSP error_sp = io_handler.GetErrorStreamFileSP();
 
 #if LLDB_ENABLE_PYTHON
     ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
@@ -433,9 +439,10 @@ class CommandObjectTypeSynthAdd : public CommandObjectParsed,
             std::string class_name_str;
             if (interpreter->GenerateTypeSynthClass(lines, class_name_str)) {
               if (class_name_str.empty()) {
-                error_sp->Printf(
+
+                LockedStreamFile locked_stream = error_sp->Lock();
+                locked_stream.Printf(
                     "error: unable to obtain a proper name for the class.\n");
-                error_sp->Flush();
               } else {
                 // everything should be fine now, let's add the synth provider
                 // class
@@ -459,37 +466,39 @@ class CommandObjectTypeSynthAdd : public CommandObjectParsed,
                     if (AddSynth(ConstString(type_name), synth_provider,
                                  options->m_match_type, options->m_category,
                                  &error)) {
-                      error_sp->Printf("error: %s\n", error.AsCString());
-                      error_sp->Flush();
+                      LockedStreamFile locked_stream = error_sp->Lock();
+                      locked_stream.Printf("error: %s\n", error.AsCString());
                       break;
                     }
                   } else {
-                    error_sp->Printf("error: invalid type name.\n");
-                    error_sp->Flush();
+                    LockedStreamFile locked_stream = error_sp->Lock();
+                    locked_stream.Printf("error: invalid type name.\n");
                     break;
                   }
                 }
               }
             } else {
-              error_sp->Printf("error: unable to generate a class.\n");
-              error_sp->Flush();
+              LockedStreamFile locked_stream = error_sp->Lock();
+              locked_stream.Printf("error: unable to generate a class.\n");
             }
           } else {
-            error_sp->Printf("error: no script interpreter.\n");
-            error_sp->Flush();
+            LockedStreamFile locked_stream = error_sp->Lock();
+            locked_stream.Printf("error: no script interpreter.\n");
           }
         } else {
-          error_sp->Printf("error: internal synchronization data missing.\n");
-          error_sp->Flush();
+          LockedStreamFile locked_stream = error_sp->Lock();
+          locked_stream.Printf(
+              "error: internal synchronization data missing.\n");
         }
       } else {
-        error_sp->Printf("error: empty function, didn't add python command.\n");
-        error_sp->Flush();
+        LockedStreamFile locked_stream = error_sp->Lock();
+        locked_stream.Printf(
+            "error: empty function, didn't add python command.\n");
       }
     } else {
-      error_sp->Printf(
+      LockedStreamFile locked_stream = error_sp->Lock();
+      locked_stream.Printf(
           "error: script interpreter missing, didn't add python command.\n");
-      error_sp->Flush();
     }
 
 #endif
@@ -952,8 +961,8 @@ class CommandObjectTypeFormatterClear : public CommandObjectParsed {
 class CommandObjectTypeFormatDelete : public CommandObjectTypeFormatterDelete {
 public:
   CommandObjectTypeFormatDelete(CommandInterpreter &interpreter)
-      : CommandObjectTypeFormatterDelete(
-            interpreter, eFormatCategoryItemFormat) {}
+      : CommandObjectTypeFormatterDelete(interpreter,
+                                         eFormatCategoryItemFormat) {}
 
   ~CommandObjectTypeFormatDelete() override = default;
 };
@@ -1603,8 +1612,8 @@ bool CommandObjectTypeSummaryAdd::AddSummary(ConstString type_name,
 class CommandObjectTypeSummaryDelete : public CommandObjectTypeFormatterDelete {
 public:
   CommandObjectTypeSummaryDelete(CommandInterpreter &interpreter)
-      : CommandObjectTypeFormatterDelete(
-            interpreter, eFormatCategoryItemSummary) {}
+      : CommandObjectTypeFormatterDelete(interpreter,
+                                         eFormatCategoryItemSummary) {}
 
   ~CommandObjectTypeSummaryDelete() override = default;
 
@@ -2070,8 +2079,8 @@ class CommandObjectTypeSynthList
 class CommandObjectTypeFilterDelete : public CommandObjectTypeFormatterDelete {
 public:
   CommandObjectTypeFilterDelete(CommandInterpreter &interpreter)
-      : CommandObjectTypeFormatterDelete(
-            interpreter, eFormatCategoryItemFilter) {}
+      : CommandObjectTypeFormatterDelete(interpreter,
+                                         eFormatCategoryItemFilter) {}
 
   ~CommandObjectTypeFilterDelete() override = default;
 };
@@ -2081,13 +2090,12 @@ class CommandObjectTypeFilterDelete : public CommandObjectTypeFormatterDelete {
 class CommandObjectTypeSynthDelete : public CommandObjectTypeFormatterDelete {
 public:
   CommandObjectTypeSynthDelete(CommandInterpreter &interpreter)
-      : CommandObjectTypeFormatterDelete(
-            interpreter, eFormatCategoryItemSynth) {}
+      : CommandObjectTypeFormatterDelete(interpreter,
+                                         eFormatCategoryItemSynth) {}
 
   ~CommandObjectTypeSynthDelete() override = default;
 };
 
-
 // CommandObjectTypeFilterClear
 
 class CommandObjectTypeFilterClear : public CommandObjectTypeFormatterClear {
diff --git a/lldb/source/Commands/CommandObjectWatchpointCommand.cpp b/lldb/source/Commands/CommandObjectWatchpointCommand.cpp
index ab1a2b390936c..507ef3fbe4759 100644
--- a/lldb/source/Commands/CommandObjectWatchpointCommand.cpp
+++ b/lldb/source/Commands/CommandObjectWatchpointCommand.cpp
@@ -14,11 +14,13 @@
 #include "lldb/Breakpoint/Watchpoint.h"
 #include "lldb/Core/IOHandler.h"
 #include "lldb/Host/OptionParser.h"
+#include "lldb/Host/StreamFile.h"
 #include "lldb/Interpreter/CommandInterpreter.h"
 #include "lldb/Interpreter/CommandOptionArgumentTable.h"
 #include "lldb/Interpreter/CommandReturnObject.h"
 #include "lldb/Interpreter/OptionArgParser.h"
 #include "lldb/Target/Target.h"
+#include "lldb/lldb-forward.h"
 
 using namespace lldb;
 using namespace lldb_private;
@@ -170,11 +172,13 @@ are no syntax errors may indicate that a function was declared but never called.
   Options *GetOptions() override { return &m_options; }
 
   void IOHandlerActivated(IOHandler &io_handler, bool interactive) override {
-    StreamFileSP output_sp(io_handler.GetOutputStreamFileSP());
-    if (output_sp && interactive) {
-      output_sp->PutCString(
-          "Enter your debugger command(s).  Type 'DONE' to end.\n");
-      output_sp->Flush();
+    if (interactive) {
+      if (lldb::LockableStreamFileSP output_sp =
+              io_handler.GetOutputStreamFileSP()) {
+        LockedStreamFile locked_stream = output_sp->Lock();
+        locked_stream.PutCString(
+            "Enter your debugger command(s).  Type 'DONE' to end.\n");
+      }
     }
   }
 
diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp
index 18569e155b517..f2e1bb9db2d90 100644
--- a/lldb/source/Core/Debugger.cpp
+++ b/lldb/source/Core/Debugger.cpp
@@ -874,8 +874,8 @@ Debugger::Debugger(lldb::LogOutputCallback log_callback, void *baton)
     : UserID(g_unique_id++),
       Properties(std::make_shared<OptionValueProperties>()),
       m_input_file_sp(std::make_shared<NativeFile>(stdin, false)),
-      m_output_stream_sp(std::make_shared<StreamFile>(stdout, false)),
-      m_error_stream_sp(std::make_shared<StreamFile>(stderr, false)),
+      m_output_stream_sp(std::make_shared<LockableStreamFile>(stdout, false)),
+      m_error_stream_sp(std::make_shared<LockableStreamFile>(stderr, false)),
       m_input_recorder(nullptr),
       m_broadcaster_manager_sp(BroadcasterManager::MakeBroadcasterManager()),
       m_terminal_state(), m_target_list(*this), m_platform_list(),
@@ -1083,12 +1083,12 @@ void Debugger::SetInputFile(FileSP file_sp) {
 
 void Debugger::SetOutputFile(FileSP file_sp) {
   assert(file_sp && file_sp->IsValid());
-  m_output_stream_sp = std::make_shared<StreamFile>(file_sp);
+  m_output_stream_sp = std::make_shared<LockableStreamFile>(file_sp);
 }
 
 void Debugger::SetErrorFile(FileSP file_sp) {
   assert(file_sp && file_sp->IsValid());
-  m_error_stream_sp = std::make_shared<StreamFile>(file_sp);
+  m_error_stream_sp = std::make_shared<LockableStreamFile>(file_sp);
 }
 
 void Debugger::SaveInputTerminalState() {
@@ -1198,9 +1198,10 @@ bool Debugger::CheckTopIOHandlerTypes(IOHandler::Type top_type,
 void Debugger::PrintAsync(const char *s, size_t len, bool is_stdout) {
   bool printed = m_io_handler_stack.PrintAsync(s, len, is_stdout);
   if (!printed) {
-    lldb::StreamFileSP stream =
+    LockableStreamFileSP stream_sp =
         is_stdout ? m_output_stream_sp : m_error_stream_sp;
-    stream->Write(s, len);
+    LockedStreamFile locked_stream = stream_sp->Lock();
+    locked_stream.Write(s, len);
   }
 }
 
@@ -1225,8 +1226,9 @@ void Debugger::RunIOHandlerAsync(const IOHandlerSP &reader_sp,
   PushIOHandler(reader_sp, cancel_top_handler);
 }
 
-void Debugger::AdoptTopIOHandlerFilesIfInvalid(FileSP &in, StreamFileSP &out,
-                                               StreamFileSP &err) {
+void Debugger::AdoptTopIOHandlerFilesIfInvalid(FileSP &in,
+                                               LockableStreamFileSP &out,
+                                               LockableStreamFileSP &err) {
   // Before an IOHandler runs, it must have in/out/err streams. This function
   // is called when one ore more of the streams are nullptr. We use the top
   // input reader's in/out/err streams, or fall back to the debugger file
@@ -1245,24 +1247,24 @@ void Debugger::AdoptTopIOHandlerFilesIfInvalid(FileSP &in, StreamFileSP &out,
       in = std::make_shared<NativeFile>(stdin, false);
   }
   // If no STDOUT has been set, then set it appropriately
-  if (!out || !out->GetFile().IsValid()) {
+  if (!out || !out->GetUnlockedFile().IsValid()) {
     if (top_reader_sp)
       out = top_reader_sp->GetOutputStreamFileSP();
     else
       out = GetOutputStreamSP();
     // If there is nothing, use stdout
     if (!out)
-      out = std::make_shared<StreamFile>(stdout, false);
+      out = std::make_shared<LockableStreamFile>(stdout, false);
   }
   // If no STDERR has been set, then set it appropriately
-  if (!err || !err->GetFile().IsValid()) {
+  if (!err || !err->GetUnlockedFile().IsValid()) {
     if (top_reader_sp)
       err = top_reader_sp->GetErrorStreamFileSP();
     else
       err = GetErrorStreamSP();
     // If there is nothing, use stderr
     if (!err)
-      err = std::make_shared<StreamFile>(stderr, false);
+      err = std::make_shared<LockableStreamFile>(stderr, false);
   }
 }
 
diff --git a/lldb/source/Core/IOHandler.cpp b/lldb/source/Core/IOHandler.cpp
index ca06b52b874db..98d14758f1987 100644
--- a/lldb/source/Core/IOHandler.cpp
+++ b/lldb/source/Core/IOHandler.cpp
@@ -54,17 +54,17 @@ using llvm::StringRef;
 
 IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type)
     : IOHandler(debugger, type,
-                FileSP(),       // Adopt STDIN from top input reader
-                StreamFileSP(), // Adopt STDOUT from top input reader
-                StreamFileSP(), // Adopt STDERR from top input reader
-                0               // Flags
+                FileSP(),               // Adopt STDIN from top input reader
+                LockableStreamFileSP(), // Adopt STDOUT from top input reader
+                LockableStreamFileSP(), // Adopt STDERR from top input reader
+                0                       // Flags
 
       ) {}
 
 IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type,
                      const lldb::FileSP &input_sp,
-                     const lldb::StreamFileSP &output_sp,
-                     const lldb::StreamFileSP &error_sp, uint32_t flags)
+                     const lldb::LockableStreamFileSP &output_sp,
+                     const lldb::LockableStreamFileSP &error_sp, uint32_t flags)
     : m_debugger(debugger), m_input_sp(input_sp), m_output_sp(output_sp),
       m_error_sp(error_sp), m_popped(false), m_flags(flags), m_type(type),
       m_user_data(nullptr), m_done(false), m_active(false) {
@@ -81,30 +81,18 @@ int IOHandler::GetInputFD() {
 }
 
 int IOHandler::GetOutputFD() {
-  return (m_output_sp ? m_output_sp->GetFile().GetDescriptor() : -1);
+  return (m_output_sp ? m_output_sp->GetUnlockedFile().GetDescriptor() : -1);
 }
 
 int IOHandler::GetErrorFD() {
-  return (m_error_sp ? m_error_sp->GetFile().GetDescriptor() : -1);
-}
-
-FILE *IOHandler::GetInputFILE() {
-  return (m_input_sp ? m_input_sp->GetStream() : nullptr);
-}
-
-FILE *IOHandler::GetOutputFILE() {
-  return (m_output_sp ? m_output_sp->GetFile().GetStream() : nullptr);
-}
-
-FILE *IOHandler::GetErrorFILE() {
-  return (m_error_sp ? m_error_sp->GetFile().GetStream() : nullptr);
+  return (m_error_sp ? m_error_sp->GetUnlockedFile().GetDescriptor() : -1);
 }
 
 FileSP IOHandler::GetInputFileSP() { return m_input_sp; }
 
-StreamFileSP IOHandler::GetOutputStreamFileSP() { return m_output_sp; }
+LockableStreamFileSP IOHandler::GetOutputStreamFileSP() { return m_output_sp; }
 
-StreamFileSP IOHandler::GetErrorStreamFileSP() { return m_error_sp; }
+LockableStreamFileSP IOHandler::GetErrorStreamFileSP() { return m_error_sp; }
 
 bool IOHandler::GetIsInteractive() {
   return GetInputFileSP() ? GetInputFileSP()->GetIsInteractive() : false;
@@ -119,10 +107,9 @@ void IOHandler::SetPopped(bool b) { m_popped.SetValue(b, eBroadcastOnChange); }
 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();
+  lldb::LockableStreamFileSP stream_sp = is_stdout ? m_output_sp : m_error_sp;
+  LockedStreamFile locked_Stream = stream_sp->Lock();
+  locked_Stream.Write(s, len);
 }
 
 bool IOHandlerStack::PrintAsync(const char *s, size_t len, bool is_stdout) {
@@ -228,19 +215,20 @@ IOHandlerEditline::IOHandlerEditline(
     llvm::StringRef prompt, llvm::StringRef continuation_prompt,
     bool multi_line, bool color, uint32_t line_number_start,
     IOHandlerDelegate &delegate)
-    : IOHandlerEditline(debugger, type,
-                        FileSP(),       // Inherit input from top input reader
-                        StreamFileSP(), // Inherit output from top input reader
-                        StreamFileSP(), // Inherit error from top input reader
-                        0,              // Flags
-                        editline_name,  // Used for saving history files
-                        prompt, continuation_prompt, multi_line, color,
-                        line_number_start, delegate) {}
+    : IOHandlerEditline(
+          debugger, type,
+          FileSP(),               // Inherit input from top input reader
+          LockableStreamFileSP(), // Inherit output from top input reader
+          LockableStreamFileSP(), // Inherit error from top input reader
+          0,                      // Flags
+          editline_name,          // Used for saving history files
+          prompt, continuation_prompt, multi_line, color, line_number_start,
+          delegate) {}
 
 IOHandlerEditline::IOHandlerEditline(
     Debugger &debugger, IOHandler::Type type, const lldb::FileSP &input_sp,
-    const lldb::StreamFileSP &output_sp, const lldb::StreamFileSP &error_sp,
-    uint32_t flags,
+    const lldb::LockableStreamFileSP &output_sp,
+    const lldb::LockableStreamFileSP &error_sp, uint32_t flags,
     const char *editline_name, // Used for saving history files
     llvm::StringRef prompt, llvm::StringRef continuation_prompt,
     bool multi_line, bool color, uint32_t line_number_start,
@@ -256,15 +244,12 @@ IOHandlerEditline::IOHandlerEditline(
   SetPrompt(prompt);
 
 #if LLDB_ENABLE_LIBEDIT
-  bool use_editline = false;
-
-  use_editline = GetInputFILE() && GetOutputFILE() && GetErrorFILE() &&
-                 m_input_sp && m_input_sp->GetIsRealTerminal();
-
+  const bool use_editline = m_input_sp && m_output_sp && m_error_sp &&
+                            m_input_sp->GetIsRealTerminal();
   if (use_editline) {
-    m_editline_up = std::make_unique<Editline>(editline_name, GetInputFILE(),
-                                               GetOutputFILE(), GetErrorFILE(),
-                                               m_color, GetOutputMutex());
+    m_editline_up = std::make_unique<Editline>(
+        editline_name, m_input_sp ? m_input_sp->GetStream() : nullptr,
+        m_output_sp, m_error_sp, m_color);
     m_editline_up->SetIsInputCompleteCallback(
         [this](Editline *editline, StringList &lines) {
           return this->IsInputCompleteCallback(editline, lines);
@@ -366,8 +351,8 @@ bool IOHandlerEditline::GetLine(std::string &line, bool &interrupted) {
 
     if (prompt && prompt[0]) {
       if (m_output_sp) {
-        m_output_sp->Printf("%s", prompt);
-        m_output_sp->Flush();
+        LockedStreamFile locked_stream = m_output_sp->Lock();
+        locked_stream.Printf("%s", prompt);
       }
     }
   }
@@ -380,7 +365,7 @@ bool IOHandlerEditline::GetLine(std::string &line, bool &interrupted) {
     return false;
   }
 
-  FILE *in = GetInputFILE();
+  FILE *in = m_input_sp ? m_input_sp->GetStream() : nullptr;
   char buffer[256];
 
   if (!got_line && !in && m_input_sp) {
@@ -545,9 +530,10 @@ bool IOHandlerEditline::GetLines(StringList &lines, bool &interrupted) {
       std::string line;
       if (m_base_line_number > 0 && GetIsInteractive()) {
         if (m_output_sp) {
-          m_output_sp->Printf("%u%s",
-                              m_base_line_number + (uint32_t)lines.GetSize(),
-                              GetPrompt() == nullptr ? " " : "");
+          LockedStreamFile locked_stream = m_output_sp->Lock();
+          locked_stream.Printf("%u%s",
+                               m_base_line_number + (uint32_t)lines.GetSize(),
+                               GetPrompt() == nullptr ? " " : "");
         }
       }
 
@@ -630,9 +616,8 @@ void IOHandlerEditline::GotEOF() {
 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);
+    lldb::LockableStreamFileSP stream_sp = is_stdout ? m_output_sp : m_error_sp;
+    m_editline_up->PrintAsync(stream_sp, s, len);
   } else
 #endif
   {
diff --git a/lldb/source/Core/IOHandlerCursesGUI.cpp b/lldb/source/Core/IOHandlerCursesGUI.cpp
index 456ce7d16e102..b51e946bb9afe 100644
--- a/lldb/source/Core/IOHandlerCursesGUI.cpp
+++ b/lldb/source/Core/IOHandlerCursesGUI.cpp
@@ -7574,7 +7574,9 @@ IOHandlerCursesGUI::IOHandlerCursesGUI(Debugger &debugger)
 void IOHandlerCursesGUI::Activate() {
   IOHandler::Activate();
   if (!m_app_ap) {
-    m_app_ap = std::make_unique<Application>(GetInputFILE(), GetOutputFILE());
+    m_app_ap = std::make_unique<Application>(
+        m_input_sp ? m_input_sp->GetStream() : nullptr,
+        m_output_sp ? m_input_sp->GetStream() : nullptr);
 
     // This is both a window and a menu delegate
     std::shared_ptr<ApplicationDelegate> app_delegate_sp(
diff --git a/lldb/source/Expression/REPL.cpp b/lldb/source/Expression/REPL.cpp
index 4b53537e50e62..e5377d3114af3 100644
--- a/lldb/source/Expression/REPL.cpp
+++ b/lldb/source/Expression/REPL.cpp
@@ -103,8 +103,8 @@ void REPL::IOHandlerActivated(IOHandler &io_handler, bool interactive) {
   lldb::ProcessSP process_sp = m_target.GetProcessSP();
   if (process_sp && process_sp->IsAlive())
     return;
-  lldb::StreamFileSP error_sp(io_handler.GetErrorStreamFileSP());
-  error_sp->Printf("REPL requires a running target process.\n");
+  LockedStreamFile locked_stream = io_handler.GetErrorStreamFileSP()->Lock();
+  locked_stream.Printf("REPL requires a running target process.\n");
   io_handler.SetIsDone(true);
 }
 
@@ -219,8 +219,10 @@ static bool ReadCode(const std::string &path, std::string &code,
 }
 
 void REPL::IOHandlerInputComplete(IOHandler &io_handler, std::string &code) {
-  lldb::StreamFileSP output_sp(io_handler.GetOutputStreamFileSP());
-  lldb::StreamFileSP error_sp(io_handler.GetErrorStreamFileSP());
+  lldb::StreamFileSP output_sp = std::make_shared<StreamFile>(
+      io_handler.GetOutputStreamFileSP()->GetUnlockedFileSP());
+  lldb::StreamFileSP error_sp = std::make_shared<StreamFile>(
+      io_handler.GetErrorStreamFileSP()->GetUnlockedFileSP());
   bool extra_line = false;
   bool did_quit = false;
 
diff --git a/lldb/source/Host/common/Editline.cpp b/lldb/source/Host/common/Editline.cpp
index 73da1d8481618..49480b4098723 100644
--- a/lldb/source/Host/common/Editline.cpp
+++ b/lldb/source/Host/common/Editline.cpp
@@ -14,6 +14,7 @@
 #include "lldb/Host/Editline.h"
 #include "lldb/Host/FileSystem.h"
 #include "lldb/Host/Host.h"
+#include "lldb/Host/StreamFile.h"
 #include "lldb/Utility/AnsiTerminal.h"
 #include "lldb/Utility/CompletionRequest.h"
 #include "lldb/Utility/FileSpec.h"
@@ -23,6 +24,7 @@
 #include "lldb/Utility/StreamString.h"
 #include "lldb/Utility/StringList.h"
 #include "lldb/Utility/Timeout.h"
+#include "lldb/lldb-forward.h"
 #include "llvm/Support/ConvertUTF.h"
 
 #include "llvm/Support/FileSystem.h"
@@ -389,11 +391,13 @@ void Editline::MoveCursor(CursorLocation from, CursorLocation to) {
       (int)((info->cursor - info->buffer) + GetPromptWidth());
   int editline_cursor_row = editline_cursor_position / m_terminal_width;
 
+  LockedStreamFile locked_stream = m_output_stream_sp->Lock();
+
   // Determine relative starting and ending lines
   int fromLine = GetLineIndexForLocation(from, editline_cursor_row);
   int toLine = GetLineIndexForLocation(to, editline_cursor_row);
   if (toLine != fromLine) {
-    fprintf(m_output_file,
+    fprintf(locked_stream.GetFile().GetStream(),
             (toLine > fromLine) ? ANSI_DOWN_N_ROWS : ANSI_UP_N_ROWS,
             std::abs(toLine - fromLine));
   }
@@ -409,21 +413,23 @@ void Editline::MoveCursor(CursorLocation from, CursorLocation to) {
          80) +
         1;
   }
-  fprintf(m_output_file, ANSI_SET_COLUMN_N, toColumn);
+  fprintf(locked_stream.GetFile().GetStream(), ANSI_SET_COLUMN_N, toColumn);
 }
 
 void Editline::DisplayInput(int firstIndex) {
-  fprintf(m_output_file, ANSI_SET_COLUMN_N ANSI_CLEAR_BELOW, 1);
+  LockedStreamFile locked_stream = m_output_stream_sp->Lock();
+  fprintf(locked_stream.GetFile().GetStream(),
+          ANSI_SET_COLUMN_N ANSI_CLEAR_BELOW, 1);
   int line_count = (int)m_input_lines.size();
   for (int index = firstIndex; index < line_count; index++) {
-    fprintf(m_output_file,
+    fprintf(locked_stream.GetFile().GetStream(),
             "%s"
             "%s"
             "%s" EditLineStringFormatSpec " ",
             m_prompt_ansi_prefix.c_str(), PromptForIndex(index).c_str(),
             m_prompt_ansi_suffix.c_str(), m_input_lines[index].c_str());
     if (index < line_count - 1)
-      fprintf(m_output_file, "\n");
+      fprintf(locked_stream.GetFile().GetStream(), "\n");
   }
 }
 
@@ -535,8 +541,9 @@ int Editline::GetCharacter(EditLineGetCharType *c) {
   // Paint a ANSI formatted version of the desired prompt over the version
   // libedit draws. (will only be requested if colors are supported)
   if (m_needs_prompt_repaint) {
+    LockedStreamFile locked_stream = m_output_stream_sp->Lock();
     MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
-    fprintf(m_output_file,
+    fprintf(locked_stream.GetFile().GetStream(),
             "%s"
             "%s"
             "%s",
@@ -574,10 +581,10 @@ int Editline::GetCharacter(EditLineGetCharType *c) {
     // indefinitely. This gives a chance for someone to interrupt us. After
     // Read returns, immediately lock the mutex again and check if we were
     // interrupted.
-    m_output_mutex.unlock();
+    m_output_stream_sp->GetMutex().unlock();
     int read_count =
         m_input_connection.Read(&ch, 1, std::nullopt, status, nullptr);
-    m_output_mutex.lock();
+    m_output_stream_sp->GetMutex().lock();
     if (m_editor_status == EditorStatus::Interrupted) {
       while (read_count > 0 && status == lldb::eConnectionStatusSuccess)
         read_count =
@@ -700,12 +707,14 @@ unsigned char Editline::EndOrAddLineCommand(int ch) {
     }
   }
   MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockEnd);
-  fprintf(m_output_file, "\n");
+  LockedStreamFile locked_stream = m_output_stream_sp->Lock();
+  fprintf(locked_stream.GetFile().GetStream(), "\n");
   m_editor_status = EditorStatus::Complete;
   return CC_NEWLINE;
 }
 
 unsigned char Editline::DeleteNextCharCommand(int ch) {
+  LockedStreamFile locked_stream = m_output_stream_sp->Lock();
   LineInfoW *info = const_cast<LineInfoW *>(el_wline(m_editline));
 
   // Just delete the next character normally if possible
@@ -719,7 +728,7 @@ unsigned char Editline::DeleteNextCharCommand(int ch) {
   // line is empty, in which case it is treated as EOF
   if (m_current_line_index == m_input_lines.size() - 1) {
     if (ch == 4 && info->buffer == info->lastchar) {
-      fprintf(m_output_file, "^D\n");
+      fprintf(locked_stream.GetFile().GetStream(), "^D\n");
       m_editor_status = EditorStatus::EndOfInput;
       return CC_EOF;
     }
@@ -767,7 +776,8 @@ unsigned char Editline::DeletePreviousCharCommand(int ch) {
       priorLine + m_input_lines[m_current_line_index];
 
   // Repaint from the new line down
-  fprintf(m_output_file, ANSI_UP_N_ROWS ANSI_SET_COLUMN_N,
+  LockedStreamFile locked_stream = m_output_stream_sp->Lock();
+  fprintf(locked_stream.GetFile().GetStream(), ANSI_UP_N_ROWS ANSI_SET_COLUMN_N,
           CountRowsForLine(priorLine), 1);
   DisplayInput(m_current_line_index);
 
@@ -785,17 +795,19 @@ unsigned char Editline::PreviousLineCommand(int ch) {
     return RecallHistory(HistoryOperation::Older);
   }
 
+  LockedStreamFile locked_stream = m_output_stream_sp->Lock();
+
   // Start from a known location
   MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
 
   // Treat moving up from a blank last line as a deletion of that line
   if (m_current_line_index == m_input_lines.size() - 1 && IsOnlySpaces()) {
     m_input_lines.erase(m_input_lines.begin() + m_current_line_index);
-    fprintf(m_output_file, ANSI_CLEAR_BELOW);
+    fprintf(locked_stream.GetFile().GetStream(), ANSI_CLEAR_BELOW);
   }
 
   SetCurrentLine(m_current_line_index - 1);
-  fprintf(m_output_file, ANSI_UP_N_ROWS ANSI_SET_COLUMN_N,
+  fprintf(locked_stream.GetFile().GetStream(), ANSI_UP_N_ROWS ANSI_SET_COLUMN_N,
           CountRowsForLine(m_input_lines[m_current_line_index]), 1);
   return CC_NEWLINE;
 }
@@ -829,9 +841,11 @@ unsigned char Editline::NextLineCommand(int ch) {
   const LineInfoW *info = el_wline(m_editline);
   int cursor_position = (int)((info->cursor - info->buffer) + GetPromptWidth());
   int cursor_row = cursor_position / m_terminal_width;
+
+  LockedStreamFile locked_stream = m_output_stream_sp->Lock();
   for (int line_count = 0; line_count < m_current_line_rows - cursor_row;
        line_count++) {
-    fprintf(m_output_file, "\n");
+    fprintf(locked_stream.GetFile().GetStream(), "\n");
   }
   return CC_NEWLINE;
 }
@@ -1031,7 +1045,9 @@ void Editline::DisplayCompletions(
     Editline &editline, llvm::ArrayRef<CompletionResult::Completion> results) {
   assert(!results.empty());
 
-  fprintf(editline.m_output_file,
+  LockedStreamFile locked_stream = editline.m_output_stream_sp->Lock();
+
+  fprintf(locked_stream.GetFile().GetStream(),
           "\n" ANSI_CLEAR_BELOW "Available completions:\n");
 
   /// Account for the current line, the line showing "Available completions"
@@ -1049,15 +1065,15 @@ void Editline::DisplayCompletions(
 
   size_t cur_pos = 0;
   while (cur_pos < results.size()) {
-    cur_pos +=
-        PrintCompletion(editline.m_output_file, results.slice(cur_pos), max_len,
-                        editline.GetTerminalWidth(),
-                        all ? std::nullopt : std::optional<size_t>(page_size));
+    cur_pos += PrintCompletion(
+        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(editline.m_output_file, "More (Y/n/a): ");
+    fprintf(locked_stream.GetFile().GetStream(), "More (Y/n/a): ");
     // 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,
@@ -1069,11 +1085,11 @@ void Editline::DisplayCompletions(
     // Check for a ^C or other interruption.
     if (editline.m_editor_status == EditorStatus::Interrupted) {
       editline.m_editor_status = EditorStatus::Editing;
-      fprintf(editline.m_output_file, "^C\n");
+      fprintf(locked_stream.GetFile().GetStream(), "^C\n");
       break;
     }
 
-    fprintf(editline.m_output_file, "\n");
+    fprintf(locked_stream.GetFile().GetStream(), "\n");
     if (got_char == -1 || reply == 'n')
       break;
     if (reply == 'a')
@@ -1182,17 +1198,18 @@ unsigned char Editline::TypedCharacter(int ch) {
                        line_info->lastchar - line_info->buffer);
 
   if (std::optional<std::string> to_add = m_suggestion_callback(line)) {
+    LockedStreamFile locked_stream = m_output_stream_sp->Lock();
     std::string to_add_color =
         m_suggestion_ansi_prefix + to_add.value() + m_suggestion_ansi_suffix;
-    fputs(typed.c_str(), m_output_file);
-    fputs(to_add_color.c_str(), m_output_file);
+    fputs(typed.c_str(), locked_stream.GetFile().GetStream());
+    fputs(to_add_color.c_str(), locked_stream.GetFile().GetStream());
     size_t new_autosuggestion_size = line.size() + to_add->length();
     // Print spaces to hide any remains of a previous longer autosuggestion.
     if (new_autosuggestion_size < m_previous_autosuggestion_size) {
       size_t spaces_to_print =
           m_previous_autosuggestion_size - new_autosuggestion_size;
       std::string spaces = std::string(spaces_to_print, ' ');
-      fputs(spaces.c_str(), m_output_file);
+      fputs(spaces.c_str(), locked_stream.GetFile().GetStream());
     }
     m_previous_autosuggestion_size = new_autosuggestion_size;
 
@@ -1201,7 +1218,7 @@ unsigned char Editline::TypedCharacter(int ch) {
     int editline_cursor_row = editline_cursor_position / m_terminal_width;
     int toColumn =
         editline_cursor_position - (editline_cursor_row * m_terminal_width);
-    fprintf(m_output_file, ANSI_SET_COLUMN_N, toColumn);
+    fprintf(locked_stream.GetFile().GetStream(), ANSI_SET_COLUMN_N, toColumn);
     return CC_REFRESH;
   }
 
@@ -1236,13 +1253,17 @@ void Editline::ConfigureEditor(bool multiline) {
     el_end(m_editline);
   }
 
-  m_editline =
-      el_init(m_editor_name.c_str(), m_input_file, m_output_file, m_error_file);
+  LockedStreamFile locked_output_stream = m_output_stream_sp->Lock();
+  LockedStreamFile locked_error_stream = m_output_stream_sp->Lock();
+  m_editline = el_init(m_editor_name.c_str(), m_input_file,
+                       locked_output_stream.GetFile().GetStream(),
+                       locked_error_stream.GetFile().GetStream());
   ApplyTerminalSizeChange();
 
   if (m_history_sp && m_history_sp->IsValid()) {
     if (!m_history_sp->Load()) {
-        fputs("Could not load history file\n.", m_output_file);
+      fputs("Could not load history file\n.",
+            locked_output_stream.GetFile().GetStream());
     }
     el_wset(m_editline, EL_HIST, history, m_history_sp->GetHistoryPtr());
   }
@@ -1473,12 +1494,12 @@ Editline *Editline::InstanceFor(EditLine *editline) {
 }
 
 Editline::Editline(const char *editline_name, FILE *input_file,
-                   FILE *output_file, FILE *error_file, bool color,
-                   std::recursive_mutex &output_mutex)
+                   lldb::LockableStreamFileSP output_stream_sp,
+                   lldb::LockableStreamFileSP error_stream_sp, bool color)
     : m_editor_status(EditorStatus::Complete), m_input_file(input_file),
-      m_output_file(output_file), m_error_file(error_file),
-      m_input_connection(fileno(input_file), false), m_color(color),
-      m_output_mutex(output_mutex) {
+      m_output_stream_sp(output_stream_sp), m_error_stream_sp(error_stream_sp),
+      m_input_connection(fileno(input_file), false), m_color(color) {
+  assert(output_stream_sp && error_stream_sp);
   // Get a shared history instance
   m_editor_name = (editline_name == nullptr) ? "lldb-tmp" : editline_name;
   m_history_sp = EditlineHistory::GetHistory(m_editor_name);
@@ -1552,9 +1573,9 @@ uint32_t Editline::GetCurrentLine() { return m_current_line_index; }
 
 bool Editline::Interrupt() {
   bool result = true;
-  std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
+  LockedStreamFile locked_stream = m_output_stream_sp->Lock();
   if (m_editor_status == EditorStatus::Editing) {
-    fprintf(m_output_file, "^C\n");
+    fprintf(locked_stream.GetFile().GetStream(), "^C\n");
     result = m_input_connection.InterruptRead();
   }
   m_editor_status = EditorStatus::Interrupted;
@@ -1563,10 +1584,10 @@ bool Editline::Interrupt() {
 
 bool Editline::Cancel() {
   bool result = true;
-  std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
+  LockedStreamFile locked_stream = m_output_stream_sp->Lock();
   if (m_editor_status == EditorStatus::Editing) {
     MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
-    fprintf(m_output_file, ANSI_CLEAR_BELOW);
+    fprintf(locked_stream.GetFile().GetStream(), ANSI_CLEAR_BELOW);
     result = m_input_connection.InterruptRead();
   }
   m_editor_status = EditorStatus::Interrupted;
@@ -1578,7 +1599,7 @@ bool Editline::GetLine(std::string &line, bool &interrupted) {
   m_input_lines = std::vector<EditLineStringType>();
   m_input_lines.insert(m_input_lines.begin(), EditLineConstString(""));
 
-  std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
+  LockedStreamFile locked_stream = m_output_stream_sp->Lock();
 
   lldbassert(m_editor_status != EditorStatus::Editing);
   if (m_editor_status == EditorStatus::Interrupted) {
@@ -1598,7 +1619,7 @@ bool Editline::GetLine(std::string &line, bool &interrupted) {
   interrupted = m_editor_status == EditorStatus::Interrupted;
   if (!interrupted) {
     if (input == nullptr) {
-      fprintf(m_output_file, "\n");
+      fprintf(locked_stream.GetFile().GetStream(), "\n");
       m_editor_status = EditorStatus::EndOfInput;
     } else {
       m_history_sp->Enter(input);
@@ -1623,7 +1644,8 @@ bool Editline::GetLines(int first_line_number, StringList &lines,
   m_input_lines = std::vector<EditLineStringType>();
   m_input_lines.insert(m_input_lines.begin(), EditLineConstString(""));
 
-  std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
+  LockedStreamFile locked_stream = m_output_stream_sp->Lock();
+
   // Begin the line editing loop
   DisplayInput();
   SetCurrentLine(0);
@@ -1652,15 +1674,15 @@ bool Editline::GetLines(int first_line_number, StringList &lines,
   return m_editor_status != EditorStatus::EndOfInput;
 }
 
-void Editline::PrintAsync(Stream *stream, const char *s, size_t len) {
-  std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
+void Editline::PrintAsync(lldb::LockableStreamFileSP stream_sp, const char *s,
+                          size_t len) {
+  LockedStreamFile locked_stream = m_output_stream_sp->Lock();
   if (m_editor_status == EditorStatus::Editing) {
     SaveEditedLine();
     MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
-    fprintf(m_output_file, ANSI_CLEAR_BELOW);
+    fprintf(locked_stream.GetFile().GetStream(), ANSI_CLEAR_BELOW);
   }
-  stream->Write(s, len);
-  stream->Flush();
+  locked_stream.Write(s, len);
   if (m_editor_status == EditorStatus::Editing) {
     DisplayInput();
     MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index acdec84a1689b..af733ac8582ea 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -57,6 +57,7 @@
 #include "lldb/Utility/Timer.h"
 
 #include "lldb/Host/Config.h"
+#include "lldb/lldb-forward.h"
 #if LLDB_ENABLE_LIBEDIT
 #include "lldb/Host/Editline.h"
 #endif
@@ -2843,7 +2844,7 @@ void CommandInterpreter::HandleCommandsFromFile(
 
   // Used for inheriting the right settings when "command source" might
   // have nested "command source" commands
-  lldb::StreamFileSP empty_stream_sp;
+  lldb::LockableStreamFileSP empty_stream_sp;
   m_command_source_flags.push_back(flags);
   IOHandlerSP io_handler_sp(new IOHandlerEditline(
       debugger, IOHandler::Type::CommandInterpreter, input_file_sp,
@@ -3100,25 +3101,26 @@ void CommandInterpreter::PrintCommandOutput(IOHandler &io_handler,
                                             llvm::StringRef str,
                                             bool is_stdout) {
 
-  lldb::StreamFileSP stream = is_stdout ? io_handler.GetOutputStreamFileSP()
-                                        : io_handler.GetErrorStreamFileSP();
+  lldb::LockableStreamFileSP stream = is_stdout
+                                          ? io_handler.GetOutputStreamFileSP()
+                                          : io_handler.GetErrorStreamFileSP();
   // Split the output into lines and poll for interrupt requests
   bool had_output = !str.empty();
   while (!str.empty()) {
     llvm::StringRef line;
     std::tie(line, str) = str.split('\n');
     {
-      std::lock_guard<std::recursive_mutex> guard(io_handler.GetOutputMutex());
-      stream->Write(line.data(), line.size());
-      stream->Write("\n", 1);
+      LockedStreamFile stream_file = stream->Lock();
+      stream_file.Write(line.data(), line.size());
+      stream_file.Write("\n", 1);
     }
   }
 
-  std::lock_guard<std::recursive_mutex> guard(io_handler.GetOutputMutex());
+  LockedStreamFile stream_file = stream->Lock();
   if (had_output &&
       INTERRUPT_REQUESTED(GetDebugger(), "Interrupted dumping command output"))
-    stream->Printf("\n... Interrupted.\n");
-  stream->Flush();
+    stream_file.Printf("\n... Interrupted.\n");
+  stream_file.Flush();
 }
 
 bool CommandInterpreter::EchoCommandNonInteractive(
@@ -3160,9 +3162,9 @@ void CommandInterpreter::IOHandlerInputComplete(IOHandler &io_handler,
     // 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())) {
-      std::lock_guard<std::recursive_mutex> guard(io_handler.GetOutputMutex());
-      io_handler.GetOutputStreamFileSP()->Printf(
-          "%s%s\n", io_handler.GetPrompt(), line.c_str());
+      LockedStreamFile locked_stream =
+          io_handler.GetOutputStreamFileSP()->Lock();
+      locked_stream.Printf("%s%s\n", io_handler.GetPrompt(), line.c_str());
     }
   }
 
diff --git a/lldb/source/Interpreter/ScriptInterpreter.cpp b/lldb/source/Interpreter/ScriptInterpreter.cpp
index 8d10e5de01225..10b20b6a74119 100644
--- a/lldb/source/Interpreter/ScriptInterpreter.cpp
+++ b/lldb/source/Interpreter/ScriptInterpreter.cpp
@@ -206,7 +206,7 @@ ScriptInterpreterIORedirect::Create(bool enable_io, Debugger &debugger,
 ScriptInterpreterIORedirect::ScriptInterpreterIORedirect(
     std::unique_ptr<File> input, std::unique_ptr<File> output)
     : m_input_file_sp(std::move(input)),
-      m_output_file_sp(std::make_shared<StreamFile>(std::move(output))),
+      m_output_file_sp(std::make_shared<LockableStreamFile>(std::move(output))),
       m_error_file_sp(m_output_file_sp),
       m_communication("lldb.ScriptInterpreterIORedirect.comm"),
       m_disconnect(false) {}
@@ -240,13 +240,16 @@ ScriptInterpreterIORedirect::ScriptInterpreterIORedirect(
       m_disconnect = true;
 
       FILE *outfile_handle = fdopen(pipe.ReleaseWriteFileDescriptor(), "w");
-      m_output_file_sp = std::make_shared<StreamFile>(outfile_handle, true);
+      m_output_file_sp = std::make_shared<LockableStreamFile>(
+          std::make_shared<StreamFile>(outfile_handle, true));
       m_error_file_sp = m_output_file_sp;
       if (outfile_handle)
         ::setbuf(outfile_handle, nullptr);
 
-      result->SetImmediateOutputFile(debugger.GetOutputStreamSP()->GetFileSP());
-      result->SetImmediateErrorFile(debugger.GetErrorStreamSP()->GetFileSP());
+      result->SetImmediateOutputFile(
+          debugger.GetOutputStreamSP()->GetUnlockedFileSP());
+      result->SetImmediateErrorFile(
+          debugger.GetErrorStreamSP()->GetUnlockedFileSP());
     }
   }
 
@@ -257,9 +260,9 @@ ScriptInterpreterIORedirect::ScriptInterpreterIORedirect(
 
 void ScriptInterpreterIORedirect::Flush() {
   if (m_output_file_sp)
-    m_output_file_sp->Flush();
+    m_output_file_sp->Lock().Flush();
   if (m_error_file_sp)
-    m_error_file_sp->Flush();
+    m_error_file_sp->Lock().Flush();
 }
 
 ScriptInterpreterIORedirect::~ScriptInterpreterIORedirect() {
@@ -273,7 +276,7 @@ ScriptInterpreterIORedirect::~ScriptInterpreterIORedirect() {
   // Close the write end of the pipe since we are done with our one line
   // script. This should cause the read thread that output_comm is using to
   // exit.
-  m_output_file_sp->GetFile().Close();
+  m_output_file_sp->GetUnlockedFile().Close();
   // The close above should cause this thread to exit when it gets to the end
   // of file, so let it get all its data.
   m_communication.JoinReadThread();
diff --git a/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp b/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp
index cff44b588e26e..2ee7d1d9006c8 100644
--- a/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp
+++ b/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp
@@ -15,6 +15,7 @@
 #include "lldb/Core/PluginManager.h"
 #include "lldb/Core/Progress.h"
 #include "lldb/Core/Section.h"
+#include "lldb/Host/StreamFile.h"
 #include "lldb/Interpreter/OptionValueProperties.h"
 #include "lldb/Symbol/ObjectFile.h"
 #include "lldb/Target/OperatingSystem.h"
@@ -29,6 +30,7 @@
 #include "lldb/Utility/LLDBLog.h"
 #include "lldb/Utility/Log.h"
 #include "lldb/Utility/State.h"
+#include "lldb/lldb-forward.h"
 
 #include "DynamicLoaderDarwinKernel.h"
 
@@ -1193,7 +1195,7 @@ bool DynamicLoaderDarwinKernel::ReadKextSummaryHeader() {
           m_kext_summary_header.version = data.GetU32(&offset);
           if (m_kext_summary_header.version > 128) {
             lldb::StreamSP s =
-                m_process->GetTarget().GetDebugger().GetOutputStreamSP();
+                m_process->GetTarget().GetDebugger().GetAsyncOutputStream();
             s->Printf("WARNING: Unable to read kext summary header, got "
                       "improbable version number %u\n",
                       m_kext_summary_header.version);
@@ -1208,7 +1210,7 @@ bool DynamicLoaderDarwinKernel::ReadKextSummaryHeader() {
               // If we get an improbably large entry_size, we're probably
               // getting bad memory.
               lldb::StreamSP s =
-                  m_process->GetTarget().GetDebugger().GetOutputStreamSP();
+                  m_process->GetTarget().GetDebugger().GetAsyncOutputStream();
               s->Printf("WARNING: Unable to read kext summary header, got "
                         "improbable entry_size %u\n",
                         m_kext_summary_header.entry_size);
@@ -1226,7 +1228,7 @@ bool DynamicLoaderDarwinKernel::ReadKextSummaryHeader() {
             // If we get an improbably large number of kexts, we're probably
             // getting bad memory.
             lldb::StreamSP s =
-                m_process->GetTarget().GetDebugger().GetOutputStreamSP();
+                m_process->GetTarget().GetDebugger().GetAsyncOutputStream();
             s->Printf("WARNING: Unable to read kext summary header, got "
                       "improbable number of kexts %u\n",
                       m_kext_summary_header.entry_count);
@@ -1330,18 +1332,25 @@ bool DynamicLoaderDarwinKernel::ParseKextSummaries(
       number_of_old_kexts_being_removed == 0)
     return true;
 
-  lldb::StreamSP s = m_process->GetTarget().GetDebugger().GetOutputStreamSP();
+  lldb::LockableStreamFileSP output_sp =
+      m_process->GetTarget().GetDebugger().GetOutputStreamSP();
   if (load_kexts) {
+    LockedStreamFile locked_stream =
+        m_process->GetTarget().GetDebugger().GetOutputStreamSP()->Lock();
     if (number_of_new_kexts_being_added > 0 &&
         number_of_old_kexts_being_removed > 0) {
-      s->Printf("Loading %d kext modules and unloading %d kext modules ",
-                number_of_new_kexts_being_added,
-                number_of_old_kexts_being_removed);
+      LockedStreamFile locked_stream = output_sp->Lock();
+      locked_stream.Printf(
+          "Loading %d kext modules and unloading %d kext modules ",
+          number_of_new_kexts_being_added, number_of_old_kexts_being_removed);
     } else if (number_of_new_kexts_being_added > 0) {
-      s->Printf("Loading %d kext modules ", number_of_new_kexts_being_added);
+      LockedStreamFile locked_stream = output_sp->Lock();
+      locked_stream.Printf("Loading %d kext modules ",
+                           number_of_new_kexts_being_added);
     } else if (number_of_old_kexts_being_removed > 0) {
-      s->Printf("Unloading %d kext modules ",
-                number_of_old_kexts_being_removed);
+      LockedStreamFile locked_stream = output_sp->Lock();
+      locked_stream.Printf("Unloading %d kext modules ",
+                           number_of_old_kexts_being_removed);
     }
   }
 
@@ -1396,6 +1405,7 @@ bool DynamicLoaderDarwinKernel::ParseKextSummaries(
   if (number_of_old_kexts_being_removed > 0) {
     ModuleList loaded_module_list;
     const uint32_t num_of_old_kexts = m_known_kexts.size();
+    LockedStreamFile locked_stream = output_sp->Lock();
     for (uint32_t old_kext = 0; old_kext < num_of_old_kexts; old_kext++) {
       ModuleList unloaded_module_list;
       if (to_be_removed[old_kext]) {
@@ -1405,7 +1415,7 @@ bool DynamicLoaderDarwinKernel::ParseKextSummaries(
           if (image_info.GetModule()) {
             unloaded_module_list.AppendIfNeeded(image_info.GetModule());
           }
-          s->Printf(".");
+          locked_stream.Printf(".");
           image_info.Clear();
           // should pull it out of the KextImageInfos vector but that would
           // mutate the list and invalidate the to_be_removed bool vector;
@@ -1417,11 +1427,12 @@ bool DynamicLoaderDarwinKernel::ParseKextSummaries(
   }
 
   if (load_kexts) {
-    s->Printf(" done.\n");
+    LockedStreamFile locked_stream = output_sp->Lock();
+    locked_stream.Printf(" done.\n");
     if (kexts_failed_to_load.size() > 0 && number_of_new_kexts_being_added > 0) {
-      s->Printf("Failed to load %d of %d kexts:\n",
-                (int)kexts_failed_to_load.size(),
-                number_of_new_kexts_being_added);
+      locked_stream.Printf("Failed to load %d of %d kexts:\n",
+                           (int)kexts_failed_to_load.size(),
+                           number_of_new_kexts_being_added);
       // print a sorted list of <kext-name, uuid> kexts which failed to load
       unsigned longest_name = 0;
       std::sort(kexts_failed_to_load.begin(), kexts_failed_to_load.end());
@@ -1433,7 +1444,8 @@ bool DynamicLoaderDarwinKernel::ParseKextSummaries(
         std::string uuid;
         if (ku.second.IsValid())
           uuid = ku.second.GetAsString();
-        s->Printf(" %-*s %s\n", longest_name, ku.first.c_str(), uuid.c_str());
+        locked_stream.Printf(" %-*s %s\n", longest_name, ku.first.c_str(),
+                             uuid.c_str());
       }
     }
   }
diff --git a/lldb/source/Plugins/InstrumentationRuntime/UBSan/InstrumentationRuntimeUBSan.cpp b/lldb/source/Plugins/InstrumentationRuntime/UBSan/InstrumentationRuntimeUBSan.cpp
index 8c2700cf21de9..c2db3540a797b 100644
--- a/lldb/source/Plugins/InstrumentationRuntime/UBSan/InstrumentationRuntimeUBSan.cpp
+++ b/lldb/source/Plugins/InstrumentationRuntime/UBSan/InstrumentationRuntimeUBSan.cpp
@@ -116,8 +116,6 @@ StructuredData::ObjectSP InstrumentationRuntimeUBSan::RetrieveReportData(
   if (!frame_sp)
     return StructuredData::ObjectSP();
 
-  StreamFileSP Stream = target.GetDebugger().GetOutputStreamSP();
-
   EvaluateExpressionOptions options;
   options.SetUnwindOnError(true);
   options.SetTryAllThreads(true);
diff --git a/lldb/source/Plugins/InstrumentationRuntime/Utility/ReportRetriever.cpp b/lldb/source/Plugins/InstrumentationRuntime/Utility/ReportRetriever.cpp
index 74e0fa7d49f82..964c958aac1e9 100644
--- a/lldb/source/Plugins/InstrumentationRuntime/Utility/ReportRetriever.cpp
+++ b/lldb/source/Plugins/InstrumentationRuntime/Utility/ReportRetriever.cpp
@@ -210,8 +210,8 @@ bool ReportRetriever::NotifyBreakpointHit(ProcessSP process_sp,
         InstrumentationRuntimeStopInfo::CreateStopReasonWithInstrumentationData(
             *thread_sp, description, report));
 
-  if (StreamFileSP stream_sp = StreamFileSP(
-          process_sp->GetTarget().GetDebugger().GetOutputStreamSP()))
+  if (lldb::StreamSP stream_sp =
+          process_sp->GetTarget().GetDebugger().GetAsyncOutputStream())
     stream_sp->Printf("AddressSanitizer report breakpoint hit. Use 'thread "
                       "info -s' to get extended information about the "
                       "report.\n");
diff --git a/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp b/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp
index 7e8eee9f5aa4f..277245ca41a9c 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp
@@ -17,6 +17,7 @@
 #include "lldb/Utility/Stream.h"
 #include "lldb/Utility/StringList.h"
 #include "lldb/Utility/Timer.h"
+#include "lldb/lldb-forward.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/FormatAdapters.h"
 #include <memory>
@@ -76,8 +77,13 @@ class IOHandlerLuaInterpreter : public IOHandlerDelegate,
     }
     if (instructions == nullptr)
       return;
-    if (interactive)
-      *io_handler.GetOutputStreamFileSP() << instructions;
+    if (interactive) {
+      if (lldb::LockableStreamFileSP output_sp =
+              io_handler.GetOutputStreamFileSP()) {
+        LockedStreamFile locked_stream = output_sp->Lock();
+        locked_stream << instructions;
+      }
+    }
   }
 
   bool IOHandlerIsInputComplete(IOHandler &io_handler,
@@ -112,8 +118,11 @@ class IOHandlerLuaInterpreter : public IOHandlerDelegate,
       for (BreakpointOptions &bp_options : *bp_options_vec) {
         Status error = m_script_interpreter.SetBreakpointCommandCallback(
             bp_options, data.c_str(), /*is_callback=*/false);
-        if (error.Fail())
-          *io_handler.GetErrorStreamFileSP() << error.AsCString() << '\n';
+        if (error.Fail()) {
+          LockedStreamFile locked_stream =
+              io_handler.GetErrorStreamFileSP()->Lock();
+          locked_stream << error.AsCString() << '\n';
+        }
       }
       io_handler.SetIsDone(true);
     } break;
@@ -130,8 +139,11 @@ class IOHandlerLuaInterpreter : public IOHandlerDelegate,
         io_handler.SetIsDone(true);
         return;
       }
-      if (llvm::Error error = m_script_interpreter.GetLua().Run(data))
-        *io_handler.GetErrorStreamFileSP() << toString(std::move(error));
+      if (llvm::Error error = m_script_interpreter.GetLua().Run(data)) {
+        LockedStreamFile locked_stream =
+            io_handler.GetErrorStreamFileSP()->Lock();
+        locked_stream << toString(std::move(error));
+      }
       break;
     }
   }
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
index 9ea5b95a3d803..2b92beaf6c5ef 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
@@ -7,7 +7,10 @@
 //===----------------------------------------------------------------------===//
 
 #include "lldb/Host/Config.h"
+#include "lldb/Host/StreamFile.h"
 #include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-forward.h"
+#include <locale>
 
 #if LLDB_ENABLE_PYTHON
 
@@ -489,11 +492,11 @@ def function (frame, bp_loc, internal_dict):
     break;
   }
 
-  if (instructions) {
-    StreamFileSP output_sp(io_handler.GetOutputStreamFileSP());
-    if (output_sp && interactive) {
-      output_sp->PutCString(instructions);
-      output_sp->Flush();
+  if (instructions && interactive) {
+    if (LockableStreamFileSP stream_sp = io_handler.GetOutputStreamFileSP()) {
+      LockedStreamFile locked_stream = stream_sp->Lock();
+      locked_stream.PutCString(instructions);
+      locked_stream.Flush();
     }
   }
 }
@@ -527,10 +530,9 @@ void ScriptInterpreterPythonImpl::IOHandlerInputComplete(IOHandler &io_handler,
         bp_options.SetCallback(
             ScriptInterpreterPythonImpl::BreakpointCallbackFunction, baton_sp);
       } else if (!batch_mode) {
-        StreamFileSP error_sp = io_handler.GetErrorStreamFileSP();
-        if (error_sp) {
-          error_sp->Printf("Warning: No command attached to breakpoint.\n");
-          error_sp->Flush();
+        if (LockableStreamFileSP error_sp = io_handler.GetErrorStreamFileSP()) {
+          LockedStreamFile locked_stream = error_sp->Lock();
+          locked_stream.Printf("Warning: No command attached to breakpoint.\n");
         }
       }
     }
@@ -550,10 +552,9 @@ void ScriptInterpreterPythonImpl::IOHandlerInputComplete(IOHandler &io_handler,
       wp_options->SetCallback(
           ScriptInterpreterPythonImpl::WatchpointCallbackFunction, baton_sp);
     } else if (!batch_mode) {
-      StreamFileSP error_sp = io_handler.GetErrorStreamFileSP();
-      if (error_sp) {
-        error_sp->Printf("Warning: No command attached to breakpoint.\n");
-        error_sp->Flush();
+      if (LockableStreamFileSP error_sp = io_handler.GetErrorStreamFileSP()) {
+        LockedStreamFile locked_stream = error_sp->Lock();
+        locked_stream.Printf("Warning: No command attached to breakpoint.\n");
       }
     }
     m_active_io_handler = eIOHandlerNone;
@@ -680,7 +681,7 @@ bool ScriptInterpreterPythonImpl::EnterSession(uint16_t on_entry_flags,
   PythonDictionary &sys_module_dict = GetSysModuleDictionary();
   if (sys_module_dict.IsValid()) {
     lldb::FileSP top_in_sp;
-    lldb::StreamFileSP top_out_sp, top_err_sp;
+    lldb::LockableStreamFileSP top_out_sp, top_err_sp;
     if (!in_sp || !out_sp || !err_sp || !*in_sp || !*out_sp || !*err_sp)
       m_debugger.AdoptTopIOHandlerFilesIfInvalid(top_in_sp, top_out_sp,
                                                  top_err_sp);
@@ -696,12 +697,14 @@ bool ScriptInterpreterPythonImpl::EnterSession(uint16_t on_entry_flags,
 
     if (!SetStdHandle(out_sp, "stdout", m_saved_stdout, "w")) {
       if (top_out_sp)
-        SetStdHandle(top_out_sp->GetFileSP(), "stdout", m_saved_stdout, "w");
+        SetStdHandle(top_out_sp->GetUnlockedFileSP(), "stdout", m_saved_stdout,
+                     "w");
     }
 
     if (!SetStdHandle(err_sp, "stderr", m_saved_stderr, "w")) {
       if (top_err_sp)
-        SetStdHandle(top_err_sp->GetFileSP(), "stderr", m_saved_stderr, "w");
+        SetStdHandle(top_err_sp->GetUnlockedFileSP(), "stderr", m_saved_stderr,
+                     "w");
     }
   }
 
diff --git a/lldb/source/Target/ThreadPlanTracer.cpp b/lldb/source/Target/ThreadPlanTracer.cpp
index a119bf8589279..7c168ff4568f8 100644
--- a/lldb/source/Target/ThreadPlanTracer.cpp
+++ b/lldb/source/Target/ThreadPlanTracer.cpp
@@ -41,13 +41,13 @@ ThreadPlanTracer::ThreadPlanTracer(Thread &thread)
     : m_process(*thread.GetProcess().get()), m_tid(thread.GetID()),
       m_enabled(false), m_stream_sp(), m_thread(nullptr) {}
 
-Stream *ThreadPlanTracer::GetLogStream() {
+StreamSP ThreadPlanTracer::GetLogStream() {
   if (m_stream_sp)
-    return m_stream_sp.get();
+    return m_stream_sp;
   else {
     TargetSP target_sp(GetThread().CalculateTarget());
     if (target_sp)
-      return target_sp->GetDebugger().GetOutputStreamSP().get();
+      return target_sp->GetDebugger().GetAsyncOutputStream();
   }
   return nullptr;
 }
@@ -65,12 +65,11 @@ void ThreadPlanTracer::Log() {
   bool show_frame_index = false;
   bool show_fullpaths = false;
 
-  Stream *stream = GetLogStream();
-  if (stream) {
-    GetThread().GetStackFrameAtIndex(0)->Dump(stream, show_frame_index,
+  if (lldb::StreamSP stream_sp = GetLogStream()) {
+    GetThread().GetStackFrameAtIndex(0)->Dump(stream_sp.get(), show_frame_index,
                                               show_fullpaths);
-    stream->Printf("\n");
-    stream->Flush();
+    stream_sp->Printf("\n");
+    stream_sp->Flush();
   }
 }
 
@@ -123,15 +122,14 @@ TypeFromUser ThreadPlanAssemblyTracer::GetIntPointerType() {
 
 ThreadPlanAssemblyTracer::~ThreadPlanAssemblyTracer() = default;
 
-void ThreadPlanAssemblyTracer::TracingStarted() {
-}
+void ThreadPlanAssemblyTracer::TracingStarted() {}
 
 void ThreadPlanAssemblyTracer::TracingEnded() { m_register_values.clear(); }
 
 void ThreadPlanAssemblyTracer::Log() {
-  Stream *stream = GetLogStream();
+  lldb::StreamSP stream_sp = GetLogStream();
 
-  if (!stream)
+  if (!stream_sp)
     return;
 
   RegisterContext *reg_ctx = GetThread().GetRegisterContext().get();
@@ -142,9 +140,10 @@ void ThreadPlanAssemblyTracer::Log() {
   uint8_t buffer[16] = {0}; // Must be big enough for any single instruction
   addr_valid = m_process.GetTarget().ResolveLoadAddress(pc, pc_addr);
 
-  pc_addr.Dump(stream, &GetThread(), Address::DumpStyleResolvedDescription,
+  pc_addr.Dump(stream_sp.get(), &GetThread(),
+               Address::DumpStyleResolvedDescription,
                Address::DumpStyleModuleWithFileAddress);
-  stream->PutCString(" ");
+  stream_sp->PutCString(" ");
 
   Disassembler *disassembler = GetDisassembler();
   if (disassembler) {
@@ -175,7 +174,7 @@ void ThreadPlanAssemblyTracer::Log() {
             instruction_list.GetInstructionAtIndex(0).get();
         const FormatEntity::Entry *disassemble_format =
             m_process.GetTarget().GetDebugger().GetDisassemblyFormat();
-        instruction->Dump(stream, max_opcode_byte_size, show_address,
+        instruction->Dump(stream_sp.get(), max_opcode_byte_size, show_address,
                           show_bytes, show_control_flow_kind, nullptr, nullptr,
                           nullptr, disassemble_format, 0);
       }
@@ -198,12 +197,12 @@ void ThreadPlanAssemblyTracer::Log() {
 
     if (abi->GetArgumentValues(GetThread(), value_list)) {
       for (int arg_index = 0; arg_index < num_args; ++arg_index) {
-        stream->Printf(
+        stream_sp->Printf(
             "\n\targ[%d]=%llx", arg_index,
             value_list.GetValueAtIndex(arg_index)->GetScalar().ULongLong());
 
         if (arg_index + 1 < num_args)
-          stream->PutCString(", ");
+          stream_sp->PutCString(", ");
       }
     }
   }
@@ -222,14 +221,13 @@ void ThreadPlanAssemblyTracer::Log() {
       if (m_register_values[reg_num].GetType() == RegisterValue::eTypeInvalid ||
           reg_value != m_register_values[reg_num]) {
         if (reg_value.GetType() != RegisterValue::eTypeInvalid) {
-          stream->PutCString("\n\t");
-          DumpRegisterValue(reg_value, *stream, *reg_info, true, false,
+          stream_sp->PutCString("\n\t");
+          DumpRegisterValue(reg_value, *stream_sp, *reg_info, true, false,
                             eFormatDefault);
         }
       }
       m_register_values[reg_num] = reg_value;
     }
   }
-  stream->EOL();
-  stream->Flush();
+  stream_sp->EOL();
 }
diff --git a/lldb/unittests/Editline/EditlineTest.cpp b/lldb/unittests/Editline/EditlineTest.cpp
index 1327b587e7c3d..96be7931c54eb 100644
--- a/lldb/unittests/Editline/EditlineTest.cpp
+++ b/lldb/unittests/Editline/EditlineTest.cpp
@@ -25,6 +25,7 @@
 #include "lldb/Host/FileSystem.h"
 #include "lldb/Host/Pipe.h"
 #include "lldb/Host/PseudoTerminal.h"
+#include "lldb/Host/StreamFile.h"
 #include "lldb/Utility/Status.h"
 #include "lldb/Utility/StringList.h"
 
@@ -115,10 +116,15 @@ EditlineAdapter::EditlineAdapter()
   if (*_el_secondary_file == nullptr)
     return;
 
+  lldb::LockableStreamFileSP output_stream_sp =
+      std::make_shared<LockableStreamFile>(*_el_secondary_file, false);
+  lldb::LockableStreamFileSP error_stream_sp =
+      std::make_shared<LockableStreamFile>(*_el_secondary_file, false);
+
   // Create an Editline instance.
   _editline_sp.reset(new lldb_private::Editline(
-      "gtest editor", *_el_secondary_file, *_el_secondary_file,
-      *_el_secondary_file, /*color=*/false, output_mutex));
+      "gtest editor", *_el_secondary_file, output_stream_sp, error_stream_sp,
+      /*color=*/false));
   _editline_sp->SetPrompt("> ");
 
   // Hookup our input complete callback.



More information about the lldb-commits mailing list