diff options
Diffstat (limited to 'contrib/llvm/tools/lldb/source/Utility/SelectHelper.cpp')
| -rw-r--r-- | contrib/llvm/tools/lldb/source/Utility/SelectHelper.cpp | 260 | 
1 files changed, 260 insertions, 0 deletions
diff --git a/contrib/llvm/tools/lldb/source/Utility/SelectHelper.cpp b/contrib/llvm/tools/lldb/source/Utility/SelectHelper.cpp new file mode 100644 index 000000000000..805bcf2c7950 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Utility/SelectHelper.cpp @@ -0,0 +1,260 @@ +//===-- SelectHelper.cpp ----------------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if defined(__APPLE__) +// Enable this special support for Apple builds where we can have unlimited +// select bounds. We tried switching to poll() and kqueue and we were panicing +// the kernel, so we have to stick with select for now. +#define _DARWIN_UNLIMITED_SELECT +#endif + +// C Includes +#include <errno.h> +#if defined(_WIN32) +// Define NOMINMAX to avoid macros that conflict with std::min and std::max +#define NOMINMAX +#include <winsock2.h> +#else +#include <sys/select.h> +#endif + +// C++ Includes +#include <algorithm> + +// Other libraries and framework includes +#include "llvm/ADT/SmallVector.h" + +// Project includes +#include "lldb/Core/Error.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/SelectHelper.h" + +SelectHelper::SelectHelper() +    : m_fd_map(), m_end_time() // Infinite timeout unless +                               // SelectHelper::SetTimeout() gets called +{} + +void SelectHelper::SetTimeout(const std::chrono::microseconds &timeout) { +  using namespace std::chrono; +  m_end_time = steady_clock::time_point(steady_clock::now() + timeout); +} + +void SelectHelper::FDSetRead(lldb::socket_t fd) { +  m_fd_map[fd].read_set = true; +} + +void SelectHelper::FDSetWrite(lldb::socket_t fd) { +  m_fd_map[fd].write_set = true; +} + +void SelectHelper::FDSetError(lldb::socket_t fd) { +  m_fd_map[fd].error_set = true; +} + +bool SelectHelper::FDIsSetRead(lldb::socket_t fd) const { +  auto pos = m_fd_map.find(fd); +  if (pos != m_fd_map.end()) +    return pos->second.read_is_set; +  else +    return false; +} + +bool SelectHelper::FDIsSetWrite(lldb::socket_t fd) const { +  auto pos = m_fd_map.find(fd); +  if (pos != m_fd_map.end()) +    return pos->second.write_is_set; +  else +    return false; +} + +bool SelectHelper::FDIsSetError(lldb::socket_t fd) const { +  auto pos = m_fd_map.find(fd); +  if (pos != m_fd_map.end()) +    return pos->second.error_is_set; +  else +    return false; +} + +static void updateMaxFd(llvm::Optional<lldb::socket_t> &vold, +                        lldb::socket_t vnew) { +  if (!vold.hasValue()) +    vold = vnew; +  else +    vold = std::max(*vold, vnew); +} + +lldb_private::Error SelectHelper::Select() { +  lldb_private::Error error; +#ifdef _MSC_VER +  // On windows FD_SETSIZE limits the number of file descriptors, not their +  // numeric value. +  lldbassert(m_fd_map.size() <= FD_SETSIZE); +  if (m_fd_map.size() > FD_SETSIZE) +    return lldb_private::Error("Too many file descriptors for select()"); +#endif + +  llvm::Optional<lldb::socket_t> max_read_fd; +  llvm::Optional<lldb::socket_t> max_write_fd; +  llvm::Optional<lldb::socket_t> max_error_fd; +  llvm::Optional<lldb::socket_t> max_fd; +  for (auto &pair : m_fd_map) { +    pair.second.PrepareForSelect(); +    const lldb::socket_t fd = pair.first; +#if !defined(__APPLE__) && !defined(_MSC_VER) +    lldbassert(fd < FD_SETSIZE); +    if (fd >= FD_SETSIZE) { +      error.SetErrorStringWithFormat("%i is too large for select()", fd); +      return error; +    } +#endif +    if (pair.second.read_set) +      updateMaxFd(max_read_fd, fd); +    if (pair.second.write_set) +      updateMaxFd(max_write_fd, fd); +    if (pair.second.error_set) +      updateMaxFd(max_error_fd, fd); +    updateMaxFd(max_fd, fd); +  } + +  if (!max_fd.hasValue()) { +    error.SetErrorString("no valid file descriptors"); +    return error; +  } + +  const unsigned nfds = static_cast<unsigned>(*max_fd) + 1; +  fd_set *read_fdset_ptr = nullptr; +  fd_set *write_fdset_ptr = nullptr; +  fd_set *error_fdset_ptr = nullptr; +//---------------------------------------------------------------------- +// Initialize and zero out the fdsets +//---------------------------------------------------------------------- +#if defined(__APPLE__) +  llvm::SmallVector<fd_set, 1> read_fdset; +  llvm::SmallVector<fd_set, 1> write_fdset; +  llvm::SmallVector<fd_set, 1> error_fdset; + +  if (max_read_fd.hasValue()) { +    read_fdset.resize((nfds / FD_SETSIZE) + 1); +    read_fdset_ptr = read_fdset.data(); +  } +  if (max_write_fd.hasValue()) { +    write_fdset.resize((nfds / FD_SETSIZE) + 1); +    write_fdset_ptr = write_fdset.data(); +  } +  if (max_error_fd.hasValue()) { +    error_fdset.resize((nfds / FD_SETSIZE) + 1); +    error_fdset_ptr = error_fdset.data(); +  } +  for (auto &fd_set : read_fdset) +    FD_ZERO(&fd_set); +  for (auto &fd_set : write_fdset) +    FD_ZERO(&fd_set); +  for (auto &fd_set : error_fdset) +    FD_ZERO(&fd_set); +#else +  fd_set read_fdset; +  fd_set write_fdset; +  fd_set error_fdset; + +  if (max_read_fd.hasValue()) { +    FD_ZERO(&read_fdset); +    read_fdset_ptr = &read_fdset; +  } +  if (max_write_fd.hasValue()) { +    FD_ZERO(&write_fdset); +    write_fdset_ptr = &write_fdset; +  } +  if (max_error_fd.hasValue()) { +    FD_ZERO(&error_fdset); +    error_fdset_ptr = &error_fdset; +  } +#endif +  //---------------------------------------------------------------------- +  // Set the FD bits in the fdsets for read/write/error +  //---------------------------------------------------------------------- +  for (auto &pair : m_fd_map) { +    const lldb::socket_t fd = pair.first; + +    if (pair.second.read_set) +      FD_SET(fd, read_fdset_ptr); + +    if (pair.second.write_set) +      FD_SET(fd, write_fdset_ptr); + +    if (pair.second.error_set) +      FD_SET(fd, error_fdset_ptr); +  } + +  //---------------------------------------------------------------------- +  // Setup our timeout time value if needed +  //---------------------------------------------------------------------- +  struct timeval *tv_ptr = nullptr; +  struct timeval tv = {0, 0}; + +  while (1) { +    using namespace std::chrono; +    //------------------------------------------------------------------ +    // Setup out relative timeout based on the end time if we have one +    //------------------------------------------------------------------ +    if (m_end_time.hasValue()) { +      tv_ptr = &tv; +      const auto remaining_dur = duration_cast<microseconds>( +          m_end_time.getValue() - steady_clock::now()); +      if (remaining_dur.count() > 0) { +        // Wait for a specific amount of time +        const auto dur_secs = duration_cast<seconds>(remaining_dur); +        const auto dur_usecs = remaining_dur % seconds(1); +        tv.tv_sec = dur_secs.count(); +        tv.tv_usec = dur_usecs.count(); +      } else { +        // Just poll once with no timeout +        tv.tv_sec = 0; +        tv.tv_usec = 0; +      } +    } +    const int num_set_fds = ::select(nfds, read_fdset_ptr, write_fdset_ptr, +                                     error_fdset_ptr, tv_ptr); +    if (num_set_fds < 0) { +      // We got an error +      error.SetErrorToErrno(); +      if (error.GetError() == EINTR) { +        error.Clear(); +        continue; // Keep calling select if we get EINTR +      } else +        return error; +    } else if (num_set_fds == 0) { +      // Timeout +      error.SetError(ETIMEDOUT, lldb::eErrorTypePOSIX); +      error.SetErrorString("timed out"); +      return error; +    } else { +      // One or more descriptors were set, update the FDInfo::select_is_set mask +      // so users can ask the SelectHelper class so clients can call one of: + +      for (auto &pair : m_fd_map) { +        const int fd = pair.first; + +        if (pair.second.read_set) { +          if (FD_ISSET(fd, read_fdset_ptr)) +            pair.second.read_is_set = true; +        } +        if (pair.second.write_set) { +          if (FD_ISSET(fd, write_fdset_ptr)) +            pair.second.write_is_set = true; +        } +        if (pair.second.error_set) { +          if (FD_ISSET(fd, error_fdset_ptr)) +            pair.second.error_is_set = true; +        } +      } +      break; +    } +  } +  return error; +}  | 
