15ffd83dbSDimitry Andric //===-- SelectHelper.cpp --------------------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric
90b57cec5SDimitry Andric #if defined(__APPLE__)
100b57cec5SDimitry Andric // Enable this special support for Apple builds where we can have unlimited
110b57cec5SDimitry Andric // select bounds. We tried switching to poll() and kqueue and we were panicing
120b57cec5SDimitry Andric // the kernel, so we have to stick with select for now.
130b57cec5SDimitry Andric #define _DARWIN_UNLIMITED_SELECT
140b57cec5SDimitry Andric #endif
150b57cec5SDimitry Andric
160b57cec5SDimitry Andric #include "lldb/Utility/SelectHelper.h"
170b57cec5SDimitry Andric #include "lldb/Utility/LLDBAssert.h"
180b57cec5SDimitry Andric #include "lldb/Utility/Status.h"
190b57cec5SDimitry Andric #include "lldb/lldb-enumerations.h"
200b57cec5SDimitry Andric #include "lldb/lldb-types.h"
210b57cec5SDimitry Andric
220b57cec5SDimitry Andric #include "llvm/ADT/DenseMap.h"
230b57cec5SDimitry Andric #include "llvm/ADT/Optional.h"
240b57cec5SDimitry Andric
250b57cec5SDimitry Andric #include <algorithm>
260b57cec5SDimitry Andric #include <chrono>
270b57cec5SDimitry Andric
28*5f7ddb14SDimitry Andric #include <cerrno>
290b57cec5SDimitry Andric #if defined(_WIN32)
300b57cec5SDimitry Andric // Define NOMINMAX to avoid macros that conflict with std::min and std::max
310b57cec5SDimitry Andric #define NOMINMAX
320b57cec5SDimitry Andric #include <winsock2.h>
330b57cec5SDimitry Andric #else
340b57cec5SDimitry Andric #include <sys/time.h>
350b57cec5SDimitry Andric #include <sys/select.h>
360b57cec5SDimitry Andric #endif
370b57cec5SDimitry Andric
380b57cec5SDimitry Andric
SelectHelper()390b57cec5SDimitry Andric SelectHelper::SelectHelper()
400b57cec5SDimitry Andric : m_fd_map(), m_end_time() // Infinite timeout unless
410b57cec5SDimitry Andric // SelectHelper::SetTimeout() gets called
420b57cec5SDimitry Andric {}
430b57cec5SDimitry Andric
SetTimeout(const std::chrono::microseconds & timeout)440b57cec5SDimitry Andric void SelectHelper::SetTimeout(const std::chrono::microseconds &timeout) {
450b57cec5SDimitry Andric using namespace std::chrono;
460b57cec5SDimitry Andric m_end_time = steady_clock::time_point(steady_clock::now() + timeout);
470b57cec5SDimitry Andric }
480b57cec5SDimitry Andric
FDSetRead(lldb::socket_t fd)490b57cec5SDimitry Andric void SelectHelper::FDSetRead(lldb::socket_t fd) {
500b57cec5SDimitry Andric m_fd_map[fd].read_set = true;
510b57cec5SDimitry Andric }
520b57cec5SDimitry Andric
FDSetWrite(lldb::socket_t fd)530b57cec5SDimitry Andric void SelectHelper::FDSetWrite(lldb::socket_t fd) {
540b57cec5SDimitry Andric m_fd_map[fd].write_set = true;
550b57cec5SDimitry Andric }
560b57cec5SDimitry Andric
FDSetError(lldb::socket_t fd)570b57cec5SDimitry Andric void SelectHelper::FDSetError(lldb::socket_t fd) {
580b57cec5SDimitry Andric m_fd_map[fd].error_set = true;
590b57cec5SDimitry Andric }
600b57cec5SDimitry Andric
FDIsSetRead(lldb::socket_t fd) const610b57cec5SDimitry Andric bool SelectHelper::FDIsSetRead(lldb::socket_t fd) const {
620b57cec5SDimitry Andric auto pos = m_fd_map.find(fd);
630b57cec5SDimitry Andric if (pos != m_fd_map.end())
640b57cec5SDimitry Andric return pos->second.read_is_set;
650b57cec5SDimitry Andric else
660b57cec5SDimitry Andric return false;
670b57cec5SDimitry Andric }
680b57cec5SDimitry Andric
FDIsSetWrite(lldb::socket_t fd) const690b57cec5SDimitry Andric bool SelectHelper::FDIsSetWrite(lldb::socket_t fd) const {
700b57cec5SDimitry Andric auto pos = m_fd_map.find(fd);
710b57cec5SDimitry Andric if (pos != m_fd_map.end())
720b57cec5SDimitry Andric return pos->second.write_is_set;
730b57cec5SDimitry Andric else
740b57cec5SDimitry Andric return false;
750b57cec5SDimitry Andric }
760b57cec5SDimitry Andric
FDIsSetError(lldb::socket_t fd) const770b57cec5SDimitry Andric bool SelectHelper::FDIsSetError(lldb::socket_t fd) const {
780b57cec5SDimitry Andric auto pos = m_fd_map.find(fd);
790b57cec5SDimitry Andric if (pos != m_fd_map.end())
800b57cec5SDimitry Andric return pos->second.error_is_set;
810b57cec5SDimitry Andric else
820b57cec5SDimitry Andric return false;
830b57cec5SDimitry Andric }
840b57cec5SDimitry Andric
updateMaxFd(llvm::Optional<lldb::socket_t> & vold,lldb::socket_t vnew)850b57cec5SDimitry Andric static void updateMaxFd(llvm::Optional<lldb::socket_t> &vold,
860b57cec5SDimitry Andric lldb::socket_t vnew) {
870b57cec5SDimitry Andric if (!vold.hasValue())
880b57cec5SDimitry Andric vold = vnew;
890b57cec5SDimitry Andric else
900b57cec5SDimitry Andric vold = std::max(*vold, vnew);
910b57cec5SDimitry Andric }
920b57cec5SDimitry Andric
Select()930b57cec5SDimitry Andric lldb_private::Status SelectHelper::Select() {
940b57cec5SDimitry Andric lldb_private::Status error;
959dba64beSDimitry Andric #ifdef _WIN32
960b57cec5SDimitry Andric // On windows FD_SETSIZE limits the number of file descriptors, not their
970b57cec5SDimitry Andric // numeric value.
980b57cec5SDimitry Andric lldbassert(m_fd_map.size() <= FD_SETSIZE);
990b57cec5SDimitry Andric if (m_fd_map.size() > FD_SETSIZE)
1000b57cec5SDimitry Andric return lldb_private::Status("Too many file descriptors for select()");
1010b57cec5SDimitry Andric #endif
1020b57cec5SDimitry Andric
1030b57cec5SDimitry Andric llvm::Optional<lldb::socket_t> max_read_fd;
1040b57cec5SDimitry Andric llvm::Optional<lldb::socket_t> max_write_fd;
1050b57cec5SDimitry Andric llvm::Optional<lldb::socket_t> max_error_fd;
1060b57cec5SDimitry Andric llvm::Optional<lldb::socket_t> max_fd;
1070b57cec5SDimitry Andric for (auto &pair : m_fd_map) {
1080b57cec5SDimitry Andric pair.second.PrepareForSelect();
1090b57cec5SDimitry Andric const lldb::socket_t fd = pair.first;
1109dba64beSDimitry Andric #if !defined(__APPLE__) && !defined(_WIN32)
1110b57cec5SDimitry Andric lldbassert(fd < static_cast<int>(FD_SETSIZE));
1120b57cec5SDimitry Andric if (fd >= static_cast<int>(FD_SETSIZE)) {
1130b57cec5SDimitry Andric error.SetErrorStringWithFormat("%i is too large for select()", fd);
1140b57cec5SDimitry Andric return error;
1150b57cec5SDimitry Andric }
1160b57cec5SDimitry Andric #endif
1170b57cec5SDimitry Andric if (pair.second.read_set)
1180b57cec5SDimitry Andric updateMaxFd(max_read_fd, fd);
1190b57cec5SDimitry Andric if (pair.second.write_set)
1200b57cec5SDimitry Andric updateMaxFd(max_write_fd, fd);
1210b57cec5SDimitry Andric if (pair.second.error_set)
1220b57cec5SDimitry Andric updateMaxFd(max_error_fd, fd);
1230b57cec5SDimitry Andric updateMaxFd(max_fd, fd);
1240b57cec5SDimitry Andric }
1250b57cec5SDimitry Andric
1260b57cec5SDimitry Andric if (!max_fd.hasValue()) {
1270b57cec5SDimitry Andric error.SetErrorString("no valid file descriptors");
1280b57cec5SDimitry Andric return error;
1290b57cec5SDimitry Andric }
1300b57cec5SDimitry Andric
1310b57cec5SDimitry Andric const unsigned nfds = static_cast<unsigned>(*max_fd) + 1;
1320b57cec5SDimitry Andric fd_set *read_fdset_ptr = nullptr;
1330b57cec5SDimitry Andric fd_set *write_fdset_ptr = nullptr;
1340b57cec5SDimitry Andric fd_set *error_fdset_ptr = nullptr;
1350b57cec5SDimitry Andric // Initialize and zero out the fdsets
1360b57cec5SDimitry Andric #if defined(__APPLE__)
1370b57cec5SDimitry Andric llvm::SmallVector<fd_set, 1> read_fdset;
1380b57cec5SDimitry Andric llvm::SmallVector<fd_set, 1> write_fdset;
1390b57cec5SDimitry Andric llvm::SmallVector<fd_set, 1> error_fdset;
1400b57cec5SDimitry Andric
1410b57cec5SDimitry Andric if (max_read_fd.hasValue()) {
1420b57cec5SDimitry Andric read_fdset.resize((nfds / FD_SETSIZE) + 1);
1430b57cec5SDimitry Andric read_fdset_ptr = read_fdset.data();
1440b57cec5SDimitry Andric }
1450b57cec5SDimitry Andric if (max_write_fd.hasValue()) {
1460b57cec5SDimitry Andric write_fdset.resize((nfds / FD_SETSIZE) + 1);
1470b57cec5SDimitry Andric write_fdset_ptr = write_fdset.data();
1480b57cec5SDimitry Andric }
1490b57cec5SDimitry Andric if (max_error_fd.hasValue()) {
1500b57cec5SDimitry Andric error_fdset.resize((nfds / FD_SETSIZE) + 1);
1510b57cec5SDimitry Andric error_fdset_ptr = error_fdset.data();
1520b57cec5SDimitry Andric }
1530b57cec5SDimitry Andric for (auto &fd_set : read_fdset)
1540b57cec5SDimitry Andric FD_ZERO(&fd_set);
1550b57cec5SDimitry Andric for (auto &fd_set : write_fdset)
1560b57cec5SDimitry Andric FD_ZERO(&fd_set);
1570b57cec5SDimitry Andric for (auto &fd_set : error_fdset)
1580b57cec5SDimitry Andric FD_ZERO(&fd_set);
1590b57cec5SDimitry Andric #else
1600b57cec5SDimitry Andric fd_set read_fdset;
1610b57cec5SDimitry Andric fd_set write_fdset;
1620b57cec5SDimitry Andric fd_set error_fdset;
1630b57cec5SDimitry Andric
1640b57cec5SDimitry Andric if (max_read_fd.hasValue()) {
1650b57cec5SDimitry Andric FD_ZERO(&read_fdset);
1660b57cec5SDimitry Andric read_fdset_ptr = &read_fdset;
1670b57cec5SDimitry Andric }
1680b57cec5SDimitry Andric if (max_write_fd.hasValue()) {
1690b57cec5SDimitry Andric FD_ZERO(&write_fdset);
1700b57cec5SDimitry Andric write_fdset_ptr = &write_fdset;
1710b57cec5SDimitry Andric }
1720b57cec5SDimitry Andric if (max_error_fd.hasValue()) {
1730b57cec5SDimitry Andric FD_ZERO(&error_fdset);
1740b57cec5SDimitry Andric error_fdset_ptr = &error_fdset;
1750b57cec5SDimitry Andric }
1760b57cec5SDimitry Andric #endif
1770b57cec5SDimitry Andric // Set the FD bits in the fdsets for read/write/error
1780b57cec5SDimitry Andric for (auto &pair : m_fd_map) {
1790b57cec5SDimitry Andric const lldb::socket_t fd = pair.first;
1800b57cec5SDimitry Andric
1810b57cec5SDimitry Andric if (pair.second.read_set)
1820b57cec5SDimitry Andric FD_SET(fd, read_fdset_ptr);
1830b57cec5SDimitry Andric
1840b57cec5SDimitry Andric if (pair.second.write_set)
1850b57cec5SDimitry Andric FD_SET(fd, write_fdset_ptr);
1860b57cec5SDimitry Andric
1870b57cec5SDimitry Andric if (pair.second.error_set)
1880b57cec5SDimitry Andric FD_SET(fd, error_fdset_ptr);
1890b57cec5SDimitry Andric }
1900b57cec5SDimitry Andric
1910b57cec5SDimitry Andric // Setup our timeout time value if needed
1920b57cec5SDimitry Andric struct timeval *tv_ptr = nullptr;
1930b57cec5SDimitry Andric struct timeval tv = {0, 0};
1940b57cec5SDimitry Andric
1950b57cec5SDimitry Andric while (true) {
1960b57cec5SDimitry Andric using namespace std::chrono;
1970b57cec5SDimitry Andric // Setup out relative timeout based on the end time if we have one
1980b57cec5SDimitry Andric if (m_end_time.hasValue()) {
1990b57cec5SDimitry Andric tv_ptr = &tv;
2000b57cec5SDimitry Andric const auto remaining_dur = duration_cast<microseconds>(
2010b57cec5SDimitry Andric m_end_time.getValue() - steady_clock::now());
2020b57cec5SDimitry Andric if (remaining_dur.count() > 0) {
2030b57cec5SDimitry Andric // Wait for a specific amount of time
2040b57cec5SDimitry Andric const auto dur_secs = duration_cast<seconds>(remaining_dur);
2050b57cec5SDimitry Andric const auto dur_usecs = remaining_dur % seconds(1);
2060b57cec5SDimitry Andric tv.tv_sec = dur_secs.count();
2070b57cec5SDimitry Andric tv.tv_usec = dur_usecs.count();
2080b57cec5SDimitry Andric } else {
2090b57cec5SDimitry Andric // Just poll once with no timeout
2100b57cec5SDimitry Andric tv.tv_sec = 0;
2110b57cec5SDimitry Andric tv.tv_usec = 0;
2120b57cec5SDimitry Andric }
2130b57cec5SDimitry Andric }
2140b57cec5SDimitry Andric const int num_set_fds = ::select(nfds, read_fdset_ptr, write_fdset_ptr,
2150b57cec5SDimitry Andric error_fdset_ptr, tv_ptr);
2160b57cec5SDimitry Andric if (num_set_fds < 0) {
2170b57cec5SDimitry Andric // We got an error
2180b57cec5SDimitry Andric error.SetErrorToErrno();
2190b57cec5SDimitry Andric if (error.GetError() == EINTR) {
2200b57cec5SDimitry Andric error.Clear();
2210b57cec5SDimitry Andric continue; // Keep calling select if we get EINTR
2220b57cec5SDimitry Andric } else
2230b57cec5SDimitry Andric return error;
2240b57cec5SDimitry Andric } else if (num_set_fds == 0) {
2250b57cec5SDimitry Andric // Timeout
2260b57cec5SDimitry Andric error.SetError(ETIMEDOUT, lldb::eErrorTypePOSIX);
2270b57cec5SDimitry Andric error.SetErrorString("timed out");
2280b57cec5SDimitry Andric return error;
2290b57cec5SDimitry Andric } else {
2300b57cec5SDimitry Andric // One or more descriptors were set, update the FDInfo::select_is_set
2310b57cec5SDimitry Andric // mask so users can ask the SelectHelper class so clients can call one
2320b57cec5SDimitry Andric // of:
2330b57cec5SDimitry Andric
2340b57cec5SDimitry Andric for (auto &pair : m_fd_map) {
2350b57cec5SDimitry Andric const int fd = pair.first;
2360b57cec5SDimitry Andric
2370b57cec5SDimitry Andric if (pair.second.read_set) {
2380b57cec5SDimitry Andric if (FD_ISSET(fd, read_fdset_ptr))
2390b57cec5SDimitry Andric pair.second.read_is_set = true;
2400b57cec5SDimitry Andric }
2410b57cec5SDimitry Andric if (pair.second.write_set) {
2420b57cec5SDimitry Andric if (FD_ISSET(fd, write_fdset_ptr))
2430b57cec5SDimitry Andric pair.second.write_is_set = true;
2440b57cec5SDimitry Andric }
2450b57cec5SDimitry Andric if (pair.second.error_set) {
2460b57cec5SDimitry Andric if (FD_ISSET(fd, error_fdset_ptr))
2470b57cec5SDimitry Andric pair.second.error_is_set = true;
2480b57cec5SDimitry Andric }
2490b57cec5SDimitry Andric }
2500b57cec5SDimitry Andric break;
2510b57cec5SDimitry Andric }
2520b57cec5SDimitry Andric }
2530b57cec5SDimitry Andric return error;
2540b57cec5SDimitry Andric }
255