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