xref: /llvm-project/lldb/unittests/Host/MainLoopTest.cpp (revision 244fcecb90fa7a3fb710ca5768d3bae9af5868cc)
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("localhost:0", 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         llvm::formatv("localhost:{0}", 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(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(socketpair[1], 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(socketpair[0], make_callback(), error);
90   ASSERT_TRUE(error.Success());
91   auto handle1 = loop.RegisterReadObject(socketpair[1], 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       socketpair[1],
107       [&](MainLoopBase &loop) {
108         // Both callbacks should be called before the loop terminates.
109         loop.AddPendingCallback(make_callback());
110         loop.AddPendingCallback(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       socketpair[1],
129       [&](MainLoopBase &loop) {
130         // Add one pending callback on the first iteration.
131         if (callback_count == 0) {
132           loop.AddPendingCallback([&](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([&](MainLoopBase &loop) {
153     callback1_called = true;
154     add_callback2.set_value();
155   });
156   Status error;
157   auto socket_handle = loop.RegisterReadObject(
158       socketpair[1], [](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([&](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([&](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       term.ReleasePrimaryFileDescriptor(), true);
194 
195   Status error;
196   MainLoop loop;
197   auto handle =
198       loop.RegisterReadObject(conn->GetReadObject(), 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, make_callback(), error);
211   ASSERT_TRUE(error.Success());
212   kill(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(&sa.sa_mask);
226   ASSERT_EQ(0, sigaction(SIGUSR2, &sa, nullptr));
227 
228   auto handle = loop.RegisterSignal(SIGUSR1, make_callback(), error);
229   ASSERT_TRUE(error.Success());
230   kill(getpid(), SIGUSR2);
231   kill(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, 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, [&](MainLoopBase &loop) { ++callback2_count; }, error);
251     ASSERT_TRUE(error.Success());
252 
253     kill(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, [&](MainLoopBase &loop) { ++callback3_count; }, error);
264     ASSERT_TRUE(error.Success());
265 
266     kill(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(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