1 | //===-- MainLoopTest.cpp --------------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "lldb/Host/MainLoop.h" |
10 | #include "TestingSupport/SubsystemRAII.h" |
11 | #include "lldb/Host/ConnectionFileDescriptor.h" |
12 | #include "lldb/Host/FileSystem.h" |
13 | #include "lldb/Host/PseudoTerminal.h" |
14 | #include "lldb/Host/common/TCPSocket.h" |
15 | #include "llvm/Testing/Support/Error.h" |
16 | #include "gtest/gtest.h" |
17 | #include <future> |
18 | |
19 | using namespace lldb_private; |
20 | |
21 | namespace { |
22 | class MainLoopTest : public testing::Test { |
23 | public: |
24 | SubsystemRAII<FileSystem, Socket> subsystems; |
25 | |
26 | void SetUp() override { |
27 | bool child_processes_inherit = false; |
28 | Status error; |
29 | std::unique_ptr<TCPSocket> listen_socket_up( |
30 | new TCPSocket(true, child_processes_inherit)); |
31 | ASSERT_TRUE(error.Success()); |
32 | error = listen_socket_up->Listen(name: "localhost:0" , backlog: 5); |
33 | ASSERT_TRUE(error.Success()); |
34 | |
35 | Socket *accept_socket; |
36 | std::unique_ptr<TCPSocket> connect_socket_up( |
37 | new TCPSocket(true, child_processes_inherit)); |
38 | error = connect_socket_up->Connect( |
39 | name: llvm::formatv(Fmt: "localhost:{0}" , Vals: listen_socket_up->GetLocalPortNumber()) |
40 | .str()); |
41 | ASSERT_TRUE(error.Success()); |
42 | ASSERT_TRUE(listen_socket_up->Accept(accept_socket).Success()); |
43 | |
44 | callback_count = 0; |
45 | socketpair[0] = std::move(connect_socket_up); |
46 | socketpair[1].reset(p: accept_socket); |
47 | } |
48 | |
49 | void TearDown() override { |
50 | socketpair[0].reset(); |
51 | socketpair[1].reset(); |
52 | } |
53 | |
54 | protected: |
55 | MainLoop::Callback make_callback() { |
56 | return [&](MainLoopBase &loop) { |
57 | ++callback_count; |
58 | loop.RequestTermination(); |
59 | }; |
60 | } |
61 | std::shared_ptr<Socket> socketpair[2]; |
62 | unsigned callback_count; |
63 | }; |
64 | } // namespace |
65 | |
66 | TEST_F(MainLoopTest, ReadObject) { |
67 | char X = 'X'; |
68 | size_t len = sizeof(X); |
69 | ASSERT_TRUE(socketpair[0]->Write(&X, len).Success()); |
70 | |
71 | MainLoop loop; |
72 | |
73 | Status error; |
74 | auto handle = loop.RegisterReadObject(object_sp: socketpair[1], callback: make_callback(), error); |
75 | ASSERT_TRUE(error.Success()); |
76 | ASSERT_TRUE(handle); |
77 | ASSERT_TRUE(loop.Run().Success()); |
78 | ASSERT_EQ(1u, callback_count); |
79 | } |
80 | |
81 | TEST_F(MainLoopTest, TerminatesImmediately) { |
82 | char X = 'X'; |
83 | size_t len = sizeof(X); |
84 | ASSERT_TRUE(socketpair[0]->Write(&X, len).Success()); |
85 | ASSERT_TRUE(socketpair[1]->Write(&X, len).Success()); |
86 | |
87 | MainLoop loop; |
88 | Status error; |
89 | auto handle0 = loop.RegisterReadObject(object_sp: socketpair[0], callback: make_callback(), error); |
90 | ASSERT_TRUE(error.Success()); |
91 | auto handle1 = loop.RegisterReadObject(object_sp: socketpair[1], callback: make_callback(), error); |
92 | ASSERT_TRUE(error.Success()); |
93 | |
94 | ASSERT_TRUE(loop.Run().Success()); |
95 | ASSERT_EQ(1u, callback_count); |
96 | } |
97 | |
98 | TEST_F(MainLoopTest, PendingCallback) { |
99 | char X = 'X'; |
100 | size_t len = sizeof(X); |
101 | ASSERT_TRUE(socketpair[0]->Write(&X, len).Success()); |
102 | |
103 | MainLoop loop; |
104 | Status error; |
105 | auto handle = loop.RegisterReadObject( |
106 | object_sp: socketpair[1], |
107 | callback: [&](MainLoopBase &loop) { |
108 | // Both callbacks should be called before the loop terminates. |
109 | loop.AddPendingCallback(callback: make_callback()); |
110 | loop.AddPendingCallback(callback: make_callback()); |
111 | loop.RequestTermination(); |
112 | }, |
113 | error); |
114 | ASSERT_TRUE(error.Success()); |
115 | ASSERT_TRUE(handle); |
116 | ASSERT_TRUE(loop.Run().Success()); |
117 | ASSERT_EQ(2u, callback_count); |
118 | } |
119 | |
120 | TEST_F(MainLoopTest, PendingCallbackCalledOnlyOnce) { |
121 | char X = 'X'; |
122 | size_t len = sizeof(X); |
123 | ASSERT_TRUE(socketpair[0]->Write(&X, len).Success()); |
124 | |
125 | MainLoop loop; |
126 | Status error; |
127 | auto handle = loop.RegisterReadObject( |
128 | object_sp: socketpair[1], |
129 | callback: [&](MainLoopBase &loop) { |
130 | // Add one pending callback on the first iteration. |
131 | if (callback_count == 0) { |
132 | loop.AddPendingCallback(callback: [&](MainLoopBase &loop) { |
133 | callback_count++; |
134 | }); |
135 | } |
136 | // Terminate the loop on second iteration. |
137 | if (callback_count++ >= 1) |
138 | loop.RequestTermination(); |
139 | }, |
140 | error); |
141 | ASSERT_TRUE(error.Success()); |
142 | ASSERT_TRUE(handle); |
143 | ASSERT_TRUE(loop.Run().Success()); |
144 | // 2 iterations of read callback + 1 call of pending callback. |
145 | ASSERT_EQ(3u, callback_count); |
146 | } |
147 | |
148 | TEST_F(MainLoopTest, PendingCallbackTrigger) { |
149 | MainLoop loop; |
150 | std::promise<void> add_callback2; |
151 | bool callback1_called = false; |
152 | loop.AddPendingCallback(callback: [&](MainLoopBase &loop) { |
153 | callback1_called = true; |
154 | add_callback2.set_value(); |
155 | }); |
156 | Status error; |
157 | auto socket_handle = loop.RegisterReadObject( |
158 | object_sp: socketpair[1], callback: [](MainLoopBase &) {}, error); |
159 | ASSERT_TRUE(socket_handle); |
160 | ASSERT_THAT_ERROR(error.ToError(), llvm::Succeeded()); |
161 | bool callback2_called = false; |
162 | std::thread callback2_adder([&]() { |
163 | add_callback2.get_future().get(); |
164 | loop.AddPendingCallback(callback: [&](MainLoopBase &loop) { |
165 | callback2_called = true; |
166 | loop.RequestTermination(); |
167 | }); |
168 | }); |
169 | ASSERT_THAT_ERROR(loop.Run().ToError(), llvm::Succeeded()); |
170 | callback2_adder.join(); |
171 | ASSERT_TRUE(callback1_called); |
172 | ASSERT_TRUE(callback2_called); |
173 | } |
174 | |
175 | // Regression test for assertion failure if a lot of callbacks end up |
176 | // being queued after loop exits. |
177 | TEST_F(MainLoopTest, PendingCallbackAfterLoopExited) { |
178 | MainLoop loop; |
179 | Status error; |
180 | ASSERT_TRUE(loop.Run().Success()); |
181 | // Try to fill the pipe buffer in. |
182 | for (int i = 0; i < 65536; ++i) |
183 | loop.AddPendingCallback(callback: [&](MainLoopBase &loop) {}); |
184 | } |
185 | |
186 | #ifdef LLVM_ON_UNIX |
187 | TEST_F(MainLoopTest, DetectsEOF) { |
188 | |
189 | PseudoTerminal term; |
190 | ASSERT_THAT_ERROR(term.OpenFirstAvailablePrimary(O_RDWR), llvm::Succeeded()); |
191 | ASSERT_THAT_ERROR(term.OpenSecondary(O_RDWR | O_NOCTTY), llvm::Succeeded()); |
192 | auto conn = std::make_unique<ConnectionFileDescriptor>( |
193 | args: term.ReleasePrimaryFileDescriptor(), args: true); |
194 | |
195 | Status error; |
196 | MainLoop loop; |
197 | auto handle = |
198 | loop.RegisterReadObject(object_sp: conn->GetReadObject(), callback: make_callback(), error); |
199 | ASSERT_TRUE(error.Success()); |
200 | term.CloseSecondaryFileDescriptor(); |
201 | |
202 | ASSERT_TRUE(loop.Run().Success()); |
203 | ASSERT_EQ(1u, callback_count); |
204 | } |
205 | |
206 | TEST_F(MainLoopTest, Signal) { |
207 | MainLoop loop; |
208 | Status error; |
209 | |
210 | auto handle = loop.RegisterSignal(SIGUSR1, callback: make_callback(), error); |
211 | ASSERT_TRUE(error.Success()); |
212 | kill(pid: getpid(), SIGUSR1); |
213 | ASSERT_TRUE(loop.Run().Success()); |
214 | ASSERT_EQ(1u, callback_count); |
215 | } |
216 | |
217 | // Test that a signal which is not monitored by the MainLoop does not |
218 | // cause a premature exit. |
219 | TEST_F(MainLoopTest, UnmonitoredSignal) { |
220 | MainLoop loop; |
221 | Status error; |
222 | struct sigaction sa; |
223 | sa.sa_sigaction = [](int, siginfo_t *, void *) { }; |
224 | sa.sa_flags = SA_SIGINFO; // important: no SA_RESTART |
225 | sigemptyset(set: &sa.sa_mask); |
226 | ASSERT_EQ(0, sigaction(SIGUSR2, &sa, nullptr)); |
227 | |
228 | auto handle = loop.RegisterSignal(SIGUSR1, callback: make_callback(), error); |
229 | ASSERT_TRUE(error.Success()); |
230 | kill(pid: getpid(), SIGUSR2); |
231 | kill(pid: getpid(), SIGUSR1); |
232 | ASSERT_TRUE(loop.Run().Success()); |
233 | ASSERT_EQ(1u, callback_count); |
234 | } |
235 | |
236 | // Test that two callbacks can be registered for the same signal |
237 | // and unregistered independently. |
238 | TEST_F(MainLoopTest, TwoSignalCallbacks) { |
239 | MainLoop loop; |
240 | Status error; |
241 | unsigned callback2_count = 0; |
242 | unsigned callback3_count = 0; |
243 | |
244 | auto handle = loop.RegisterSignal(SIGUSR1, callback: make_callback(), error); |
245 | ASSERT_TRUE(error.Success()); |
246 | |
247 | { |
248 | // Run a single iteration with two callbacks enabled. |
249 | auto handle2 = loop.RegisterSignal( |
250 | SIGUSR1, callback: [&](MainLoopBase &loop) { ++callback2_count; }, error); |
251 | ASSERT_TRUE(error.Success()); |
252 | |
253 | kill(pid: getpid(), SIGUSR1); |
254 | ASSERT_TRUE(loop.Run().Success()); |
255 | ASSERT_EQ(1u, callback_count); |
256 | ASSERT_EQ(1u, callback2_count); |
257 | ASSERT_EQ(0u, callback3_count); |
258 | } |
259 | |
260 | { |
261 | // Make sure that remove + add new works. |
262 | auto handle3 = loop.RegisterSignal( |
263 | SIGUSR1, callback: [&](MainLoopBase &loop) { ++callback3_count; }, error); |
264 | ASSERT_TRUE(error.Success()); |
265 | |
266 | kill(pid: getpid(), SIGUSR1); |
267 | ASSERT_TRUE(loop.Run().Success()); |
268 | ASSERT_EQ(2u, callback_count); |
269 | ASSERT_EQ(1u, callback2_count); |
270 | ASSERT_EQ(1u, callback3_count); |
271 | } |
272 | |
273 | // Both extra callbacks should be unregistered now. |
274 | kill(pid: getpid(), SIGUSR1); |
275 | ASSERT_TRUE(loop.Run().Success()); |
276 | ASSERT_EQ(3u, callback_count); |
277 | ASSERT_EQ(1u, callback2_count); |
278 | ASSERT_EQ(1u, callback3_count); |
279 | } |
280 | #endif |
281 | |