180814287SRaphael Isemann //===-- MainLoopTest.cpp --------------------------------------------------===//
24f1f6417SPavel Labath //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
64f1f6417SPavel Labath //
74f1f6417SPavel Labath //===----------------------------------------------------------------------===//
84f1f6417SPavel Labath
94f1f6417SPavel Labath #include "lldb/Host/MainLoop.h"
105dca0596SRaphael Isemann #include "TestingSupport/SubsystemRAII.h"
11390b4879SPavel Labath #include "lldb/Host/ConnectionFileDescriptor.h"
12390b4879SPavel Labath #include "lldb/Host/PseudoTerminal.h"
134f1f6417SPavel Labath #include "lldb/Host/common/TCPSocket.h"
14f8a74c18SAaron Smith #include "llvm/Testing/Support/Error.h"
154f1f6417SPavel Labath #include "gtest/gtest.h"
164f1f6417SPavel Labath #include <future>
174f1f6417SPavel Labath
184f1f6417SPavel Labath using namespace lldb_private;
194f1f6417SPavel Labath
204f1f6417SPavel Labath namespace {
214f1f6417SPavel Labath class MainLoopTest : public testing::Test {
224f1f6417SPavel Labath public:
235dca0596SRaphael Isemann SubsystemRAII<Socket> subsystems;
244f1f6417SPavel Labath
SetUp()254f1f6417SPavel Labath void SetUp() override {
264f1f6417SPavel Labath bool child_processes_inherit = false;
2797206d57SZachary Turner Status error;
284f1f6417SPavel Labath std::unique_ptr<TCPSocket> listen_socket_up(
294f1f6417SPavel Labath new TCPSocket(true, child_processes_inherit));
304f1f6417SPavel Labath ASSERT_TRUE(error.Success());
314f1f6417SPavel Labath error = listen_socket_up->Listen("localhost:0", 5);
324f1f6417SPavel Labath ASSERT_TRUE(error.Success());
334f1f6417SPavel Labath
344f1f6417SPavel Labath Socket *accept_socket;
3597206d57SZachary Turner std::future<Status> accept_error = std::async(std::launch::async, [&] {
364f1f6417SPavel Labath return listen_socket_up->Accept(accept_socket);
374f1f6417SPavel Labath });
384f1f6417SPavel Labath
394f1f6417SPavel Labath std::unique_ptr<TCPSocket> connect_socket_up(
404f1f6417SPavel Labath new TCPSocket(true, child_processes_inherit));
414f1f6417SPavel Labath error = connect_socket_up->Connect(
424f1f6417SPavel Labath llvm::formatv("localhost:{0}", listen_socket_up->GetLocalPortNumber())
434f1f6417SPavel Labath .str());
444f1f6417SPavel Labath ASSERT_TRUE(error.Success());
454f1f6417SPavel Labath ASSERT_TRUE(accept_error.get().Success());
464f1f6417SPavel Labath
474f1f6417SPavel Labath callback_count = 0;
484f1f6417SPavel Labath socketpair[0] = std::move(connect_socket_up);
494f1f6417SPavel Labath socketpair[1].reset(accept_socket);
504f1f6417SPavel Labath }
514f1f6417SPavel Labath
TearDown()524f1f6417SPavel Labath void TearDown() override {
534f1f6417SPavel Labath socketpair[0].reset();
544f1f6417SPavel Labath socketpair[1].reset();
554f1f6417SPavel Labath }
564f1f6417SPavel Labath
574f1f6417SPavel Labath protected:
make_callback()584f1f6417SPavel Labath MainLoop::Callback make_callback() {
594f1f6417SPavel Labath return [&](MainLoopBase &loop) {
604f1f6417SPavel Labath ++callback_count;
614f1f6417SPavel Labath loop.RequestTermination();
624f1f6417SPavel Labath };
634f1f6417SPavel Labath }
644f1f6417SPavel Labath std::shared_ptr<Socket> socketpair[2];
654f1f6417SPavel Labath unsigned callback_count;
664f1f6417SPavel Labath };
674f1f6417SPavel Labath } // namespace
684f1f6417SPavel Labath
TEST_F(MainLoopTest,ReadObject)694f1f6417SPavel Labath TEST_F(MainLoopTest, ReadObject) {
704f1f6417SPavel Labath char X = 'X';
714f1f6417SPavel Labath size_t len = sizeof(X);
724f1f6417SPavel Labath ASSERT_TRUE(socketpair[0]->Write(&X, len).Success());
734f1f6417SPavel Labath
744f1f6417SPavel Labath MainLoop loop;
754f1f6417SPavel Labath
7697206d57SZachary Turner Status error;
774f1f6417SPavel Labath auto handle = loop.RegisterReadObject(socketpair[1], make_callback(), error);
784f1f6417SPavel Labath ASSERT_TRUE(error.Success());
794f1f6417SPavel Labath ASSERT_TRUE(handle);
804f1f6417SPavel Labath ASSERT_TRUE(loop.Run().Success());
814f1f6417SPavel Labath ASSERT_EQ(1u, callback_count);
824f1f6417SPavel Labath }
834f1f6417SPavel Labath
TEST_F(MainLoopTest,TerminatesImmediately)844f1f6417SPavel Labath TEST_F(MainLoopTest, TerminatesImmediately) {
854f1f6417SPavel Labath char X = 'X';
864f1f6417SPavel Labath size_t len = sizeof(X);
874f1f6417SPavel Labath ASSERT_TRUE(socketpair[0]->Write(&X, len).Success());
884f1f6417SPavel Labath ASSERT_TRUE(socketpair[1]->Write(&X, len).Success());
894f1f6417SPavel Labath
904f1f6417SPavel Labath MainLoop loop;
9197206d57SZachary Turner Status error;
924f1f6417SPavel Labath auto handle0 = loop.RegisterReadObject(socketpair[0], make_callback(), error);
934f1f6417SPavel Labath ASSERT_TRUE(error.Success());
944f1f6417SPavel Labath auto handle1 = loop.RegisterReadObject(socketpair[1], make_callback(), error);
954f1f6417SPavel Labath ASSERT_TRUE(error.Success());
964f1f6417SPavel Labath
974f1f6417SPavel Labath ASSERT_TRUE(loop.Run().Success());
984f1f6417SPavel Labath ASSERT_EQ(1u, callback_count);
994f1f6417SPavel Labath }
1004f1f6417SPavel Labath
TEST_F(MainLoopTest,PendingCallback)101*5b04eb23SMichał Górny TEST_F(MainLoopTest, PendingCallback) {
102*5b04eb23SMichał Górny char X = 'X';
103*5b04eb23SMichał Górny size_t len = sizeof(X);
104*5b04eb23SMichał Górny ASSERT_TRUE(socketpair[0]->Write(&X, len).Success());
105*5b04eb23SMichał Górny
106*5b04eb23SMichał Górny MainLoop loop;
107*5b04eb23SMichał Górny Status error;
108*5b04eb23SMichał Górny auto handle = loop.RegisterReadObject(
109*5b04eb23SMichał Górny socketpair[1],
110*5b04eb23SMichał Górny [&](MainLoopBase &loop) {
111*5b04eb23SMichał Górny // Both callbacks should be called before the loop terminates.
112*5b04eb23SMichał Górny loop.AddPendingCallback(make_callback());
113*5b04eb23SMichał Górny loop.AddPendingCallback(make_callback());
114*5b04eb23SMichał Górny loop.RequestTermination();
115*5b04eb23SMichał Górny },
116*5b04eb23SMichał Górny error);
117*5b04eb23SMichał Górny ASSERT_TRUE(error.Success());
118*5b04eb23SMichał Górny ASSERT_TRUE(handle);
119*5b04eb23SMichał Górny ASSERT_TRUE(loop.Run().Success());
120*5b04eb23SMichał Górny ASSERT_EQ(2u, callback_count);
121*5b04eb23SMichał Górny }
122*5b04eb23SMichał Górny
TEST_F(MainLoopTest,PendingCallbackCalledOnlyOnce)123*5b04eb23SMichał Górny TEST_F(MainLoopTest, PendingCallbackCalledOnlyOnce) {
124*5b04eb23SMichał Górny char X = 'X';
125*5b04eb23SMichał Górny size_t len = sizeof(X);
126*5b04eb23SMichał Górny ASSERT_TRUE(socketpair[0]->Write(&X, len).Success());
127*5b04eb23SMichał Górny
128*5b04eb23SMichał Górny MainLoop loop;
129*5b04eb23SMichał Górny Status error;
130*5b04eb23SMichał Górny auto handle = loop.RegisterReadObject(
131*5b04eb23SMichał Górny socketpair[1],
132*5b04eb23SMichał Górny [&](MainLoopBase &loop) {
133*5b04eb23SMichał Górny // Add one pending callback on the first iteration.
134*5b04eb23SMichał Górny if (callback_count == 0) {
135*5b04eb23SMichał Górny loop.AddPendingCallback([&](MainLoopBase &loop) {
136*5b04eb23SMichał Górny callback_count++;
137*5b04eb23SMichał Górny });
138*5b04eb23SMichał Górny }
139*5b04eb23SMichał Górny // Terminate the loop on second iteration.
140*5b04eb23SMichał Górny if (callback_count++ >= 1)
141*5b04eb23SMichał Górny loop.RequestTermination();
142*5b04eb23SMichał Górny },
143*5b04eb23SMichał Górny error);
144*5b04eb23SMichał Górny ASSERT_TRUE(error.Success());
145*5b04eb23SMichał Górny ASSERT_TRUE(handle);
146*5b04eb23SMichał Górny ASSERT_TRUE(loop.Run().Success());
147*5b04eb23SMichał Górny // 2 iterations of read callback + 1 call of pending callback.
148*5b04eb23SMichał Górny ASSERT_EQ(3u, callback_count);
149*5b04eb23SMichał Górny }
150*5b04eb23SMichał Górny
1514f1f6417SPavel Labath #ifdef LLVM_ON_UNIX
TEST_F(MainLoopTest,DetectsEOF)152390b4879SPavel Labath TEST_F(MainLoopTest, DetectsEOF) {
15301486b22SMichal Gorny
15407d6f881SPavel Labath PseudoTerminal term;
1556bb123b8SPavel Labath ASSERT_THAT_ERROR(term.OpenFirstAvailablePrimary(O_RDWR), llvm::Succeeded());
156daae4a84SPavel Labath ASSERT_THAT_ERROR(term.OpenSecondary(O_RDWR | O_NOCTTY), llvm::Succeeded());
157a8f3ae7cSJonas Devlieghere auto conn = std::make_unique<ConnectionFileDescriptor>(
15864ec505dSJonas Devlieghere term.ReleasePrimaryFileDescriptor(), true);
159390b4879SPavel Labath
160390b4879SPavel Labath Status error;
161390b4879SPavel Labath MainLoop loop;
162390b4879SPavel Labath auto handle =
163390b4879SPavel Labath loop.RegisterReadObject(conn->GetReadObject(), make_callback(), error);
164390b4879SPavel Labath ASSERT_TRUE(error.Success());
16564ec505dSJonas Devlieghere term.CloseSecondaryFileDescriptor();
166390b4879SPavel Labath
167390b4879SPavel Labath ASSERT_TRUE(loop.Run().Success());
168390b4879SPavel Labath ASSERT_EQ(1u, callback_count);
169390b4879SPavel Labath }
170390b4879SPavel Labath
TEST_F(MainLoopTest,Signal)1714f1f6417SPavel Labath TEST_F(MainLoopTest, Signal) {
1724f1f6417SPavel Labath MainLoop loop;
17397206d57SZachary Turner Status error;
1744f1f6417SPavel Labath
1754f1f6417SPavel Labath auto handle = loop.RegisterSignal(SIGUSR1, make_callback(), error);
1764f1f6417SPavel Labath ASSERT_TRUE(error.Success());
1774f1f6417SPavel Labath kill(getpid(), SIGUSR1);
1784f1f6417SPavel Labath ASSERT_TRUE(loop.Run().Success());
1794f1f6417SPavel Labath ASSERT_EQ(1u, callback_count);
1804f1f6417SPavel Labath }
18153eabaabSMichal Gorny
18253eabaabSMichal Gorny // Test that a signal which is not monitored by the MainLoop does not
18353eabaabSMichal Gorny // cause a premature exit.
TEST_F(MainLoopTest,UnmonitoredSignal)18453eabaabSMichal Gorny TEST_F(MainLoopTest, UnmonitoredSignal) {
18553eabaabSMichal Gorny MainLoop loop;
18653eabaabSMichal Gorny Status error;
18753eabaabSMichal Gorny struct sigaction sa;
18853eabaabSMichal Gorny sa.sa_sigaction = [](int, siginfo_t *, void *) { };
18953eabaabSMichal Gorny sa.sa_flags = SA_SIGINFO; // important: no SA_RESTART
19053eabaabSMichal Gorny sigemptyset(&sa.sa_mask);
19153eabaabSMichal Gorny ASSERT_EQ(0, sigaction(SIGUSR2, &sa, nullptr));
19253eabaabSMichal Gorny
19353eabaabSMichal Gorny auto handle = loop.RegisterSignal(SIGUSR1, make_callback(), error);
19453eabaabSMichal Gorny ASSERT_TRUE(error.Success());
19553eabaabSMichal Gorny std::thread killer([]() {
19653eabaabSMichal Gorny sleep(1);
19753eabaabSMichal Gorny kill(getpid(), SIGUSR2);
19853eabaabSMichal Gorny sleep(1);
19953eabaabSMichal Gorny kill(getpid(), SIGUSR1);
20053eabaabSMichal Gorny });
20153eabaabSMichal Gorny ASSERT_TRUE(loop.Run().Success());
20253eabaabSMichal Gorny killer.join();
20353eabaabSMichal Gorny ASSERT_EQ(1u, callback_count);
20453eabaabSMichal Gorny }
20508ce2ba5SMichał Górny
20608ce2ba5SMichał Górny // Test that two callbacks can be registered for the same signal
20708ce2ba5SMichał Górny // and unregistered independently.
TEST_F(MainLoopTest,TwoSignalCallbacks)20808ce2ba5SMichał Górny TEST_F(MainLoopTest, TwoSignalCallbacks) {
20908ce2ba5SMichał Górny MainLoop loop;
21008ce2ba5SMichał Górny Status error;
211034c73d4SRaphael Isemann unsigned callback2_count = 0;
212034c73d4SRaphael Isemann unsigned callback3_count = 0;
21308ce2ba5SMichał Górny
21408ce2ba5SMichał Górny auto handle = loop.RegisterSignal(SIGUSR1, make_callback(), error);
21508ce2ba5SMichał Górny ASSERT_TRUE(error.Success());
21608ce2ba5SMichał Górny
21708ce2ba5SMichał Górny {
21808ce2ba5SMichał Górny // Run a single iteration with two callbacks enabled.
21908ce2ba5SMichał Górny auto handle2 = loop.RegisterSignal(
22008ce2ba5SMichał Górny SIGUSR1, [&](MainLoopBase &loop) { ++callback2_count; }, error);
22108ce2ba5SMichał Górny ASSERT_TRUE(error.Success());
22208ce2ba5SMichał Górny
22308ce2ba5SMichał Górny kill(getpid(), SIGUSR1);
22408ce2ba5SMichał Górny ASSERT_TRUE(loop.Run().Success());
22508ce2ba5SMichał Górny ASSERT_EQ(1u, callback_count);
22608ce2ba5SMichał Górny ASSERT_EQ(1u, callback2_count);
22708ce2ba5SMichał Górny ASSERT_EQ(0u, callback3_count);
22808ce2ba5SMichał Górny }
22908ce2ba5SMichał Górny
23008ce2ba5SMichał Górny {
23108ce2ba5SMichał Górny // Make sure that remove + add new works.
23208ce2ba5SMichał Górny auto handle3 = loop.RegisterSignal(
23308ce2ba5SMichał Górny SIGUSR1, [&](MainLoopBase &loop) { ++callback3_count; }, error);
23408ce2ba5SMichał Górny ASSERT_TRUE(error.Success());
23508ce2ba5SMichał Górny
23608ce2ba5SMichał Górny kill(getpid(), SIGUSR1);
23708ce2ba5SMichał Górny ASSERT_TRUE(loop.Run().Success());
23808ce2ba5SMichał Górny ASSERT_EQ(2u, callback_count);
23908ce2ba5SMichał Górny ASSERT_EQ(1u, callback2_count);
24008ce2ba5SMichał Górny ASSERT_EQ(1u, callback3_count);
24108ce2ba5SMichał Górny }
24208ce2ba5SMichał Górny
24308ce2ba5SMichał Górny // Both extra callbacks should be unregistered now.
24408ce2ba5SMichał Górny kill(getpid(), SIGUSR1);
24508ce2ba5SMichał Górny ASSERT_TRUE(loop.Run().Success());
24608ce2ba5SMichał Górny ASSERT_EQ(3u, callback_count);
24708ce2ba5SMichał Górny ASSERT_EQ(1u, callback2_count);
24808ce2ba5SMichał Górny ASSERT_EQ(1u, callback3_count);
24908ce2ba5SMichał Górny }
2504f1f6417SPavel Labath #endif
251