1//===-- MainLoopPosix.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/posix/MainLoopPosix.h"
10#include "lldb/Host/Config.h"
11#include "lldb/Host/PosixApi.h"
12#include "lldb/Utility/Status.h"
13#include "llvm/Config/llvm-config.h"
14#include "llvm/Support/Errno.h"
15#include <algorithm>
16#include <cassert>
17#include <cerrno>
18#include <chrono>
19#include <csignal>
20#include <ctime>
21#include <fcntl.h>
22#include <vector>
23
24// Multiplexing is implemented using kqueue on systems that support it (BSD
25// variants including OSX). On linux we use ppoll.
26
27#if HAVE_SYS_EVENT_H
28#include <sys/event.h>
29#else
30#include <poll.h>
31#endif
32
33using namespace lldb;
34using namespace lldb_private;
35
36namespace {
37struct GlobalSignalInfo {
38 sig_atomic_t pipe_fd = -1;
39 static_assert(sizeof(sig_atomic_t) >= sizeof(int),
40 "Type too small for a file descriptor");
41 sig_atomic_t flag = 0;
42};
43} // namespace
44static GlobalSignalInfo g_signal_info[NSIG];
45
46static void SignalHandler(int signo, siginfo_t *info, void *) {
47 assert(signo < NSIG);
48
49 // Set the flag before writing to the pipe!
50 g_signal_info[signo].flag = 1;
51
52 int fd = g_signal_info[signo].pipe_fd;
53 if (fd < 0) {
54 // This can happen with the following (unlikely) sequence of events:
55 // 1. Thread 1 gets a signal, starts running the signal handler
56 // 2. Thread 2 unregisters the signal handler, setting pipe_fd to -1
57 // 3. Signal handler on thread 1 reads -1 out of pipe_fd
58 // In this case, we can just ignore the signal because we're no longer
59 // interested in it.
60 return;
61 }
62
63 // Write a(ny) character to the pipe to wake up from the poll syscall.
64 char c = '.';
65 ssize_t bytes_written = llvm::sys::RetryAfterSignal(Fail: -1, F&: ::write, As: fd, As: &c, As: 1);
66 // We can safely ignore EAGAIN (pipe full), as that means poll will definitely
67 // return.
68 assert(bytes_written == 1 || (bytes_written == -1 && errno == EAGAIN));
69 (void)bytes_written;
70}
71
72class ToTimeSpec {
73public:
74 explicit ToTimeSpec(std::optional<MainLoopPosix::TimePoint> point) {
75 using namespace std::chrono;
76
77 if (!point) {
78 m_ts_ptr = nullptr;
79 return;
80 }
81 nanoseconds dur = std::max(a: *point - steady_clock::now(), b: nanoseconds(0));
82 m_ts_ptr = &m_ts;
83 m_ts.tv_sec = duration_cast<seconds>(d: dur).count();
84 m_ts.tv_nsec = (dur % seconds(1)).count();
85 }
86 ToTimeSpec(const ToTimeSpec &) = delete;
87 ToTimeSpec &operator=(const ToTimeSpec &) = delete;
88
89 operator struct timespec *() { return m_ts_ptr; }
90
91private:
92 struct timespec m_ts;
93 struct timespec *m_ts_ptr;
94};
95
96class MainLoopPosix::RunImpl {
97public:
98 RunImpl(MainLoopPosix &loop);
99 ~RunImpl() = default;
100
101 Status Poll();
102
103 void ProcessReadEvents();
104
105private:
106 MainLoopPosix &loop;
107
108#if HAVE_SYS_EVENT_H
109 std::vector<struct kevent> in_events;
110 struct kevent out_events[4];
111 int num_events = -1;
112
113#else
114 std::vector<struct pollfd> read_fds;
115#endif
116};
117
118#if HAVE_SYS_EVENT_H
119MainLoopPosix::RunImpl::RunImpl(MainLoopPosix &loop) : loop(loop) {
120 in_events.reserve(loop.m_read_fds.size());
121}
122
123Status MainLoopPosix::RunImpl::Poll() {
124 in_events.resize(loop.m_read_fds.size());
125 unsigned i = 0;
126 for (auto &fd : loop.m_read_fds)
127 EV_SET(&in_events[i++], fd.first, EVFILT_READ, EV_ADD, 0, 0, 0);
128
129 num_events =
130 kevent(loop.m_kqueue, in_events.data(), in_events.size(), out_events,
131 std::size(out_events), ToTimeSpec(loop.GetNextWakeupTime()));
132
133 if (num_events < 0) {
134 if (errno == EINTR) {
135 // in case of EINTR, let the main loop run one iteration
136 // we need to zero num_events to avoid assertions failing
137 num_events = 0;
138 } else
139 return Status(errno, eErrorTypePOSIX);
140 }
141 return Status();
142}
143
144void MainLoopPosix::RunImpl::ProcessReadEvents() {
145 assert(num_events >= 0);
146 for (int i = 0; i < num_events; ++i) {
147 if (loop.m_terminate_request)
148 return;
149 switch (out_events[i].filter) {
150 case EVFILT_READ:
151 loop.ProcessReadObject(out_events[i].ident);
152 break;
153 default:
154 llvm_unreachable("Unknown event");
155 }
156 }
157}
158#else
159MainLoopPosix::RunImpl::RunImpl(MainLoopPosix &loop) : loop(loop) {
160 read_fds.reserve(n: loop.m_read_fds.size());
161}
162
163static int StartPoll(llvm::MutableArrayRef<struct pollfd> fds,
164 std::optional<MainLoopPosix::TimePoint> point) {
165#if HAVE_PPOLL
166 return ppoll(fds: fds.data(), nfds: fds.size(), timeout: ToTimeSpec(point),
167 /*sigmask=*/ss: nullptr);
168#else
169 using namespace std::chrono;
170 int timeout = -1;
171 if (point) {
172 nanoseconds dur = std::max(*point - steady_clock::now(), nanoseconds(0));
173 timeout = ceil<milliseconds>(dur).count();
174 }
175 return poll(fds.data(), fds.size(), timeout);
176#endif
177}
178
179Status MainLoopPosix::RunImpl::Poll() {
180 read_fds.clear();
181
182 for (const auto &fd : loop.m_read_fds) {
183 struct pollfd pfd;
184 pfd.fd = fd.first;
185 pfd.events = POLLIN;
186 pfd.revents = 0;
187 read_fds.push_back(x: pfd);
188 }
189 int ready = StartPoll(fds: read_fds, point: loop.GetNextWakeupTime());
190
191 if (ready == -1 && errno != EINTR)
192 return Status(errno, eErrorTypePOSIX);
193
194 return Status();
195}
196
197void MainLoopPosix::RunImpl::ProcessReadEvents() {
198 for (const auto &fd : read_fds) {
199 if ((fd.revents & (POLLIN | POLLHUP)) == 0)
200 continue;
201 IOObject::WaitableHandle handle = fd.fd;
202 if (loop.m_terminate_request)
203 return;
204
205 loop.ProcessReadObject(handle);
206 }
207}
208#endif
209
210MainLoopPosix::MainLoopPosix() {
211 Status error = m_interrupt_pipe.CreateNew(/*child_process_inherit=*/false);
212 assert(error.Success());
213
214 // Make the write end of the pipe non-blocking.
215 int result = fcntl(fd: m_interrupt_pipe.GetWriteFileDescriptor(), F_SETFL,
216 fcntl(fd: m_interrupt_pipe.GetWriteFileDescriptor(), F_GETFL) |
217 O_NONBLOCK);
218 assert(result == 0);
219 UNUSED_IF_ASSERT_DISABLED(result);
220
221 const int interrupt_pipe_fd = m_interrupt_pipe.GetReadFileDescriptor();
222 m_read_fds.insert(
223 KV: {interrupt_pipe_fd, [interrupt_pipe_fd](MainLoopBase &loop) {
224 char c;
225 ssize_t bytes_read =
226 llvm::sys::RetryAfterSignal(Fail: -1, F&: ::read, As: interrupt_pipe_fd, As: &c, As: 1);
227 assert(bytes_read == 1);
228 UNUSED_IF_ASSERT_DISABLED(bytes_read);
229 // NB: This implicitly causes another loop iteration
230 // and therefore the execution of pending callbacks.
231 }});
232#if HAVE_SYS_EVENT_H
233 m_kqueue = kqueue();
234 assert(m_kqueue >= 0);
235#endif
236}
237
238MainLoopPosix::~MainLoopPosix() {
239#if HAVE_SYS_EVENT_H
240 close(m_kqueue);
241#endif
242 m_read_fds.erase(Val: m_interrupt_pipe.GetReadFileDescriptor());
243 m_interrupt_pipe.Close();
244 assert(m_read_fds.size() == 0);
245 assert(m_signals.size() == 0);
246}
247
248MainLoopPosix::ReadHandleUP
249MainLoopPosix::RegisterReadObject(const IOObjectSP &object_sp,
250 const Callback &callback, Status &error) {
251 if (!object_sp || !object_sp->IsValid()) {
252 error = Status::FromErrorString(str: "IO object is not valid.");
253 return nullptr;
254 }
255
256 const bool inserted =
257 m_read_fds.insert(KV: {object_sp->GetWaitableHandle(), callback}).second;
258 if (!inserted) {
259 error = Status::FromErrorStringWithFormat(
260 format: "File descriptor %d already monitored.",
261 object_sp->GetWaitableHandle());
262 return nullptr;
263 }
264
265 return CreateReadHandle(object_sp);
266}
267
268// We shall block the signal, then install the signal handler. The signal will
269// be unblocked in the Run() function to check for signal delivery.
270MainLoopPosix::SignalHandleUP
271MainLoopPosix::RegisterSignal(int signo, const Callback &callback,
272 Status &error) {
273 auto signal_it = m_signals.find(Val: signo);
274 if (signal_it != m_signals.end()) {
275 auto callback_it = signal_it->second.callbacks.insert(
276 position: signal_it->second.callbacks.end(), x: callback);
277 return SignalHandleUP(new SignalHandle(*this, signo, callback_it));
278 }
279
280 SignalInfo info;
281 info.callbacks.push_back(x: callback);
282 struct sigaction new_action;
283 new_action.sa_sigaction = &SignalHandler;
284 new_action.sa_flags = SA_SIGINFO;
285 sigemptyset(set: &new_action.sa_mask);
286 sigaddset(set: &new_action.sa_mask, signo: signo);
287 sigset_t old_set;
288
289 // Set signal info before installing the signal handler!
290 g_signal_info[signo].pipe_fd = m_interrupt_pipe.GetWriteFileDescriptor();
291 g_signal_info[signo].flag = 0;
292
293 int ret = sigaction(sig: signo, act: &new_action, oact: &info.old_action);
294 UNUSED_IF_ASSERT_DISABLED(ret);
295 assert(ret == 0 && "sigaction failed");
296
297 ret = pthread_sigmask(SIG_UNBLOCK, newmask: &new_action.sa_mask, oldmask: &old_set);
298 assert(ret == 0 && "pthread_sigmask failed");
299 info.was_blocked = sigismember(set: &old_set, signo: signo);
300 auto insert_ret = m_signals.insert(KV: {signo, info});
301
302 return SignalHandleUP(new SignalHandle(
303 *this, signo, insert_ret.first->second.callbacks.begin()));
304}
305
306void MainLoopPosix::UnregisterReadObject(IOObject::WaitableHandle handle) {
307 bool erased = m_read_fds.erase(Val: handle);
308 UNUSED_IF_ASSERT_DISABLED(erased);
309 assert(erased);
310}
311
312void MainLoopPosix::UnregisterSignal(
313 int signo, std::list<Callback>::iterator callback_it) {
314 auto it = m_signals.find(Val: signo);
315 assert(it != m_signals.end());
316
317 it->second.callbacks.erase(position: callback_it);
318 // Do not remove the signal handler unless all callbacks have been erased.
319 if (!it->second.callbacks.empty())
320 return;
321
322 sigaction(sig: signo, act: &it->second.old_action, oact: nullptr);
323
324 sigset_t set;
325 sigemptyset(set: &set);
326 sigaddset(set: &set, signo: signo);
327 int ret = pthread_sigmask(how: it->second.was_blocked ? SIG_BLOCK : SIG_UNBLOCK,
328 newmask: &set, oldmask: nullptr);
329 assert(ret == 0);
330 UNUSED_IF_ASSERT_DISABLED(ret);
331
332 m_signals.erase(I: it);
333 g_signal_info[signo] = {};
334}
335
336Status MainLoopPosix::Run() {
337 m_terminate_request = false;
338
339 Status error;
340 RunImpl impl(*this);
341
342 while (!m_terminate_request) {
343 error = impl.Poll();
344 if (error.Fail())
345 return error;
346
347 impl.ProcessReadEvents();
348
349 ProcessSignals();
350
351 m_interrupting = false;
352 ProcessCallbacks();
353 }
354 return Status();
355}
356
357void MainLoopPosix::ProcessReadObject(IOObject::WaitableHandle handle) {
358 auto it = m_read_fds.find(Val: handle);
359 if (it != m_read_fds.end())
360 it->second(*this); // Do the work
361}
362
363void MainLoopPosix::ProcessSignals() {
364 std::vector<int> signals;
365 for (const auto &entry : m_signals)
366 if (g_signal_info[entry.first].flag != 0)
367 signals.push_back(x: entry.first);
368
369 for (const auto &signal : signals) {
370 if (m_terminate_request)
371 return;
372
373 g_signal_info[signal].flag = 0;
374 ProcessSignal(signo: signal);
375 }
376}
377
378void MainLoopPosix::ProcessSignal(int signo) {
379 auto it = m_signals.find(Val: signo);
380 if (it != m_signals.end()) {
381 // The callback may actually register/unregister signal handlers,
382 // so we need to create a copy first.
383 llvm::SmallVector<Callback, 4> callbacks_to_run{
384 it->second.callbacks.begin(), it->second.callbacks.end()};
385 for (auto &x : callbacks_to_run)
386 x(*this); // Do the work
387 }
388}
389
390void MainLoopPosix::Interrupt() {
391 if (m_interrupting.exchange(i: true))
392 return;
393
394 char c = '.';
395 cantFail(ValOrErr: m_interrupt_pipe.Write(buf: &c, size: 1));
396}
397

source code of lldb/source/Host/posix/MainLoopPosix.cpp