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