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