teemperor updated this revision to Diff 152402.
teemperor added a comment.

- Small code style adjustment.


https://reviews.llvm.org/D48463

Files:
  include/lldb/Core/Debugger.h
  source/Core/Debugger.cpp
  source/Core/IOHandler.cpp

Index: source/Core/IOHandler.cpp
===================================================================
--- source/Core/IOHandler.cpp
+++ source/Core/IOHandler.cpp
@@ -348,6 +348,15 @@
 }
 
 bool IOHandlerEditline::GetLine(std::string &line, bool &interrupted) {
+  // For safety reasons let's delay all messages that might be printed
+  // to the output until we are done with this method. Printing to the
+  // standard output while we are working with EditLine could cause
+  // problems. Especially any held locks that are needed for printing
+  // to the console are problematic. We might take those locks here
+  // because we want to do console output and they might be requested by any
+  // lldb code that will be called from here (possibly in another thread).
+  Debugger::MessageDelayScope buffer_scope(m_debugger);
+
 #ifndef LLDB_DISABLE_LIBEDIT
   if (m_editline_ap) {
     return m_editline_ap->GetLine(line, interrupted);
Index: source/Core/Debugger.cpp
===================================================================
--- source/Core/Debugger.cpp
+++ source/Core/Debugger.cpp
@@ -985,6 +985,21 @@
 }
 
 void Debugger::PrintAsync(const char *s, size_t len, bool is_stdout) {
+  bool should_forward = false;
+  {
+    // We check if any user requested to delay output to a later time
+    // (which is designated by m_delayed_output_counter being not 0).
+    std::lock_guard<std::mutex> guard(m_delayed_output_mutex);
+    if (m_delayed_output_counter != 0) {
+      // We want to delay messages, so push them to the buffer.
+      m_delayed_output.emplace_back(std::string(s, len), is_stdout);
+    } else {
+      // Allow direct forwarding to the IOHandlers.
+      should_forward = true;
+    }
+  }
+  if (!should_forward)
+    return;
   lldb::StreamFileSP stream = is_stdout ? GetOutputFile() : GetErrorFile();
   m_input_reader_stack.PrintAsync(stream.get(), s, len);
 }
Index: include/lldb/Core/Debugger.h
===================================================================
--- include/lldb/Core/Debugger.h
+++ include/lldb/Core/Debugger.h
@@ -204,6 +204,26 @@
   bool CheckTopIOHandlerTypes(IOHandler::Type top_type,
                               IOHandler::Type second_top_type);
 
+  //------------------------------------------------------------------
+  /// Guard class that delays the output printing from the given debugger
+  /// until the end of the scope. Useful if you aquire any IOHandler locks
+  /// in the current scope and also call code that might print via an IOHandler
+  /// (which could lead to deadlocks).
+  ///
+  /// This guard can be used in nested scopes. Multiple guards on the
+  /// same debugger behave the same as if only the top-most guard
+  /// requested to delay the messages.
+  ///
+  /// @see EnableDelayedPrinting() and TryFlushingDelayedMessages().
+  //------------------------------------------------------------------
+  struct MessageDelayScope {
+    Debugger &m_debugger;
+    MessageDelayScope(Debugger &d) : m_debugger(d) {
+      m_debugger.EnableDelayedPrinting();
+    }
+    ~MessageDelayScope() { m_debugger.TryFlushingDelayedMessages(); }
+  };
+
   void PrintAsync(const char *s, size_t len, bool is_stdout);
 
   ConstString GetTopIOHandlerControlSequence(char ch);
@@ -417,6 +437,74 @@
   // object
   Debugger(lldb::LogOutputCallback m_log_callback, void *baton);
 
+  //------------------------------------------------------------------
+  /// A message sent via PrintAsync that we store to delay the actual
+  /// call until later.
+  //------------------------------------------------------------------
+  struct DelayedMessage {
+    DelayedMessage(std::string data, bool for_stdout)
+        : m_data(data), m_for_stdout(for_stdout) {}
+    std::string m_data;
+    bool m_for_stdout;
+  };
+
+  //------------------------------------------------------------------
+  /// Starts delaying any calls to PrintAsync until a matching call
+  /// to FlushDelayedMessages occurs. This function should only be
+  /// called before a matching call to FlushDelayedMessages.
+  ///
+  /// This method is intentionally private. Use MessageDelayScope to
+  /// call this method (and the matching FlushDelayedMessage()) method
+  /// below.
+  //------------------------------------------------------------------
+  void EnableDelayedPrinting() {
+    std::lock_guard<std::mutex> guard(m_delayed_output_mutex);
+    ++m_delayed_output_counter;
+  }
+
+  //------------------------------------------------------------------
+  /// Tries to flush any delayed messages to PrintAsync. This might
+  /// not be possible if it was requested by multiple users to
+  /// delay messages and not all have given consent at this point for
+  /// forwarding the messages.
+  //------------------------------------------------------------------
+  void TryFlushingDelayedMessages() {
+    // We copy the buffer here. We do *not* want to call PrintAsync (or
+    // actually anything else) while we own this lock.
+    std::vector<DelayedMessage> buffer_to_send;
+    bool should_print_delayed = false;
+    {
+      std::lock_guard<std::mutex> guard(m_delayed_output_mutex);
+      buffer_to_send = std::move(m_delayed_output);
+      m_delayed_output.clear();
+      --m_delayed_output_counter;
+      // 0 means that all users we have have consented that we can now foward
+      // our delayed messages to the IOHandlers.
+      if (m_delayed_output_counter == 0)
+        should_print_delayed = true;
+    }
+    if (!should_print_delayed)
+      return;
+    // Forward all cached messages to the IOHandlers.
+    for (const auto &msg : buffer_to_send) {
+      PrintAsync(msg.m_data.data(), msg.m_data.size(), msg.m_for_stdout);
+    }
+  }
+
+  /// This mutex protects *only* m_async_buffer and m_should_buffer_async.
+  std::mutex m_delayed_output_mutex;
+  /// The list of delayed messages in the order in which they arrived.
+  /// @note Protected by m_delayed_output_mutex.
+  std::vector<DelayedMessage> m_delayed_output;
+  /// This counter keeps track of how many users have requested that we delay
+  /// the forwarding of calls to PrintAsync to the IOHandlers. If the counter
+  /// is 0, then it's safe to directly forward all calls to PrintAsync to the
+  /// IOHandlers. If the counter is *not* 0, then all calls to PrintAsync
+  /// should be cached until the counter is 0 again.
+  /// @see EnableDelayedPrinting() and TryFlushingDelayedMessages().
+  /// @note Protected by m_delayed_output_mutex.
+  unsigned m_delayed_output_counter = 0;
+
   DISALLOW_COPY_AND_ASSIGN(Debugger);
 };
 
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to