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 // NetBSD currently does not report slave pty EOF via kevent
106 // causing this test to hang forever.
107 #ifndef __NetBSD__
108 TEST_F(MainLoopTest, DetectsEOF) {
109 
110   PseudoTerminal term;
111   ASSERT_TRUE(term.OpenFirstAvailableMaster(O_RDWR, nullptr, 0));
112   ASSERT_TRUE(term.OpenSlave(O_RDWR | O_NOCTTY, nullptr, 0));
113   auto conn = llvm::make_unique<ConnectionFileDescriptor>(
114       term.ReleaseMasterFileDescriptor(), true);
115 
116   Status error;
117   MainLoop loop;
118   auto handle =
119       loop.RegisterReadObject(conn->GetReadObject(), make_callback(), error);
120   ASSERT_TRUE(error.Success());
121   term.CloseSlaveFileDescriptor();
122 
123   ASSERT_TRUE(loop.Run().Success());
124   ASSERT_EQ(1u, callback_count);
125 }
126 #endif
127 
128 TEST_F(MainLoopTest, Signal) {
129   MainLoop loop;
130   Status error;
131 
132   auto handle = loop.RegisterSignal(SIGUSR1, make_callback(), error);
133   ASSERT_TRUE(error.Success());
134   kill(getpid(), SIGUSR1);
135   ASSERT_TRUE(loop.Run().Success());
136   ASSERT_EQ(1u, callback_count);
137 }
138 
139 // Test that a signal which is not monitored by the MainLoop does not
140 // cause a premature exit.
141 TEST_F(MainLoopTest, UnmonitoredSignal) {
142   MainLoop loop;
143   Status error;
144   struct sigaction sa;
145   sa.sa_sigaction = [](int, siginfo_t *, void *) { };
146   sa.sa_flags = SA_SIGINFO; // important: no SA_RESTART
147   sigemptyset(&sa.sa_mask);
148   ASSERT_EQ(0, sigaction(SIGUSR2, &sa, nullptr));
149 
150   auto handle = loop.RegisterSignal(SIGUSR1, make_callback(), error);
151   ASSERT_TRUE(error.Success());
152   std::thread killer([]() {
153     sleep(1);
154     kill(getpid(), SIGUSR2);
155     sleep(1);
156     kill(getpid(), SIGUSR1);
157   });
158   ASSERT_TRUE(loop.Run().Success());
159   killer.join();
160   ASSERT_EQ(1u, callback_count);
161 }
162 #endif
163