brintos

brintos / llvm-project-archived public Read only

0
0
Text · 11.1 KiB · c6fe781 Raw
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