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