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