This revision was automatically updated to reflect the committed changes.
Closed by commit rG10222764a9a3: [lldb] Avoid data race in 
IOHandlerProcessSTDIO (authored by JDevlieghere).
Herald added a project: LLDB.

Changed prior to commit:
  https://reviews.llvm.org/D120762?vs=412479&id=412567#toc

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D120762/new/

https://reviews.llvm.org/D120762

Files:
  lldb/source/Target/Process.cpp
  lldb/test/API/iohandler/stdio/Makefile
  lldb/test/API/iohandler/stdio/TestIOHandlerProcessSTDIO.py
  lldb/test/API/iohandler/stdio/main.cpp

Index: lldb/test/API/iohandler/stdio/main.cpp
===================================================================
--- /dev/null
+++ lldb/test/API/iohandler/stdio/main.cpp
@@ -0,0 +1,8 @@
+#include <iostream>
+#include <string>
+
+int main(int argc, char **argv) {
+  for (std::string line; std::getline(std::cin, line);)
+    std::cout << "stdout: " << line << '\n';
+  return 0;
+}
Index: lldb/test/API/iohandler/stdio/TestIOHandlerProcessSTDIO.py
===================================================================
--- /dev/null
+++ lldb/test/API/iohandler/stdio/TestIOHandlerProcessSTDIO.py
@@ -0,0 +1,30 @@
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.lldbpexpect import PExpectTest
+
+class TestIOHandlerProcessSTDIO(PExpectTest):
+
+    mydir = TestBase.compute_mydir(__file__)
+    NO_DEBUG_INFO_TESTCASE = True
+
+    # PExpect uses many timeouts internally and doesn't play well
+    # under ASAN on a loaded machine..
+    @skipIfAsan
+    def test(self):
+        self.build()
+        self.launch(executable=self.getBuildArtifact("a.out"))
+        self.child.sendline("run")
+
+        self.child.send("foo\n")
+        self.child.expect_exact("stdout: foo")
+
+        self.child.send("bar\n")
+        self.child.expect_exact("stdout: bar")
+
+        self.child.send("baz\n")
+        self.child.expect_exact("stdout: baz")
+
+        self.child.sendcontrol('d')
+        self.expect_prompt()
+        self.quit()
Index: lldb/test/API/iohandler/stdio/Makefile
===================================================================
--- /dev/null
+++ lldb/test/API/iohandler/stdio/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
Index: lldb/source/Target/Process.cpp
===================================================================
--- lldb/source/Target/Process.cpp
+++ lldb/source/Target/Process.cpp
@@ -4310,6 +4310,12 @@
 
   ~IOHandlerProcessSTDIO() override = default;
 
+  void SetIsRunning(bool running) {
+    std::lock_guard<std::mutex> guard(m_mutex);
+    SetIsDone(!running);
+    m_is_running = running;
+  }
+
   // Each IOHandler gets to run until it is done. It should read data from the
   // "in" and place output into "out" and "err and return when done.
   void Run() override {
@@ -4329,49 +4335,52 @@
 // FD_ZERO, FD_SET are not supported on windows
 #ifndef _WIN32
     const int pipe_read_fd = m_pipe.GetReadFileDescriptor();
-    m_is_running = true;
-    while (!GetIsDone()) {
+    SetIsRunning(true);
+    while (true) {
+      {
+        std::lock_guard<std::mutex> guard(m_mutex);
+        if (GetIsDone())
+          break;
+      }
+
       SelectHelper select_helper;
       select_helper.FDSetRead(read_fd);
       select_helper.FDSetRead(pipe_read_fd);
       Status error = select_helper.Select();
 
-      if (error.Fail()) {
-        SetIsDone(true);
-      } else {
-        char ch = 0;
-        size_t n;
-        if (select_helper.FDIsSetRead(read_fd)) {
-          n = 1;
-          if (m_read_file.Read(&ch, n).Success() && n == 1) {
-            if (m_write_file.Write(&ch, n).Fail() || n != 1)
-              SetIsDone(true);
-          } else
-            SetIsDone(true);
-        }
+      if (error.Fail())
+        break;
+
+      char ch = 0;
+      size_t n;
+      if (select_helper.FDIsSetRead(read_fd)) {
+        n = 1;
+        if (m_read_file.Read(&ch, n).Success() && n == 1) {
+          if (m_write_file.Write(&ch, n).Fail() || n != 1)
+            break;
+        } else
+          break;
+
         if (select_helper.FDIsSetRead(pipe_read_fd)) {
           size_t bytes_read;
           // Consume the interrupt byte
           Status error = m_pipe.Read(&ch, 1, bytes_read);
           if (error.Success()) {
-            switch (ch) {
-            case 'q':
-              SetIsDone(true);
+            if (ch == 'q')
               break;
-            case 'i':
+            if (ch == 'i')
               if (StateIsRunningState(m_process->GetState()))
                 m_process->SendAsyncInterrupt();
-              break;
-            }
           }
         }
       }
     }
-    m_is_running = false;
+    SetIsRunning(false);
 #endif
   }
 
   void Cancel() override {
+    std::lock_guard<std::mutex> guard(m_mutex);
     SetIsDone(true);
     // Only write to our pipe to cancel if we are in
     // IOHandlerProcessSTDIO::Run(). We can end up with a python command that
@@ -4428,7 +4437,8 @@
   NativeFile m_write_file; // Write to this file (usually the primary pty for
                            // getting io to debuggee)
   Pipe m_pipe;
-  std::atomic<bool> m_is_running{false};
+  std::mutex m_mutex;
+  bool m_is_running = false;
 };
 
 void Process::SetSTDIOFileDescriptor(int fd) {
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to