1 //===-- MainLoopTest.cpp --------------------------------------------------===// 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 "TestingSupport/SubsystemRAII.h" 11 #include "lldb/Host/ConnectionFileDescriptor.h" 12 #include "lldb/Host/PseudoTerminal.h" 13 #include "lldb/Host/common/TCPSocket.h" 14 #include "llvm/Testing/Support/Error.h" 15 #include "gtest/gtest.h" 16 #include <future> 17 18 using namespace lldb_private; 19 20 namespace { 21 class MainLoopTest : public testing::Test { 22 public: 23 SubsystemRAII<Socket> subsystems; 24 25 void SetUp() override { 26 bool child_processes_inherit = false; 27 Status error; 28 std::unique_ptr<TCPSocket> listen_socket_up( 29 new TCPSocket(true, child_processes_inherit)); 30 ASSERT_TRUE(error.Success()); 31 error = listen_socket_up->Listen("localhost:0", 5); 32 ASSERT_TRUE(error.Success()); 33 34 Socket *accept_socket; 35 std::future<Status> accept_error = std::async(std::launch::async, [&] { 36 return listen_socket_up->Accept(accept_socket); 37 }); 38 39 std::unique_ptr<TCPSocket> connect_socket_up( 40 new TCPSocket(true, child_processes_inherit)); 41 error = connect_socket_up->Connect( 42 llvm::formatv("localhost:{0}", listen_socket_up->GetLocalPortNumber()) 43 .str()); 44 ASSERT_TRUE(error.Success()); 45 ASSERT_TRUE(accept_error.get().Success()); 46 47 callback_count = 0; 48 socketpair[0] = std::move(connect_socket_up); 49 socketpair[1].reset(accept_socket); 50 } 51 52 void TearDown() override { 53 socketpair[0].reset(); 54 socketpair[1].reset(); 55 } 56 57 protected: 58 MainLoop::Callback make_callback() { 59 return [&](MainLoopBase &loop) { 60 ++callback_count; 61 loop.RequestTermination(); 62 }; 63 } 64 std::shared_ptr<Socket> socketpair[2]; 65 unsigned callback_count; 66 }; 67 } // namespace 68 69 TEST_F(MainLoopTest, ReadObject) { 70 char X = 'X'; 71 size_t len = sizeof(X); 72 ASSERT_TRUE(socketpair[0]->Write(&X, len).Success()); 73 74 MainLoop loop; 75 76 Status error; 77 auto handle = loop.RegisterReadObject(socketpair[1], make_callback(), error); 78 ASSERT_TRUE(error.Success()); 79 ASSERT_TRUE(handle); 80 ASSERT_TRUE(loop.Run().Success()); 81 ASSERT_EQ(1u, callback_count); 82 } 83 84 TEST_F(MainLoopTest, TerminatesImmediately) { 85 char X = 'X'; 86 size_t len = sizeof(X); 87 ASSERT_TRUE(socketpair[0]->Write(&X, len).Success()); 88 ASSERT_TRUE(socketpair[1]->Write(&X, len).Success()); 89 90 MainLoop loop; 91 Status error; 92 auto handle0 = loop.RegisterReadObject(socketpair[0], make_callback(), error); 93 ASSERT_TRUE(error.Success()); 94 auto handle1 = loop.RegisterReadObject(socketpair[1], make_callback(), error); 95 ASSERT_TRUE(error.Success()); 96 97 ASSERT_TRUE(loop.Run().Success()); 98 ASSERT_EQ(1u, callback_count); 99 } 100 101 TEST_F(MainLoopTest, PendingCallback) { 102 char X = 'X'; 103 size_t len = sizeof(X); 104 ASSERT_TRUE(socketpair[0]->Write(&X, len).Success()); 105 106 MainLoop loop; 107 Status error; 108 auto handle = loop.RegisterReadObject( 109 socketpair[1], 110 [&](MainLoopBase &loop) { 111 // Both callbacks should be called before the loop terminates. 112 loop.AddPendingCallback(make_callback()); 113 loop.AddPendingCallback(make_callback()); 114 loop.RequestTermination(); 115 }, 116 error); 117 ASSERT_TRUE(error.Success()); 118 ASSERT_TRUE(handle); 119 ASSERT_TRUE(loop.Run().Success()); 120 ASSERT_EQ(2u, callback_count); 121 } 122 123 TEST_F(MainLoopTest, PendingCallbackCalledOnlyOnce) { 124 char X = 'X'; 125 size_t len = sizeof(X); 126 ASSERT_TRUE(socketpair[0]->Write(&X, len).Success()); 127 128 MainLoop loop; 129 Status error; 130 auto handle = loop.RegisterReadObject( 131 socketpair[1], 132 [&](MainLoopBase &loop) { 133 // Add one pending callback on the first iteration. 134 if (callback_count == 0) { 135 loop.AddPendingCallback([&](MainLoopBase &loop) { 136 callback_count++; 137 }); 138 } 139 // Terminate the loop on second iteration. 140 if (callback_count++ >= 1) 141 loop.RequestTermination(); 142 }, 143 error); 144 ASSERT_TRUE(error.Success()); 145 ASSERT_TRUE(handle); 146 ASSERT_TRUE(loop.Run().Success()); 147 // 2 iterations of read callback + 1 call of pending callback. 148 ASSERT_EQ(3u, callback_count); 149 } 150 151 #ifdef LLVM_ON_UNIX 152 TEST_F(MainLoopTest, DetectsEOF) { 153 154 PseudoTerminal term; 155 ASSERT_THAT_ERROR(term.OpenFirstAvailablePrimary(O_RDWR), llvm::Succeeded()); 156 ASSERT_THAT_ERROR(term.OpenSecondary(O_RDWR | O_NOCTTY), llvm::Succeeded()); 157 auto conn = std::make_unique<ConnectionFileDescriptor>( 158 term.ReleasePrimaryFileDescriptor(), true); 159 160 Status error; 161 MainLoop loop; 162 auto handle = 163 loop.RegisterReadObject(conn->GetReadObject(), make_callback(), error); 164 ASSERT_TRUE(error.Success()); 165 term.CloseSecondaryFileDescriptor(); 166 167 ASSERT_TRUE(loop.Run().Success()); 168 ASSERT_EQ(1u, callback_count); 169 } 170 171 TEST_F(MainLoopTest, Signal) { 172 MainLoop loop; 173 Status error; 174 175 auto handle = loop.RegisterSignal(SIGUSR1, make_callback(), error); 176 ASSERT_TRUE(error.Success()); 177 kill(getpid(), SIGUSR1); 178 ASSERT_TRUE(loop.Run().Success()); 179 ASSERT_EQ(1u, callback_count); 180 } 181 182 // Test that a signal which is not monitored by the MainLoop does not 183 // cause a premature exit. 184 TEST_F(MainLoopTest, UnmonitoredSignal) { 185 MainLoop loop; 186 Status error; 187 struct sigaction sa; 188 sa.sa_sigaction = [](int, siginfo_t *, void *) { }; 189 sa.sa_flags = SA_SIGINFO; // important: no SA_RESTART 190 sigemptyset(&sa.sa_mask); 191 ASSERT_EQ(0, sigaction(SIGUSR2, &sa, nullptr)); 192 193 auto handle = loop.RegisterSignal(SIGUSR1, make_callback(), error); 194 ASSERT_TRUE(error.Success()); 195 std::thread killer([]() { 196 sleep(1); 197 kill(getpid(), SIGUSR2); 198 sleep(1); 199 kill(getpid(), SIGUSR1); 200 }); 201 ASSERT_TRUE(loop.Run().Success()); 202 killer.join(); 203 ASSERT_EQ(1u, callback_count); 204 } 205 206 // Test that two callbacks can be registered for the same signal 207 // and unregistered independently. 208 TEST_F(MainLoopTest, TwoSignalCallbacks) { 209 MainLoop loop; 210 Status error; 211 unsigned callback2_count = 0; 212 unsigned callback3_count = 0; 213 214 auto handle = loop.RegisterSignal(SIGUSR1, make_callback(), error); 215 ASSERT_TRUE(error.Success()); 216 217 { 218 // Run a single iteration with two callbacks enabled. 219 auto handle2 = loop.RegisterSignal( 220 SIGUSR1, [&](MainLoopBase &loop) { ++callback2_count; }, error); 221 ASSERT_TRUE(error.Success()); 222 223 kill(getpid(), SIGUSR1); 224 ASSERT_TRUE(loop.Run().Success()); 225 ASSERT_EQ(1u, callback_count); 226 ASSERT_EQ(1u, callback2_count); 227 ASSERT_EQ(0u, callback3_count); 228 } 229 230 { 231 // Make sure that remove + add new works. 232 auto handle3 = loop.RegisterSignal( 233 SIGUSR1, [&](MainLoopBase &loop) { ++callback3_count; }, error); 234 ASSERT_TRUE(error.Success()); 235 236 kill(getpid(), SIGUSR1); 237 ASSERT_TRUE(loop.Run().Success()); 238 ASSERT_EQ(2u, callback_count); 239 ASSERT_EQ(1u, callback2_count); 240 ASSERT_EQ(1u, callback3_count); 241 } 242 243 // Both extra callbacks should be unregistered now. 244 kill(getpid(), SIGUSR1); 245 ASSERT_TRUE(loop.Run().Success()); 246 ASSERT_EQ(3u, callback_count); 247 ASSERT_EQ(1u, callback2_count); 248 ASSERT_EQ(1u, callback3_count); 249 } 250 #endif 251