brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.2 KiB · 3044cda Raw
255 lines · cpp
1//===-- SelectHelper.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#if defined(__APPLE__)10// Enable this special support for Apple builds where we can have unlimited11// select bounds. We tried switching to poll() and kqueue and we were panicing12// the kernel, so we have to stick with select for now.13#define _DARWIN_UNLIMITED_SELECT14#endif15 16#include "lldb/Utility/SelectHelper.h"17#include "lldb/Utility/LLDBAssert.h"18#include "lldb/Utility/Status.h"19#include "lldb/lldb-enumerations.h"20#include "lldb/lldb-types.h"21 22#include "llvm/ADT/DenseMap.h"23 24#include <algorithm>25#include <chrono>26#include <optional>27 28#include <cerrno>29#if defined(_WIN32)30// Define NOMINMAX to avoid macros that conflict with std::min and std::max31#define NOMINMAX32#include <winsock2.h>33#else34#include <sys/time.h>35#include <sys/select.h>36#endif37 38 39SelectHelper::SelectHelper()40    : m_fd_map(), m_end_time() // Infinite timeout unless41                               // SelectHelper::SetTimeout() gets called42{}43 44void SelectHelper::SetTimeout(const std::chrono::microseconds &timeout) {45  using namespace std::chrono;46  m_end_time = steady_clock::time_point(steady_clock::now() + timeout);47}48 49void SelectHelper::FDSetRead(lldb::socket_t fd) {50  m_fd_map[fd].read_set = true;51}52 53void SelectHelper::FDSetWrite(lldb::socket_t fd) {54  m_fd_map[fd].write_set = true;55}56 57void SelectHelper::FDSetError(lldb::socket_t fd) {58  m_fd_map[fd].error_set = true;59}60 61bool SelectHelper::FDIsSetRead(lldb::socket_t fd) const {62  auto pos = m_fd_map.find(fd);63  if (pos != m_fd_map.end())64    return pos->second.read_is_set;65  else66    return false;67}68 69bool SelectHelper::FDIsSetWrite(lldb::socket_t fd) const {70  auto pos = m_fd_map.find(fd);71  if (pos != m_fd_map.end())72    return pos->second.write_is_set;73  else74    return false;75}76 77bool SelectHelper::FDIsSetError(lldb::socket_t fd) const {78  auto pos = m_fd_map.find(fd);79  if (pos != m_fd_map.end())80    return pos->second.error_is_set;81  else82    return false;83}84 85static void updateMaxFd(std::optional<lldb::socket_t> &vold,86                        lldb::socket_t vnew) {87  if (!vold)88    vold = vnew;89  else90    vold = std::max(*vold, vnew);91}92 93lldb_private::Status SelectHelper::Select() {94  lldb_private::Status error;95#ifdef _WIN3296  // On windows FD_SETSIZE limits the number of file descriptors, not their97  // numeric value.98  lldbassert(m_fd_map.size() <= FD_SETSIZE);99  if (m_fd_map.size() > FD_SETSIZE)100    return lldb_private::Status::FromErrorString(101        "Too many file descriptors for select()");102#endif103 104  std::optional<lldb::socket_t> max_read_fd;105  std::optional<lldb::socket_t> max_write_fd;106  std::optional<lldb::socket_t> max_error_fd;107  std::optional<lldb::socket_t> max_fd;108  for (auto &pair : m_fd_map) {109    pair.second.PrepareForSelect();110    const lldb::socket_t fd = pair.first;111#if !defined(__APPLE__) && !defined(_WIN32)112    lldbassert(fd < static_cast<int>(FD_SETSIZE));113    if (fd >= static_cast<int>(FD_SETSIZE)) {114      error = lldb_private::Status::FromErrorStringWithFormat(115          "%i is too large for select()", fd);116      return error;117    }118#endif119    if (pair.second.read_set)120      updateMaxFd(max_read_fd, fd);121    if (pair.second.write_set)122      updateMaxFd(max_write_fd, fd);123    if (pair.second.error_set)124      updateMaxFd(max_error_fd, fd);125    updateMaxFd(max_fd, fd);126  }127 128  if (!max_fd) {129    return lldb_private::Status::FromErrorString("no valid file descriptors");130  }131 132  const unsigned nfds = static_cast<unsigned>(*max_fd) + 1;133  fd_set *read_fdset_ptr = nullptr;134  fd_set *write_fdset_ptr = nullptr;135  fd_set *error_fdset_ptr = nullptr;136// Initialize and zero out the fdsets137#if defined(__APPLE__)138  llvm::SmallVector<fd_set, 1> read_fdset;139  llvm::SmallVector<fd_set, 1> write_fdset;140  llvm::SmallVector<fd_set, 1> error_fdset;141 142  if (max_read_fd.has_value()) {143    read_fdset.resize((nfds / FD_SETSIZE) + 1);144    read_fdset_ptr = read_fdset.data();145  }146  if (max_write_fd.has_value()) {147    write_fdset.resize((nfds / FD_SETSIZE) + 1);148    write_fdset_ptr = write_fdset.data();149  }150  if (max_error_fd.has_value()) {151    error_fdset.resize((nfds / FD_SETSIZE) + 1);152    error_fdset_ptr = error_fdset.data();153  }154  for (auto &fd_set : read_fdset)155    FD_ZERO(&fd_set);156  for (auto &fd_set : write_fdset)157    FD_ZERO(&fd_set);158  for (auto &fd_set : error_fdset)159    FD_ZERO(&fd_set);160#else161  fd_set read_fdset;162  fd_set write_fdset;163  fd_set error_fdset;164 165  if (max_read_fd) {166    FD_ZERO(&read_fdset);167    read_fdset_ptr = &read_fdset;168  }169  if (max_write_fd) {170    FD_ZERO(&write_fdset);171    write_fdset_ptr = &write_fdset;172  }173  if (max_error_fd) {174    FD_ZERO(&error_fdset);175    error_fdset_ptr = &error_fdset;176  }177#endif178  // Set the FD bits in the fdsets for read/write/error179  for (auto &pair : m_fd_map) {180    lldb::socket_t fd = pair.first;181 182    if (pair.second.read_set)183      FD_SET(fd, read_fdset_ptr);184 185    if (pair.second.write_set)186      FD_SET(fd, write_fdset_ptr);187 188    if (pair.second.error_set)189      FD_SET(fd, error_fdset_ptr);190  }191 192  // Setup our timeout time value if needed193  struct timeval *tv_ptr = nullptr;194  struct timeval tv = {0, 0};195 196  while (true) {197    using namespace std::chrono;198    // Setup out relative timeout based on the end time if we have one199    if (m_end_time) {200      tv_ptr = &tv;201      const auto remaining_dur =202          duration_cast<microseconds>(*m_end_time - steady_clock::now());203      if (remaining_dur.count() > 0) {204        // Wait for a specific amount of time205        const auto dur_secs = duration_cast<seconds>(remaining_dur);206        const auto dur_usecs = remaining_dur % seconds(1);207        tv.tv_sec = dur_secs.count();208        tv.tv_usec = dur_usecs.count();209      } else {210        // Just poll once with no timeout211        tv.tv_sec = 0;212        tv.tv_usec = 0;213      }214    }215    const int num_set_fds = ::select(nfds, read_fdset_ptr, write_fdset_ptr,216                                     error_fdset_ptr, tv_ptr);217    if (num_set_fds < 0) {218      // We got an error219      error = lldb_private::Status::FromErrno();220      if (error.GetError() == EINTR) {221        error.Clear();222        continue; // Keep calling select if we get EINTR223      } else224        return error;225    } else if (num_set_fds == 0) {226      // Timeout227      return lldb_private::Status(ETIMEDOUT, lldb::eErrorTypePOSIX,228                                  "timed out");229    } else {230      // One or more descriptors were set, update the FDInfo::select_is_set231      // mask so users can ask the SelectHelper class so clients can call one232      // of:233 234      for (auto &pair : m_fd_map) {235        const int fd = pair.first;236 237        if (pair.second.read_set) {238          if (FD_ISSET(fd, read_fdset_ptr))239            pair.second.read_is_set = true;240        }241        if (pair.second.write_set) {242          if (FD_ISSET(fd, write_fdset_ptr))243            pair.second.write_is_set = true;244        }245        if (pair.second.error_set) {246          if (FD_ISSET(fd, error_fdset_ptr))247            pair.second.error_is_set = true;248        }249      }250      break;251    }252  }253  return error;254}255