xref: /llvm-project/lldb/unittests/Host/MainLoopTest.cpp (revision c1dff7152592f1beee9059ee8e2cb3cc68baea4d)
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 
22 using namespace lldb_private;
23 
24 namespace {
25 class MainLoopTest : public testing::Test {
26 public:
27   SubsystemRAII<FileSystem, Socket> subsystems;
28 
29   void SetUp() override {
30     Status error;
31     auto listen_socket_up = std::make_unique<TCPSocket>(true);
32     ASSERT_TRUE(error.Success());
33     error = listen_socket_up->Listen("localhost:0", 5);
34     ASSERT_TRUE(error.Success());
35 
36     Socket *accept_socket;
37     auto connect_socket_up = std::make_unique<TCPSocket>(true);
38     error = connect_socket_up->Connect(
39         llvm::formatv("localhost:{0}", 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(accept_socket);
48   }
49 
50   void TearDown() override {
51     socketpair[0].reset();
52     socketpair[1].reset();
53   }
54 
55 protected:
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 
67 TEST_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(socketpair[1], 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 
82 TEST_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       socketpair[1],
93       [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([](MainLoopBase &loop) { loop.RequestTermination(); },
109                    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 
116 TEST_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(socketpair[0], make_callback(), error);
125   ASSERT_TRUE(error.Success());
126   auto handle1 = loop.RegisterReadObject(socketpair[1], make_callback(), error);
127   ASSERT_TRUE(error.Success());
128 
129   ASSERT_TRUE(loop.Run().Success());
130   ASSERT_EQ(1u, callback_count);
131 }
132 
133 TEST_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       socketpair[1],
142       [&](MainLoopBase &loop) {
143         // Both callbacks should be called before the loop terminates.
144         loop.AddPendingCallback(make_callback());
145         loop.AddPendingCallback(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 
155 TEST_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       socketpair[1],
164       [&](MainLoopBase &loop) {
165         // Add one pending callback on the first iteration.
166         if (callback_count == 0) {
167           loop.AddPendingCallback([&](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 
183 TEST_F(MainLoopTest, PendingCallbackTrigger) {
184   MainLoop loop;
185   std::promise<void> add_callback2;
186   bool callback1_called = false;
187   loop.AddPendingCallback([&](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([&](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 
207 TEST_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         [&](MainLoopBase &loop) { loop.RequestTermination(); });
218   ASSERT_TRUE(loop.Run().Success());
219 }
220 
221 TEST_F(MainLoopTest, CallbackWithTimeout) {
222   MainLoop loop;
223   loop.AddCallback([](MainLoopBase &loop) { loop.RequestTermination(); },
224                    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 
230 TEST_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([&order, id](MainLoopBase &) { order.push_back(id); },
237                      start + id * epsilon);
238   };
239   add_cb(3);
240   add_cb(2);
241   add_cb(4);
242   add_cb(1);
243   loop.AddCallback([](MainLoopBase &loop) { loop.RequestTermination(); },
244                    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 
250 TEST_F(MainLoopTest, TimedCallbackShortensSleep) {
251   MainLoop loop;
252   auto start = std::chrono::steady_clock::now();
253   bool long_callback_called = false;
254   loop.AddCallback(
255       [&](MainLoopBase &loop) {
256         long_callback_called = true;
257         loop.RequestTermination();
258       },
259       std::chrono::seconds(30));
260   std::future<Status> async_run =
261       std::async(std::launch::async, &MainLoop::Run, std::ref(loop));
262   std::this_thread::sleep_for(std::chrono::milliseconds(100));
263   bool short_callback_called = false;
264   loop.AddCallback(
265       [&](MainLoopBase &loop) {
266         short_callback_called = true;
267         loop.RequestTermination();
268       },
269       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
277 TEST_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       term.ReleasePrimaryFileDescriptor(), true);
284 
285   Status error;
286   MainLoop loop;
287   auto handle =
288       loop.RegisterReadObject(conn->GetReadObject(), 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 
296 TEST_F(MainLoopTest, Signal) {
297   MainLoop loop;
298   Status error;
299 
300   auto handle = loop.RegisterSignal(SIGUSR1, make_callback(), error);
301   ASSERT_TRUE(error.Success());
302   kill(getpid(), SIGUSR1);
303   ASSERT_TRUE(loop.Run().Success());
304   ASSERT_EQ(1u, callback_count);
305 }
306 
307 TEST_F(MainLoopTest, SignalOnOtherThread) {
308   MainLoop loop;
309   Status error;
310 
311   auto handle = loop.RegisterSignal(SIGUSR1, make_callback(), error);
312   ASSERT_TRUE(error.Success());
313   std::thread([] { pthread_kill(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.
320 TEST_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(&sa.sa_mask);
327   ASSERT_EQ(0, sigaction(SIGUSR2, &sa, nullptr));
328 
329   auto handle = loop.RegisterSignal(SIGUSR1, make_callback(), error);
330   ASSERT_TRUE(error.Success());
331   kill(getpid(), SIGUSR2);
332   kill(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.
339 TEST_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, 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, [&](MainLoopBase &loop) { ++callback2_count; }, error);
352     ASSERT_TRUE(error.Success());
353 
354     kill(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, [&](MainLoopBase &loop) { ++callback3_count; }, error);
365     ASSERT_TRUE(error.Success());
366 
367     kill(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(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