Skip to content

Commit 5b04eb2

Browse files
committed
[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
1 parent 9f499d9 commit 5b04eb2

File tree

4 files changed

+72
-0
lines changed

4 files changed

+72
-0
lines changed

lldb/include/lldb/Host/MainLoop.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "llvm/ADT/DenseMap.h"
1515
#include <csignal>
1616
#include <list>
17+
#include <vector>
1718

1819
#if !HAVE_PPOLL && !HAVE_SYS_EVENT_H && !defined(__ANDROID__)
1920
#define SIGNAL_POLLING_UNSUPPORTED 1
@@ -59,6 +60,11 @@ class MainLoop : public MainLoopBase {
5960
SignalHandleUP RegisterSignal(int signo, const Callback &callback,
6061
Status &error);
6162

63+
// Add a pending callback that will be executed once after all the pending
64+
// events are processed. The callback will be executed even if termination
65+
// was requested.
66+
void AddPendingCallback(const Callback &callback) override;
67+
6268
Status Run() override;
6369

6470
// This should only be performed from a callback. Do not attempt to terminate
@@ -104,6 +110,7 @@ class MainLoop : public MainLoopBase {
104110

105111
llvm::DenseMap<IOObject::WaitableHandle, Callback> m_read_fds;
106112
llvm::DenseMap<int, SignalInfo> m_signals;
113+
std::vector<Callback> m_pending_callbacks;
107114
#if HAVE_SYS_EVENT_H
108115
int m_kqueue;
109116
#endif

lldb/include/lldb/Host/MainLoopBase.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@ class MainLoopBase {
4646
llvm_unreachable("Not implemented");
4747
}
4848

49+
// Add a pending callback that will be executed once after all the pending
50+
// events are processed. The callback will be executed even if termination
51+
// was requested.
52+
virtual void AddPendingCallback(const Callback &callback) {
53+
llvm_unreachable("Not implemented");
54+
}
55+
4956
// Waits for registered events and invoke the proper callbacks. Returns when
5057
// all callbacks deregister themselves or when someone requests termination.
5158
virtual Status Run() { llvm_unreachable("Not implemented"); }

lldb/source/Host/common/MainLoop.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,10 @@ MainLoop::RegisterSignal(int signo, const Callback &callback, Status &error) {
347347
#endif
348348
}
349349

350+
void MainLoop::AddPendingCallback(const Callback &callback) {
351+
m_pending_callbacks.push_back(callback);
352+
}
353+
350354
void MainLoop::UnregisterReadObject(IOObject::WaitableHandle handle) {
351355
bool erased = m_read_fds.erase(handle);
352356
UNUSED_IF_ASSERT_DISABLED(erased);
@@ -401,6 +405,10 @@ Status MainLoop::Run() {
401405
return error;
402406

403407
impl.ProcessEvents();
408+
409+
for (const Callback &callback : m_pending_callbacks)
410+
callback(*this);
411+
m_pending_callbacks.clear();
404412
}
405413
return Status();
406414
}

lldb/unittests/Host/MainLoopTest.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,56 @@ TEST_F(MainLoopTest, TerminatesImmediately) {
9898
ASSERT_EQ(1u, callback_count);
9999
}
100100

101+
TEST_F(MainLoopTest, PendingCallback) {
102+
char X = 'X';
103+
size_t len = sizeof(X);
104+
ASSERT_TRUE(socketpair[0]->Write(&X, len).Success());
105+
106+
MainLoop loop;
107+
Status error;
108+
auto handle = loop.RegisterReadObject(
109+
socketpair[1],
110+
[&](MainLoopBase &loop) {
111+
// Both callbacks should be called before the loop terminates.
112+
loop.AddPendingCallback(make_callback());
113+
loop.AddPendingCallback(make_callback());
114+
loop.RequestTermination();
115+
},
116+
error);
117+
ASSERT_TRUE(error.Success());
118+
ASSERT_TRUE(handle);
119+
ASSERT_TRUE(loop.Run().Success());
120+
ASSERT_EQ(2u, callback_count);
121+
}
122+
123+
TEST_F(MainLoopTest, PendingCallbackCalledOnlyOnce) {
124+
char X = 'X';
125+
size_t len = sizeof(X);
126+
ASSERT_TRUE(socketpair[0]->Write(&X, len).Success());
127+
128+
MainLoop loop;
129+
Status error;
130+
auto handle = loop.RegisterReadObject(
131+
socketpair[1],
132+
[&](MainLoopBase &loop) {
133+
// Add one pending callback on the first iteration.
134+
if (callback_count == 0) {
135+
loop.AddPendingCallback([&](MainLoopBase &loop) {
136+
callback_count++;
137+
});
138+
}
139+
// Terminate the loop on second iteration.
140+
if (callback_count++ >= 1)
141+
loop.RequestTermination();
142+
},
143+
error);
144+
ASSERT_TRUE(error.Success());
145+
ASSERT_TRUE(handle);
146+
ASSERT_TRUE(loop.Run().Success());
147+
// 2 iterations of read callback + 1 call of pending callback.
148+
ASSERT_EQ(3u, callback_count);
149+
}
150+
101151
#ifdef LLVM_ON_UNIX
102152
TEST_F(MainLoopTest, DetectsEOF) {
103153

0 commit comments

Comments
 (0)