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