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