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/Config/llvm-config.h" // for LLVM_ON_UNIX
16#include "llvm/Testing/Support/Error.h"
17#include "gtest/gtest.h"
18#include <chrono>
19#include <future>
20#include <thread>
21
22using namespace lldb_private;
23
24namespace {
25class MainLoopTest : public testing::Test {
26public:
27 SubsystemRAII<FileSystem, Socket> subsystems;
28
29 void SetUp() override {
30 Status error;
31 auto listen_socket_up = std::make_unique<TCPSocket>(args: true);
32 ASSERT_TRUE(error.Success());
33 error = listen_socket_up->Listen(name: "localhost:0", backlog: 5);
34 ASSERT_TRUE(error.Success());
35
36 Socket *accept_socket;
37 auto connect_socket_up = std::make_unique<TCPSocket>(args: true);
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(std::chrono::seconds(1), accept_socket)
43 .Success());
44
45 callback_count = 0;
46 socketpair[0] = std::move(connect_socket_up);
47 socketpair[1].reset(p: accept_socket);
48 }
49
50 void TearDown() override {
51 socketpair[0].reset();
52 socketpair[1].reset();
53 }
54
55protected:
56 MainLoop::Callback make_callback() {
57 return [&](MainLoopBase &loop) {
58 ++callback_count;
59 loop.RequestTermination();
60 };
61 }
62 std::shared_ptr<Socket> socketpair[2];
63 unsigned callback_count;
64};
65} // namespace
66
67TEST_F(MainLoopTest, ReadObject) {
68 char X = 'X';
69 size_t len = sizeof(X);
70 ASSERT_TRUE(socketpair[0]->Write(&X, len).Success());
71
72 MainLoop loop;
73
74 Status error;
75 auto handle = loop.RegisterReadObject(object_sp: socketpair[1], callback: make_callback(), error);
76 ASSERT_TRUE(error.Success());
77 ASSERT_TRUE(handle);
78 ASSERT_TRUE(loop.Run().Success());
79 ASSERT_EQ(1u, callback_count);
80}
81
82TEST_F(MainLoopTest, NoSpuriousReads) {
83 // Write one byte into the socket.
84 char X = 'X';
85 size_t len = sizeof(X);
86 ASSERT_TRUE(socketpair[0]->Write(&X, len).Success());
87
88 MainLoop loop;
89
90 Status error;
91 auto handle = loop.RegisterReadObject(
92 object_sp: socketpair[1],
93 callback: [this](MainLoopBase &) {
94 if (callback_count == 0) {
95 // Read the byte back the first time we're called. After that, the
96 // socket is empty, and we should not be called anymore.
97 char X;
98 size_t len = sizeof(X);
99 EXPECT_THAT_ERROR(socketpair[1]->Read(&X, len).ToError(),
100 llvm::Succeeded());
101 EXPECT_EQ(len, sizeof(X));
102 }
103 ++callback_count;
104 },
105 error);
106 ASSERT_THAT_ERROR(error.ToError(), llvm::Succeeded());
107 // Terminate the loop after one second.
108 loop.AddCallback(callback: [](MainLoopBase &loop) { loop.RequestTermination(); },
109 delay: std::chrono::seconds(1));
110 ASSERT_THAT_ERROR(loop.Run().ToError(), llvm::Succeeded());
111
112 // Make sure the callback was called only once.
113 ASSERT_EQ(1u, callback_count);
114}
115
116TEST_F(MainLoopTest, TerminatesImmediately) {
117 char X = 'X';
118 size_t len = sizeof(X);
119 ASSERT_TRUE(socketpair[0]->Write(&X, len).Success());
120 ASSERT_TRUE(socketpair[1]->Write(&X, len).Success());
121
122 MainLoop loop;
123 Status error;
124 auto handle0 = loop.RegisterReadObject(object_sp: socketpair[0], callback: make_callback(), error);
125 ASSERT_TRUE(error.Success());
126 auto handle1 = loop.RegisterReadObject(object_sp: socketpair[1], callback: make_callback(), error);
127 ASSERT_TRUE(error.Success());
128
129 ASSERT_TRUE(loop.Run().Success());
130 ASSERT_EQ(1u, callback_count);
131}
132
133TEST_F(MainLoopTest, PendingCallback) {
134 char X = 'X';
135 size_t len = sizeof(X);
136 ASSERT_TRUE(socketpair[0]->Write(&X, len).Success());
137
138 MainLoop loop;
139 Status error;
140 auto handle = loop.RegisterReadObject(
141 object_sp: socketpair[1],
142 callback: [&](MainLoopBase &loop) {
143 // Both callbacks should be called before the loop terminates.
144 loop.AddPendingCallback(callback: make_callback());
145 loop.AddPendingCallback(callback: make_callback());
146 loop.RequestTermination();
147 },
148 error);
149 ASSERT_TRUE(error.Success());
150 ASSERT_TRUE(handle);
151 ASSERT_TRUE(loop.Run().Success());
152 ASSERT_EQ(2u, callback_count);
153}
154
155TEST_F(MainLoopTest, PendingCallbackCalledOnlyOnce) {
156 char X = 'X';
157 size_t len = sizeof(X);
158 ASSERT_TRUE(socketpair[0]->Write(&X, len).Success());
159
160 MainLoop loop;
161 Status error;
162 auto handle = loop.RegisterReadObject(
163 object_sp: socketpair[1],
164 callback: [&](MainLoopBase &loop) {
165 // Add one pending callback on the first iteration.
166 if (callback_count == 0) {
167 loop.AddPendingCallback(callback: [&](MainLoopBase &loop) {
168 callback_count++;
169 });
170 }
171 // Terminate the loop on second iteration.
172 if (callback_count++ >= 1)
173 loop.RequestTermination();
174 },
175 error);
176 ASSERT_TRUE(error.Success());
177 ASSERT_TRUE(handle);
178 ASSERT_TRUE(loop.Run().Success());
179 // 2 iterations of read callback + 1 call of pending callback.
180 ASSERT_EQ(3u, callback_count);
181}
182
183TEST_F(MainLoopTest, PendingCallbackTrigger) {
184 MainLoop loop;
185 std::promise<void> add_callback2;
186 bool callback1_called = false;
187 loop.AddPendingCallback(callback: [&](MainLoopBase &loop) {
188 callback1_called = true;
189 add_callback2.set_value();
190 });
191 Status error;
192 ASSERT_THAT_ERROR(error.ToError(), llvm::Succeeded());
193 bool callback2_called = false;
194 std::thread callback2_adder([&]() {
195 add_callback2.get_future().get();
196 loop.AddPendingCallback(callback: [&](MainLoopBase &loop) {
197 callback2_called = true;
198 loop.RequestTermination();
199 });
200 });
201 ASSERT_THAT_ERROR(loop.Run().ToError(), llvm::Succeeded());
202 callback2_adder.join();
203 ASSERT_TRUE(callback1_called);
204 ASSERT_TRUE(callback2_called);
205}
206
207TEST_F(MainLoopTest, ManyPendingCallbacks) {
208 MainLoop loop;
209 Status error;
210 // Try to fill up the pipe buffer and make sure bad things don't happen. This
211 // is a regression test for the case where writing to the interrupt pipe
212 // caused a deadlock when the pipe filled up (either because the main loop was
213 // not running, because it was slow, or because it was busy/blocked doing
214 // something else).
215 for (int i = 0; i < 65536; ++i)
216 loop.AddPendingCallback(
217 callback: [&](MainLoopBase &loop) { loop.RequestTermination(); });
218 ASSERT_TRUE(loop.Run().Success());
219}
220
221TEST_F(MainLoopTest, CallbackWithTimeout) {
222 MainLoop loop;
223 loop.AddCallback(callback: [](MainLoopBase &loop) { loop.RequestTermination(); },
224 delay: std::chrono::seconds(2));
225 auto start = std::chrono::steady_clock::now();
226 ASSERT_THAT_ERROR(loop.Run().takeError(), llvm::Succeeded());
227 EXPECT_GE(std::chrono::steady_clock::now() - start, std::chrono::seconds(2));
228}
229
230TEST_F(MainLoopTest, TimedCallbacksRunInOrder) {
231 MainLoop loop;
232 auto start = std::chrono::steady_clock::now();
233 std::chrono::milliseconds epsilon(10);
234 std::vector<int> order;
235 auto add_cb = [&](int id) {
236 loop.AddCallback(callback: [&order, id](MainLoopBase &) { order.push_back(x: id); },
237 point: start + id * epsilon);
238 };
239 add_cb(3);
240 add_cb(2);
241 add_cb(4);
242 add_cb(1);
243 loop.AddCallback(callback: [](MainLoopBase &loop) { loop.RequestTermination(); },
244 point: start + 5 * epsilon);
245 ASSERT_THAT_ERROR(loop.Run().takeError(), llvm::Succeeded());
246 EXPECT_GE(std::chrono::steady_clock::now() - start, 5 * epsilon);
247 ASSERT_THAT(order, testing::ElementsAre(1, 2, 3, 4));
248}
249
250TEST_F(MainLoopTest, TimedCallbackShortensSleep) {
251 MainLoop loop;
252 auto start = std::chrono::steady_clock::now();
253 bool long_callback_called = false;
254 loop.AddCallback(
255 callback: [&](MainLoopBase &loop) {
256 long_callback_called = true;
257 loop.RequestTermination();
258 },
259 delay: std::chrono::seconds(30));
260 std::future<Status> async_run =
261 std::async(policy: std::launch::async, fn: &MainLoop::Run, args: std::ref(t&: loop));
262 std::this_thread::sleep_for(rtime: std::chrono::milliseconds(100));
263 bool short_callback_called = false;
264 loop.AddCallback(
265 callback: [&](MainLoopBase &loop) {
266 short_callback_called = true;
267 loop.RequestTermination();
268 },
269 delay: std::chrono::seconds(1));
270 ASSERT_THAT_ERROR(async_run.get().takeError(), llvm::Succeeded());
271 EXPECT_LT(std::chrono::steady_clock::now() - start, std::chrono::seconds(10));
272 EXPECT_TRUE(short_callback_called);
273 EXPECT_FALSE(long_callback_called);
274}
275
276#ifdef LLVM_ON_UNIX
277TEST_F(MainLoopTest, DetectsEOF) {
278
279 PseudoTerminal term;
280 ASSERT_THAT_ERROR(term.OpenFirstAvailablePrimary(O_RDWR), llvm::Succeeded());
281 ASSERT_THAT_ERROR(term.OpenSecondary(O_RDWR | O_NOCTTY), llvm::Succeeded());
282 auto conn = std::make_unique<ConnectionFileDescriptor>(
283 args: term.ReleasePrimaryFileDescriptor(), args: true);
284
285 Status error;
286 MainLoop loop;
287 auto handle =
288 loop.RegisterReadObject(object_sp: conn->GetReadObject(), callback: make_callback(), error);
289 ASSERT_TRUE(error.Success());
290 term.CloseSecondaryFileDescriptor();
291
292 ASSERT_TRUE(loop.Run().Success());
293 ASSERT_EQ(1u, callback_count);
294}
295
296TEST_F(MainLoopTest, Signal) {
297 MainLoop loop;
298 Status error;
299
300 auto handle = loop.RegisterSignal(SIGUSR1, callback: make_callback(), error);
301 ASSERT_TRUE(error.Success());
302 kill(pid: getpid(), SIGUSR1);
303 ASSERT_TRUE(loop.Run().Success());
304 ASSERT_EQ(1u, callback_count);
305}
306
307TEST_F(MainLoopTest, SignalOnOtherThread) {
308 MainLoop loop;
309 Status error;
310
311 auto handle = loop.RegisterSignal(SIGUSR1, callback: make_callback(), error);
312 ASSERT_TRUE(error.Success());
313 std::thread([] { pthread_kill(threadid: pthread_self(), SIGUSR1); }).join();
314 ASSERT_TRUE(loop.Run().Success());
315 ASSERT_EQ(1u, callback_count);
316}
317
318// Test that a signal which is not monitored by the MainLoop does not
319// cause a premature exit.
320TEST_F(MainLoopTest, UnmonitoredSignal) {
321 MainLoop loop;
322 Status error;
323 struct sigaction sa;
324 sa.sa_sigaction = [](int, siginfo_t *, void *) { };
325 sa.sa_flags = SA_SIGINFO; // important: no SA_RESTART
326 sigemptyset(set: &sa.sa_mask);
327 ASSERT_EQ(0, sigaction(SIGUSR2, &sa, nullptr));
328
329 auto handle = loop.RegisterSignal(SIGUSR1, callback: make_callback(), error);
330 ASSERT_TRUE(error.Success());
331 kill(pid: getpid(), SIGUSR2);
332 kill(pid: getpid(), SIGUSR1);
333 ASSERT_TRUE(loop.Run().Success());
334 ASSERT_EQ(1u, callback_count);
335}
336
337// Test that two callbacks can be registered for the same signal
338// and unregistered independently.
339TEST_F(MainLoopTest, TwoSignalCallbacks) {
340 MainLoop loop;
341 Status error;
342 unsigned callback2_count = 0;
343 unsigned callback3_count = 0;
344
345 auto handle = loop.RegisterSignal(SIGUSR1, callback: make_callback(), error);
346 ASSERT_TRUE(error.Success());
347
348 {
349 // Run a single iteration with two callbacks enabled.
350 auto handle2 = loop.RegisterSignal(
351 SIGUSR1, callback: [&](MainLoopBase &loop) { ++callback2_count; }, error);
352 ASSERT_TRUE(error.Success());
353
354 kill(pid: getpid(), SIGUSR1);
355 ASSERT_TRUE(loop.Run().Success());
356 ASSERT_EQ(1u, callback_count);
357 ASSERT_EQ(1u, callback2_count);
358 ASSERT_EQ(0u, callback3_count);
359 }
360
361 {
362 // Make sure that remove + add new works.
363 auto handle3 = loop.RegisterSignal(
364 SIGUSR1, callback: [&](MainLoopBase &loop) { ++callback3_count; }, error);
365 ASSERT_TRUE(error.Success());
366
367 kill(pid: getpid(), SIGUSR1);
368 ASSERT_TRUE(loop.Run().Success());
369 ASSERT_EQ(2u, callback_count);
370 ASSERT_EQ(1u, callback2_count);
371 ASSERT_EQ(1u, callback3_count);
372 }
373
374 // Both extra callbacks should be unregistered now.
375 kill(pid: getpid(), SIGUSR1);
376 ASSERT_TRUE(loop.Run().Success());
377 ASSERT_EQ(3u, callback_count);
378 ASSERT_EQ(1u, callback2_count);
379 ASSERT_EQ(1u, callback3_count);
380}
381#endif
382

source code of lldb/unittests/Host/MainLoopTest.cpp