Path: blob/main/contrib/llvm-project/lldb/source/Utility/SelectHelper.cpp
39587 views
//===-- SelectHelper.cpp --------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#if defined(__APPLE__)9// Enable this special support for Apple builds where we can have unlimited10// select bounds. We tried switching to poll() and kqueue and we were panicing11// the kernel, so we have to stick with select for now.12#define _DARWIN_UNLIMITED_SELECT13#endif1415#include "lldb/Utility/SelectHelper.h"16#include "lldb/Utility/LLDBAssert.h"17#include "lldb/Utility/Status.h"18#include "lldb/lldb-enumerations.h"19#include "lldb/lldb-types.h"2021#include "llvm/ADT/DenseMap.h"2223#include <algorithm>24#include <chrono>25#include <optional>2627#include <cerrno>28#if defined(_WIN32)29// Define NOMINMAX to avoid macros that conflict with std::min and std::max30#define NOMINMAX31#include <winsock2.h>32#else33#include <sys/time.h>34#include <sys/select.h>35#endif363738SelectHelper::SelectHelper()39: m_fd_map(), m_end_time() // Infinite timeout unless40// SelectHelper::SetTimeout() gets called41{}4243void SelectHelper::SetTimeout(const std::chrono::microseconds &timeout) {44using namespace std::chrono;45m_end_time = steady_clock::time_point(steady_clock::now() + timeout);46}4748void SelectHelper::FDSetRead(lldb::socket_t fd) {49m_fd_map[fd].read_set = true;50}5152void SelectHelper::FDSetWrite(lldb::socket_t fd) {53m_fd_map[fd].write_set = true;54}5556void SelectHelper::FDSetError(lldb::socket_t fd) {57m_fd_map[fd].error_set = true;58}5960bool SelectHelper::FDIsSetRead(lldb::socket_t fd) const {61auto pos = m_fd_map.find(fd);62if (pos != m_fd_map.end())63return pos->second.read_is_set;64else65return false;66}6768bool SelectHelper::FDIsSetWrite(lldb::socket_t fd) const {69auto pos = m_fd_map.find(fd);70if (pos != m_fd_map.end())71return pos->second.write_is_set;72else73return false;74}7576bool SelectHelper::FDIsSetError(lldb::socket_t fd) const {77auto pos = m_fd_map.find(fd);78if (pos != m_fd_map.end())79return pos->second.error_is_set;80else81return false;82}8384static void updateMaxFd(std::optional<lldb::socket_t> &vold,85lldb::socket_t vnew) {86if (!vold)87vold = vnew;88else89vold = std::max(*vold, vnew);90}9192lldb_private::Status SelectHelper::Select() {93lldb_private::Status error;94#ifdef _WIN3295// On windows FD_SETSIZE limits the number of file descriptors, not their96// numeric value.97lldbassert(m_fd_map.size() <= FD_SETSIZE);98if (m_fd_map.size() > FD_SETSIZE)99return lldb_private::Status("Too many file descriptors for select()");100#endif101102std::optional<lldb::socket_t> max_read_fd;103std::optional<lldb::socket_t> max_write_fd;104std::optional<lldb::socket_t> max_error_fd;105std::optional<lldb::socket_t> max_fd;106for (auto &pair : m_fd_map) {107pair.second.PrepareForSelect();108const lldb::socket_t fd = pair.first;109#if !defined(__APPLE__) && !defined(_WIN32)110lldbassert(fd < static_cast<int>(FD_SETSIZE));111if (fd >= static_cast<int>(FD_SETSIZE)) {112error.SetErrorStringWithFormat("%i is too large for select()", fd);113return error;114}115#endif116if (pair.second.read_set)117updateMaxFd(max_read_fd, fd);118if (pair.second.write_set)119updateMaxFd(max_write_fd, fd);120if (pair.second.error_set)121updateMaxFd(max_error_fd, fd);122updateMaxFd(max_fd, fd);123}124125if (!max_fd) {126error.SetErrorString("no valid file descriptors");127return error;128}129130const unsigned nfds = static_cast<unsigned>(*max_fd) + 1;131fd_set *read_fdset_ptr = nullptr;132fd_set *write_fdset_ptr = nullptr;133fd_set *error_fdset_ptr = nullptr;134// Initialize and zero out the fdsets135#if defined(__APPLE__)136llvm::SmallVector<fd_set, 1> read_fdset;137llvm::SmallVector<fd_set, 1> write_fdset;138llvm::SmallVector<fd_set, 1> error_fdset;139140if (max_read_fd.has_value()) {141read_fdset.resize((nfds / FD_SETSIZE) + 1);142read_fdset_ptr = read_fdset.data();143}144if (max_write_fd.has_value()) {145write_fdset.resize((nfds / FD_SETSIZE) + 1);146write_fdset_ptr = write_fdset.data();147}148if (max_error_fd.has_value()) {149error_fdset.resize((nfds / FD_SETSIZE) + 1);150error_fdset_ptr = error_fdset.data();151}152for (auto &fd_set : read_fdset)153FD_ZERO(&fd_set);154for (auto &fd_set : write_fdset)155FD_ZERO(&fd_set);156for (auto &fd_set : error_fdset)157FD_ZERO(&fd_set);158#else159fd_set read_fdset;160fd_set write_fdset;161fd_set error_fdset;162163if (max_read_fd) {164FD_ZERO(&read_fdset);165read_fdset_ptr = &read_fdset;166}167if (max_write_fd) {168FD_ZERO(&write_fdset);169write_fdset_ptr = &write_fdset;170}171if (max_error_fd) {172FD_ZERO(&error_fdset);173error_fdset_ptr = &error_fdset;174}175#endif176// Set the FD bits in the fdsets for read/write/error177for (auto &pair : m_fd_map) {178const lldb::socket_t fd = pair.first;179180if (pair.second.read_set)181FD_SET(fd, read_fdset_ptr);182183if (pair.second.write_set)184FD_SET(fd, write_fdset_ptr);185186if (pair.second.error_set)187FD_SET(fd, error_fdset_ptr);188}189190// Setup our timeout time value if needed191struct timeval *tv_ptr = nullptr;192struct timeval tv = {0, 0};193194while (true) {195using namespace std::chrono;196// Setup out relative timeout based on the end time if we have one197if (m_end_time) {198tv_ptr = &tv;199const auto remaining_dur =200duration_cast<microseconds>(*m_end_time - steady_clock::now());201if (remaining_dur.count() > 0) {202// Wait for a specific amount of time203const auto dur_secs = duration_cast<seconds>(remaining_dur);204const auto dur_usecs = remaining_dur % seconds(1);205tv.tv_sec = dur_secs.count();206tv.tv_usec = dur_usecs.count();207} else {208// Just poll once with no timeout209tv.tv_sec = 0;210tv.tv_usec = 0;211}212}213const int num_set_fds = ::select(nfds, read_fdset_ptr, write_fdset_ptr,214error_fdset_ptr, tv_ptr);215if (num_set_fds < 0) {216// We got an error217error.SetErrorToErrno();218if (error.GetError() == EINTR) {219error.Clear();220continue; // Keep calling select if we get EINTR221} else222return error;223} else if (num_set_fds == 0) {224// Timeout225error.SetError(ETIMEDOUT, lldb::eErrorTypePOSIX);226error.SetErrorString("timed out");227return error;228} else {229// One or more descriptors were set, update the FDInfo::select_is_set230// mask so users can ask the SelectHelper class so clients can call one231// of:232233for (auto &pair : m_fd_map) {234const int fd = pair.first;235236if (pair.second.read_set) {237if (FD_ISSET(fd, read_fdset_ptr))238pair.second.read_is_set = true;239}240if (pair.second.write_set) {241if (FD_ISSET(fd, write_fdset_ptr))242pair.second.write_is_set = true;243}244if (pair.second.error_set) {245if (FD_ISSET(fd, error_fdset_ptr))246pair.second.error_is_set = true;247}248}249break;250}251}252return error;253}254255256