1 //===-- MainLoopTest.cpp --------------------------------------------------===//
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
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/Testing/Support/Error.h"
16 #include "gtest/gtest.h"
19 using namespace lldb_private
;
22 class MainLoopTest
: public testing::Test
{
24 SubsystemRAII
<FileSystem
, Socket
> subsystems
;
26 void SetUp() override
{
27 bool child_processes_inherit
= false;
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());
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())
41 ASSERT_TRUE(error
.Success());
42 ASSERT_TRUE(listen_socket_up
->Accept(accept_socket
).Success());
45 socketpair
[0] = std::move(connect_socket_up
);
46 socketpair
[1].reset(accept_socket
);
49 void TearDown() override
{
50 socketpair
[0].reset();
51 socketpair
[1].reset();
55 MainLoop::Callback
make_callback() {
56 return [&](MainLoopBase
&loop
) {
58 loop
.RequestTermination();
61 std::shared_ptr
<Socket
> socketpair
[2];
62 unsigned callback_count
;
66 TEST_F(MainLoopTest
, ReadObject
) {
68 size_t len
= sizeof(X
);
69 ASSERT_TRUE(socketpair
[0]->Write(&X
, len
).Success());
74 auto handle
= loop
.RegisterReadObject(socketpair
[1], make_callback(), error
);
75 ASSERT_TRUE(error
.Success());
77 ASSERT_TRUE(loop
.Run().Success());
78 ASSERT_EQ(1u, callback_count
);
81 TEST_F(MainLoopTest
, TerminatesImmediately
) {
83 size_t len
= sizeof(X
);
84 ASSERT_TRUE(socketpair
[0]->Write(&X
, len
).Success());
85 ASSERT_TRUE(socketpair
[1]->Write(&X
, len
).Success());
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());
94 ASSERT_TRUE(loop
.Run().Success());
95 ASSERT_EQ(1u, callback_count
);
98 TEST_F(MainLoopTest
, PendingCallback
) {
100 size_t len
= sizeof(X
);
101 ASSERT_TRUE(socketpair
[0]->Write(&X
, len
).Success());
105 auto handle
= loop
.RegisterReadObject(
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();
114 ASSERT_TRUE(error
.Success());
116 ASSERT_TRUE(loop
.Run().Success());
117 ASSERT_EQ(2u, callback_count
);
120 TEST_F(MainLoopTest
, PendingCallbackCalledOnlyOnce
) {
122 size_t len
= sizeof(X
);
123 ASSERT_TRUE(socketpair
[0]->Write(&X
, len
).Success());
127 auto handle
= loop
.RegisterReadObject(
129 [&](MainLoopBase
&loop
) {
130 // Add one pending callback on the first iteration.
131 if (callback_count
== 0) {
132 loop
.AddPendingCallback([&](MainLoopBase
&loop
) {
136 // Terminate the loop on second iteration.
137 if (callback_count
++ >= 1)
138 loop
.RequestTermination();
141 ASSERT_TRUE(error
.Success());
143 ASSERT_TRUE(loop
.Run().Success());
144 // 2 iterations of read callback + 1 call of pending callback.
145 ASSERT_EQ(3u, callback_count
);
148 TEST_F(MainLoopTest
, PendingCallbackTrigger
) {
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();
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();
169 ASSERT_THAT_ERROR(loop
.Run().ToError(), llvm::Succeeded());
170 callback2_adder
.join();
171 ASSERT_TRUE(callback1_called
);
172 ASSERT_TRUE(callback2_called
);
175 // Regression test for assertion failure if a lot of callbacks end up
176 // being queued after loop exits.
177 TEST_F(MainLoopTest
, PendingCallbackAfterLoopExited
) {
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
) {});
187 TEST_F(MainLoopTest
, DetectsEOF
) {
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);
198 loop
.RegisterReadObject(conn
->GetReadObject(), make_callback(), error
);
199 ASSERT_TRUE(error
.Success());
200 term
.CloseSecondaryFileDescriptor();
202 ASSERT_TRUE(loop
.Run().Success());
203 ASSERT_EQ(1u, callback_count
);
206 TEST_F(MainLoopTest
, Signal
) {
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
);
217 // Test that a signal which is not monitored by the MainLoop does not
218 // cause a premature exit.
219 TEST_F(MainLoopTest
, UnmonitoredSignal
) {
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));
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
);
236 // Test that two callbacks can be registered for the same signal
237 // and unregistered independently.
238 TEST_F(MainLoopTest
, TwoSignalCallbacks
) {
241 unsigned callback2_count
= 0;
242 unsigned callback3_count
= 0;
244 auto handle
= loop
.RegisterSignal(SIGUSR1
, make_callback(), error
);
245 ASSERT_TRUE(error
.Success());
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());
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
);
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());
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
);
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
);