Author: Michał Górny
Date: 2022-06-21T19:47:30+02:00
New Revision: 5b04eb23ae1a4db074b7ddc2e0c136d008fb5bc7

URL: 
https://github.com/llvm/llvm-project/commit/5b04eb23ae1a4db074b7ddc2e0c136d008fb5bc7
DIFF: 
https://github.com/llvm/llvm-project/commit/5b04eb23ae1a4db074b7ddc2e0c136d008fb5bc7.diff

LOG: [lldb] [MainLoop] Support "pending callbacks", to be called once

Support adding a "pending callback" to the main loop, that will be
called once after all the pending events are processed.  This can be
e.g. to defer destroying the process instance until its exit is fully
processed, as suggested in D127500.

Sponsored by: The FreeBSD Foundation
Differential Revision: https://reviews.llvm.org/D128253

Added: 
    

Modified: 
    lldb/include/lldb/Host/MainLoop.h
    lldb/include/lldb/Host/MainLoopBase.h
    lldb/source/Host/common/MainLoop.cpp
    lldb/unittests/Host/MainLoopTest.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/include/lldb/Host/MainLoop.h 
b/lldb/include/lldb/Host/MainLoop.h
index 94499f5834633..f04ac7359cc18 100644
--- a/lldb/include/lldb/Host/MainLoop.h
+++ b/lldb/include/lldb/Host/MainLoop.h
@@ -14,6 +14,7 @@
 #include "llvm/ADT/DenseMap.h"
 #include <csignal>
 #include <list>
+#include <vector>
 
 #if !HAVE_PPOLL && !HAVE_SYS_EVENT_H && !defined(__ANDROID__)
 #define SIGNAL_POLLING_UNSUPPORTED 1
@@ -59,6 +60,11 @@ class MainLoop : public MainLoopBase {
   SignalHandleUP RegisterSignal(int signo, const Callback &callback,
                                 Status &error);
 
+  // Add a pending callback that will be executed once after all the pending
+  // events are processed. The callback will be executed even if termination
+  // was requested.
+  void AddPendingCallback(const Callback &callback) override;
+
   Status Run() override;
 
   // This should only be performed from a callback. Do not attempt to terminate
@@ -104,6 +110,7 @@ class MainLoop : public MainLoopBase {
 
   llvm::DenseMap<IOObject::WaitableHandle, Callback> m_read_fds;
   llvm::DenseMap<int, SignalInfo> m_signals;
+  std::vector<Callback> m_pending_callbacks;
 #if HAVE_SYS_EVENT_H
   int m_kqueue;
 #endif

diff  --git a/lldb/include/lldb/Host/MainLoopBase.h 
b/lldb/include/lldb/Host/MainLoopBase.h
index 67857b26ac70b..2a2899341a51b 100644
--- a/lldb/include/lldb/Host/MainLoopBase.h
+++ b/lldb/include/lldb/Host/MainLoopBase.h
@@ -46,6 +46,13 @@ class MainLoopBase {
     llvm_unreachable("Not implemented");
   }
 
+  // Add a pending callback that will be executed once after all the pending
+  // events are processed. The callback will be executed even if termination
+  // was requested.
+  virtual void AddPendingCallback(const Callback &callback) {
+    llvm_unreachable("Not implemented");
+  }
+
   // Waits for registered events and invoke the proper callbacks. Returns when
   // all callbacks deregister themselves or when someone requests termination.
   virtual Status Run() { llvm_unreachable("Not implemented"); }

diff  --git a/lldb/source/Host/common/MainLoop.cpp 
b/lldb/source/Host/common/MainLoop.cpp
index d36587ce2346e..8e384c9266b6b 100644
--- a/lldb/source/Host/common/MainLoop.cpp
+++ b/lldb/source/Host/common/MainLoop.cpp
@@ -347,6 +347,10 @@ MainLoop::RegisterSignal(int signo, const Callback 
&callback, Status &error) {
 #endif
 }
 
+void MainLoop::AddPendingCallback(const Callback &callback) {
+  m_pending_callbacks.push_back(callback);
+}
+
 void MainLoop::UnregisterReadObject(IOObject::WaitableHandle handle) {
   bool erased = m_read_fds.erase(handle);
   UNUSED_IF_ASSERT_DISABLED(erased);
@@ -401,6 +405,10 @@ Status MainLoop::Run() {
       return error;
 
     impl.ProcessEvents();
+
+    for (const Callback &callback : m_pending_callbacks)
+      callback(*this);
+    m_pending_callbacks.clear();
   }
   return Status();
 }

diff  --git a/lldb/unittests/Host/MainLoopTest.cpp 
b/lldb/unittests/Host/MainLoopTest.cpp
index 890f6eb66197e..fc79c0e38a392 100644
--- a/lldb/unittests/Host/MainLoopTest.cpp
+++ b/lldb/unittests/Host/MainLoopTest.cpp
@@ -98,6 +98,56 @@ TEST_F(MainLoopTest, TerminatesImmediately) {
   ASSERT_EQ(1u, callback_count);
 }
 
+TEST_F(MainLoopTest, PendingCallback) {
+  char X = 'X';
+  size_t len = sizeof(X);
+  ASSERT_TRUE(socketpair[0]->Write(&X, len).Success());
+
+  MainLoop loop;
+  Status error;
+  auto handle = loop.RegisterReadObject(
+      socketpair[1],
+      [&](MainLoopBase &loop) {
+        // Both callbacks should be called before the loop terminates.
+        loop.AddPendingCallback(make_callback());
+        loop.AddPendingCallback(make_callback());
+        loop.RequestTermination();
+      },
+      error);
+  ASSERT_TRUE(error.Success());
+  ASSERT_TRUE(handle);
+  ASSERT_TRUE(loop.Run().Success());
+  ASSERT_EQ(2u, callback_count);
+}
+
+TEST_F(MainLoopTest, PendingCallbackCalledOnlyOnce) {
+  char X = 'X';
+  size_t len = sizeof(X);
+  ASSERT_TRUE(socketpair[0]->Write(&X, len).Success());
+
+  MainLoop loop;
+  Status error;
+  auto handle = loop.RegisterReadObject(
+      socketpair[1],
+      [&](MainLoopBase &loop) {
+        // Add one pending callback on the first iteration.
+        if (callback_count == 0) {
+          loop.AddPendingCallback([&](MainLoopBase &loop) {
+            callback_count++;
+          });
+        }
+        // Terminate the loop on second iteration.
+        if (callback_count++ >= 1)
+          loop.RequestTermination();
+      },
+      error);
+  ASSERT_TRUE(error.Success());
+  ASSERT_TRUE(handle);
+  ASSERT_TRUE(loop.Run().Success());
+  // 2 iterations of read callback + 1 call of pending callback.
+  ASSERT_EQ(3u, callback_count);
+}
+
 #ifdef LLVM_ON_UNIX
 TEST_F(MainLoopTest, DetectsEOF) {
 


        
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to