1 //===-- MainLoopTest.cpp ----------------------------------------*- C++ -*-===// 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 "lldb/Host/ConnectionFileDescriptor.h" 11 #include "lldb/Host/PseudoTerminal.h" 12 #include "lldb/Host/common/TCPSocket.h" 13 #include "llvm/Testing/Support/Error.h" 14 #include "gtest/gtest.h" 15 #include <future> 16 17 using namespace lldb_private; 18 19 namespace { 20 class MainLoopTest : public testing::Test { 21 public: 22 static void SetUpTestCase() { 23 ASSERT_THAT_ERROR(Socket::Initialize(), llvm::Succeeded()); 24 } 25 26 static void TearDownTestCase() { Socket::Terminate(); } 27 28 void SetUp() override { 29 bool child_processes_inherit = false; 30 Status error; 31 std::unique_ptr<TCPSocket> listen_socket_up( 32 new TCPSocket(true, child_processes_inherit)); 33 ASSERT_TRUE(error.Success()); 34 error = listen_socket_up->Listen("localhost:0", 5); 35 ASSERT_TRUE(error.Success()); 36 37 Socket *accept_socket; 38 std::future<Status> accept_error = std::async(std::launch::async, [&] { 39 return listen_socket_up->Accept(accept_socket); 40 }); 41 42 std::unique_ptr<TCPSocket> connect_socket_up( 43 new TCPSocket(true, child_processes_inherit)); 44 error = connect_socket_up->Connect( 45 llvm::formatv("localhost:{0}", listen_socket_up->GetLocalPortNumber()) 46 .str()); 47 ASSERT_TRUE(error.Success()); 48 ASSERT_TRUE(accept_error.get().Success()); 49 50 callback_count = 0; 51 socketpair[0] = std::move(connect_socket_up); 52 socketpair[1].reset(accept_socket); 53 } 54 55 void TearDown() override { 56 socketpair[0].reset(); 57 socketpair[1].reset(); 58 } 59 60 protected: 61 MainLoop::Callback make_callback() { 62 return [&](MainLoopBase &loop) { 63 ++callback_count; 64 loop.RequestTermination(); 65 }; 66 } 67 std::shared_ptr<Socket> socketpair[2]; 68 unsigned callback_count; 69 }; 70 } // namespace 71 72 TEST_F(MainLoopTest, ReadObject) { 73 char X = 'X'; 74 size_t len = sizeof(X); 75 ASSERT_TRUE(socketpair[0]->Write(&X, len).Success()); 76 77 MainLoop loop; 78 79 Status error; 80 auto handle = loop.RegisterReadObject(socketpair[1], make_callback(), error); 81 ASSERT_TRUE(error.Success()); 82 ASSERT_TRUE(handle); 83 ASSERT_TRUE(loop.Run().Success()); 84 ASSERT_EQ(1u, callback_count); 85 } 86 87 TEST_F(MainLoopTest, TerminatesImmediately) { 88 char X = 'X'; 89 size_t len = sizeof(X); 90 ASSERT_TRUE(socketpair[0]->Write(&X, len).Success()); 91 ASSERT_TRUE(socketpair[1]->Write(&X, len).Success()); 92 93 MainLoop loop; 94 Status error; 95 auto handle0 = loop.RegisterReadObject(socketpair[0], make_callback(), error); 96 ASSERT_TRUE(error.Success()); 97 auto handle1 = loop.RegisterReadObject(socketpair[1], make_callback(), error); 98 ASSERT_TRUE(error.Success()); 99 100 ASSERT_TRUE(loop.Run().Success()); 101 ASSERT_EQ(1u, callback_count); 102 } 103 104 #ifdef LLVM_ON_UNIX 105 TEST_F(MainLoopTest, DetectsEOF) { 106 107 PseudoTerminal term; 108 ASSERT_TRUE(term.OpenFirstAvailableMaster(O_RDWR, nullptr, 0)); 109 ASSERT_TRUE(term.OpenSlave(O_RDWR | O_NOCTTY, nullptr, 0)); 110 auto conn = std::make_unique<ConnectionFileDescriptor>( 111 term.ReleaseMasterFileDescriptor(), true); 112 113 Status error; 114 MainLoop loop; 115 auto handle = 116 loop.RegisterReadObject(conn->GetReadObject(), make_callback(), error); 117 ASSERT_TRUE(error.Success()); 118 term.CloseSlaveFileDescriptor(); 119 120 ASSERT_TRUE(loop.Run().Success()); 121 ASSERT_EQ(1u, callback_count); 122 } 123 124 TEST_F(MainLoopTest, Signal) { 125 MainLoop loop; 126 Status error; 127 128 auto handle = loop.RegisterSignal(SIGUSR1, make_callback(), error); 129 ASSERT_TRUE(error.Success()); 130 kill(getpid(), SIGUSR1); 131 ASSERT_TRUE(loop.Run().Success()); 132 ASSERT_EQ(1u, callback_count); 133 } 134 135 // Test that a signal which is not monitored by the MainLoop does not 136 // cause a premature exit. 137 TEST_F(MainLoopTest, UnmonitoredSignal) { 138 MainLoop loop; 139 Status error; 140 struct sigaction sa; 141 sa.sa_sigaction = [](int, siginfo_t *, void *) { }; 142 sa.sa_flags = SA_SIGINFO; // important: no SA_RESTART 143 sigemptyset(&sa.sa_mask); 144 ASSERT_EQ(0, sigaction(SIGUSR2, &sa, nullptr)); 145 146 auto handle = loop.RegisterSignal(SIGUSR1, make_callback(), error); 147 ASSERT_TRUE(error.Success()); 148 std::thread killer([]() { 149 sleep(1); 150 kill(getpid(), SIGUSR2); 151 sleep(1); 152 kill(getpid(), SIGUSR1); 153 }); 154 ASSERT_TRUE(loop.Run().Success()); 155 killer.join(); 156 ASSERT_EQ(1u, callback_count); 157 } 158 #endif 159