398 lines · cpp
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-exception6//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 (BSD25// variants including OSX). On linux we use ppoll.26 27#if HAVE_SYS_EVENT_H28#include <sys/event.h>29#else30#include <poll.h>31#endif32 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} // namespace44static 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 handler56 // 2. Thread 2 unregisters the signal handler, setting pipe_fd to -157 // 3. Signal handler on thread 1 reads -1 out of pipe_fd58 // In this case, we can just ignore the signal because we're no longer59 // 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(-1, ::write, fd, &c, 1);66 // We can safely ignore EAGAIN (pipe full), as that means poll will definitely67 // 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(*point - steady_clock::now(), nanoseconds(0));82 m_ts_ptr = &m_ts;83 m_ts.tv_sec = duration_cast<seconds>(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_H109 std::vector<struct kevent> in_events;110 struct kevent out_events[4];111 int num_events = -1;112 113#else114 std::vector<struct pollfd> read_fds;115#endif116};117 118#if HAVE_SYS_EVENT_H119MainLoopPosix::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 iteration136 // we need to zero num_events to avoid assertions failing137 num_events = 0;138 } else139 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#else159MainLoopPosix::RunImpl::RunImpl(MainLoopPosix &loop) : loop(loop) {160 read_fds.reserve(loop.m_read_fds.size());161}162 163static int StartPoll(llvm::MutableArrayRef<struct pollfd> fds,164 std::optional<MainLoopPosix::TimePoint> point) {165#if HAVE_PPOLL166 return ppoll(fds.data(), fds.size(), ToTimeSpec(point),167 /*sigmask=*/nullptr);168#else169 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#endif177}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(pfd);188 }189 int ready = StartPoll(read_fds, 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#endif209 210MainLoopPosix::MainLoopPosix() {211 Status error = m_interrupt_pipe.CreateNew();212 assert(error.Success());213 214 // Make the write end of the pipe non-blocking.215 int result = fcntl(m_interrupt_pipe.GetWriteFileDescriptor(), F_SETFL,216 fcntl(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 {interrupt_pipe_fd, [interrupt_pipe_fd](MainLoopBase &loop) {224 char c;225 ssize_t bytes_read =226 llvm::sys::RetryAfterSignal(-1, ::read, interrupt_pipe_fd, &c, 1);227 assert(bytes_read == 1);228 UNUSED_IF_ASSERT_DISABLED(bytes_read);229 // NB: This implicitly causes another loop iteration230 // and therefore the execution of pending callbacks.231 }});232#if HAVE_SYS_EVENT_H233 m_kqueue = kqueue();234 assert(m_kqueue >= 0);235#endif236}237 238MainLoopPosix::~MainLoopPosix() {239#if HAVE_SYS_EVENT_H240 close(m_kqueue);241#endif242 m_read_fds.erase(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::ReadHandleUP249MainLoopPosix::RegisterReadObject(const IOObjectSP &object_sp,250 const Callback &callback, Status &error) {251 if (!object_sp || !object_sp->IsValid()) {252 error = Status::FromErrorString("IO object is not valid.");253 return nullptr;254 }255 256 const bool inserted =257 m_read_fds.insert({object_sp->GetWaitableHandle(), callback}).second;258 if (!inserted) {259 error = Status::FromErrorStringWithFormat(260 "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 will269// be unblocked in the Run() function to check for signal delivery.270MainLoopPosix::SignalHandleUP271MainLoopPosix::RegisterSignal(int signo, const Callback &callback,272 Status &error) {273 auto signal_it = m_signals.find(signo);274 if (signal_it != m_signals.end()) {275 auto callback_it = signal_it->second.callbacks.insert(276 signal_it->second.callbacks.end(), callback);277 return SignalHandleUP(new SignalHandle(*this, signo, callback_it));278 }279 280 SignalInfo info;281 info.callbacks.push_back(callback);282 struct sigaction new_action;283 new_action.sa_sigaction = &SignalHandler;284 new_action.sa_flags = SA_SIGINFO;285 sigemptyset(&new_action.sa_mask);286 sigaddset(&new_action.sa_mask, 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(signo, &new_action, &info.old_action);294 UNUSED_IF_ASSERT_DISABLED(ret);295 assert(ret == 0 && "sigaction failed");296 297 ret = pthread_sigmask(SIG_UNBLOCK, &new_action.sa_mask, &old_set);298 assert(ret == 0 && "pthread_sigmask failed");299 info.was_blocked = sigismember(&old_set, signo);300 auto insert_ret = m_signals.insert({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(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(signo);315 assert(it != m_signals.end());316 317 it->second.callbacks.erase(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(signo, &it->second.old_action, nullptr);323 324 sigset_t set;325 sigemptyset(&set);326 sigaddset(&set, signo);327 int ret = pthread_sigmask(it->second.was_blocked ? SIG_BLOCK : SIG_UNBLOCK,328 &set, nullptr);329 assert(ret == 0);330 UNUSED_IF_ASSERT_DISABLED(ret);331 332 m_signals.erase(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(handle);359 if (it != m_read_fds.end())360 it->second(*this); // Do the work361}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(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(signal);375 }376}377 378void MainLoopPosix::ProcessSignal(int signo) {379 auto it = m_signals.find(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 work387 }388}389 390bool MainLoopPosix::Interrupt() {391 if (m_interrupting.exchange(true))392 return true;393 394 char c = '.';395 llvm::Expected<size_t> result = m_interrupt_pipe.Write(&c, 1);396 return result && *result != 0;397}398