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