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/Config/llvm-config.h" // for LLVM_ON_UNIX 16 #include "llvm/Testing/Support/Error.h" 17 #include "gtest/gtest.h" 18 #include <future> 19 #include <thread> 20 21 using namespace lldb_private; 22 23 namespace { 24 class MainLoopTest : public testing::Test { 25 public: 26 SubsystemRAII<FileSystem, Socket> subsystems; 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::unique_ptr<TCPSocket> connect_socket_up( 39 new TCPSocket(true, child_processes_inherit)); 40 error = connect_socket_up->Connect( 41 llvm::formatv("localhost:{0}", listen_socket_up->GetLocalPortNumber()) 42 .str()); 43 ASSERT_TRUE(error.Success()); 44 ASSERT_TRUE(listen_socket_up->Accept(accept_socket).Success()); 45 46 callback_count = 0; 47 socketpair[0] = std::move(connect_socket_up); 48 socketpair[1].reset(accept_socket); 49 } 50 51 void TearDown() override { 52 socketpair[0].reset(); 53 socketpair[1].reset(); 54 } 55 56 protected: 57 MainLoop::Callback make_callback() { 58 return [&](MainLoopBase &loop) { 59 ++callback_count; 60 loop.RequestTermination(); 61 }; 62 } 63 std::shared_ptr<Socket> socketpair[2]; 64 unsigned callback_count; 65 }; 66 } // namespace 67 68 TEST_F(MainLoopTest, ReadObject) { 69 char X = 'X'; 70 size_t len = sizeof(X); 71 ASSERT_TRUE(socketpair[0]->Write(&X, len).Success()); 72 73 MainLoop loop; 74 75 Status error; 76 auto handle = loop.RegisterReadObject(socketpair[1], make_callback(), error); 77 ASSERT_TRUE(error.Success()); 78 ASSERT_TRUE(handle); 79 ASSERT_TRUE(loop.Run().Success()); 80 ASSERT_EQ(1u, callback_count); 81 } 82 83 TEST_F(MainLoopTest, NoSpuriousReads) { 84 // Write one byte into the socket. 85 char X = 'X'; 86 size_t len = sizeof(X); 87 ASSERT_TRUE(socketpair[0]->Write(&X, len).Success()); 88 89 MainLoop loop; 90 91 Status error; 92 auto handle = loop.RegisterReadObject( 93 socketpair[1], 94 [this](MainLoopBase &) { 95 if (callback_count == 0) { 96 // Read the byte back the first time we're called. After that, the 97 // socket is empty, and we should not be called anymore. 98 char X; 99 size_t len = sizeof(X); 100 EXPECT_THAT_ERROR(socketpair[1]->Read(&X, len).ToError(), 101 llvm::Succeeded()); 102 EXPECT_EQ(len, sizeof(X)); 103 } 104 ++callback_count; 105 }, 106 error); 107 ASSERT_THAT_ERROR(error.ToError(), llvm::Succeeded()); 108 // Terminate the loop after one second. 109 std::thread terminate_thread([&loop] { 110 std::this_thread::sleep_for(std::chrono::seconds(1)); 111 loop.AddPendingCallback( 112 [](MainLoopBase &loop) { loop.RequestTermination(); }); 113 }); 114 ASSERT_THAT_ERROR(loop.Run().ToError(), llvm::Succeeded()); 115 terminate_thread.join(); 116 117 // Make sure the callback was called only once. 118 ASSERT_EQ(1u, callback_count); 119 } 120 121 TEST_F(MainLoopTest, TerminatesImmediately) { 122 char X = 'X'; 123 size_t len = sizeof(X); 124 ASSERT_TRUE(socketpair[0]->Write(&X, len).Success()); 125 ASSERT_TRUE(socketpair[1]->Write(&X, len).Success()); 126 127 MainLoop loop; 128 Status error; 129 auto handle0 = loop.RegisterReadObject(socketpair[0], make_callback(), error); 130 ASSERT_TRUE(error.Success()); 131 auto handle1 = loop.RegisterReadObject(socketpair[1], make_callback(), error); 132 ASSERT_TRUE(error.Success()); 133 134 ASSERT_TRUE(loop.Run().Success()); 135 ASSERT_EQ(1u, callback_count); 136 } 137 138 TEST_F(MainLoopTest, PendingCallback) { 139 char X = 'X'; 140 size_t len = sizeof(X); 141 ASSERT_TRUE(socketpair[0]->Write(&X, len).Success()); 142 143 MainLoop loop; 144 Status error; 145 auto handle = loop.RegisterReadObject( 146 socketpair[1], 147 [&](MainLoopBase &loop) { 148 // Both callbacks should be called before the loop terminates. 149 loop.AddPendingCallback(make_callback()); 150 loop.AddPendingCallback(make_callback()); 151 loop.RequestTermination(); 152 }, 153 error); 154 ASSERT_TRUE(error.Success()); 155 ASSERT_TRUE(handle); 156 ASSERT_TRUE(loop.Run().Success()); 157 ASSERT_EQ(2u, callback_count); 158 } 159 160 TEST_F(MainLoopTest, PendingCallbackCalledOnlyOnce) { 161 char X = 'X'; 162 size_t len = sizeof(X); 163 ASSERT_TRUE(socketpair[0]->Write(&X, len).Success()); 164 165 MainLoop loop; 166 Status error; 167 auto handle = loop.RegisterReadObject( 168 socketpair[1], 169 [&](MainLoopBase &loop) { 170 // Add one pending callback on the first iteration. 171 if (callback_count == 0) { 172 loop.AddPendingCallback([&](MainLoopBase &loop) { 173 callback_count++; 174 }); 175 } 176 // Terminate the loop on second iteration. 177 if (callback_count++ >= 1) 178 loop.RequestTermination(); 179 }, 180 error); 181 ASSERT_TRUE(error.Success()); 182 ASSERT_TRUE(handle); 183 ASSERT_TRUE(loop.Run().Success()); 184 // 2 iterations of read callback + 1 call of pending callback. 185 ASSERT_EQ(3u, callback_count); 186 } 187 188 TEST_F(MainLoopTest, PendingCallbackTrigger) { 189 MainLoop loop; 190 std::promise<void> add_callback2; 191 bool callback1_called = false; 192 loop.AddPendingCallback([&](MainLoopBase &loop) { 193 callback1_called = true; 194 add_callback2.set_value(); 195 }); 196 Status error; 197 ASSERT_THAT_ERROR(error.ToError(), llvm::Succeeded()); 198 bool callback2_called = false; 199 std::thread callback2_adder([&]() { 200 add_callback2.get_future().get(); 201 loop.AddPendingCallback([&](MainLoopBase &loop) { 202 callback2_called = true; 203 loop.RequestTermination(); 204 }); 205 }); 206 ASSERT_THAT_ERROR(loop.Run().ToError(), llvm::Succeeded()); 207 callback2_adder.join(); 208 ASSERT_TRUE(callback1_called); 209 ASSERT_TRUE(callback2_called); 210 } 211 212 TEST_F(MainLoopTest, ManyPendingCallbacks) { 213 MainLoop loop; 214 Status error; 215 // Try to fill up the pipe buffer and make sure bad things don't happen. This 216 // is a regression test for the case where writing to the interrupt pipe 217 // caused a deadlock when the pipe filled up (either because the main loop was 218 // not running, because it was slow, or because it was busy/blocked doing 219 // something else). 220 for (int i = 0; i < 65536; ++i) 221 loop.AddPendingCallback( 222 [&](MainLoopBase &loop) { loop.RequestTermination(); }); 223 ASSERT_TRUE(loop.Run().Success()); 224 } 225 226 #ifdef LLVM_ON_UNIX 227 TEST_F(MainLoopTest, DetectsEOF) { 228 229 PseudoTerminal term; 230 ASSERT_THAT_ERROR(term.OpenFirstAvailablePrimary(O_RDWR), llvm::Succeeded()); 231 ASSERT_THAT_ERROR(term.OpenSecondary(O_RDWR | O_NOCTTY), llvm::Succeeded()); 232 auto conn = std::make_unique<ConnectionFileDescriptor>( 233 term.ReleasePrimaryFileDescriptor(), true); 234 235 Status error; 236 MainLoop loop; 237 auto handle = 238 loop.RegisterReadObject(conn->GetReadObject(), make_callback(), error); 239 ASSERT_TRUE(error.Success()); 240 term.CloseSecondaryFileDescriptor(); 241 242 ASSERT_TRUE(loop.Run().Success()); 243 ASSERT_EQ(1u, callback_count); 244 } 245 246 TEST_F(MainLoopTest, Signal) { 247 MainLoop loop; 248 Status error; 249 250 auto handle = loop.RegisterSignal(SIGUSR1, make_callback(), error); 251 ASSERT_TRUE(error.Success()); 252 kill(getpid(), SIGUSR1); 253 ASSERT_TRUE(loop.Run().Success()); 254 ASSERT_EQ(1u, callback_count); 255 } 256 257 // Test that a signal which is not monitored by the MainLoop does not 258 // cause a premature exit. 259 TEST_F(MainLoopTest, UnmonitoredSignal) { 260 MainLoop loop; 261 Status error; 262 struct sigaction sa; 263 sa.sa_sigaction = [](int, siginfo_t *, void *) { }; 264 sa.sa_flags = SA_SIGINFO; // important: no SA_RESTART 265 sigemptyset(&sa.sa_mask); 266 ASSERT_EQ(0, sigaction(SIGUSR2, &sa, nullptr)); 267 268 auto handle = loop.RegisterSignal(SIGUSR1, make_callback(), error); 269 ASSERT_TRUE(error.Success()); 270 kill(getpid(), SIGUSR2); 271 kill(getpid(), SIGUSR1); 272 ASSERT_TRUE(loop.Run().Success()); 273 ASSERT_EQ(1u, callback_count); 274 } 275 276 // Test that two callbacks can be registered for the same signal 277 // and unregistered independently. 278 TEST_F(MainLoopTest, TwoSignalCallbacks) { 279 MainLoop loop; 280 Status error; 281 unsigned callback2_count = 0; 282 unsigned callback3_count = 0; 283 284 auto handle = loop.RegisterSignal(SIGUSR1, make_callback(), error); 285 ASSERT_TRUE(error.Success()); 286 287 { 288 // Run a single iteration with two callbacks enabled. 289 auto handle2 = loop.RegisterSignal( 290 SIGUSR1, [&](MainLoopBase &loop) { ++callback2_count; }, error); 291 ASSERT_TRUE(error.Success()); 292 293 kill(getpid(), SIGUSR1); 294 ASSERT_TRUE(loop.Run().Success()); 295 ASSERT_EQ(1u, callback_count); 296 ASSERT_EQ(1u, callback2_count); 297 ASSERT_EQ(0u, callback3_count); 298 } 299 300 { 301 // Make sure that remove + add new works. 302 auto handle3 = loop.RegisterSignal( 303 SIGUSR1, [&](MainLoopBase &loop) { ++callback3_count; }, error); 304 ASSERT_TRUE(error.Success()); 305 306 kill(getpid(), SIGUSR1); 307 ASSERT_TRUE(loop.Run().Success()); 308 ASSERT_EQ(2u, callback_count); 309 ASSERT_EQ(1u, callback2_count); 310 ASSERT_EQ(1u, callback3_count); 311 } 312 313 // Both extra callbacks should be unregistered now. 314 kill(getpid(), SIGUSR1); 315 ASSERT_TRUE(loop.Run().Success()); 316 ASSERT_EQ(3u, callback_count); 317 ASSERT_EQ(1u, callback2_count); 318 ASSERT_EQ(1u, callback3_count); 319 } 320 #endif 321