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