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