xref: /src/contrib/llvm-project/lldb/source/Utility/SelectHelper.cpp (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
1cfca06d7SDimitry Andric //===-- SelectHelper.cpp --------------------------------------------------===//
214f1b3e8SDimitry Andric //
35f29bb8aSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45f29bb8aSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
55f29bb8aSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
614f1b3e8SDimitry Andric //
714f1b3e8SDimitry Andric //===----------------------------------------------------------------------===//
814f1b3e8SDimitry Andric 
914f1b3e8SDimitry Andric #if defined(__APPLE__)
1014f1b3e8SDimitry Andric // Enable this special support for Apple builds where we can have unlimited
1114f1b3e8SDimitry Andric // select bounds. We tried switching to poll() and kqueue and we were panicing
1214f1b3e8SDimitry Andric // the kernel, so we have to stick with select for now.
1314f1b3e8SDimitry Andric #define _DARWIN_UNLIMITED_SELECT
1414f1b3e8SDimitry Andric #endif
1514f1b3e8SDimitry Andric 
1674a628f7SDimitry Andric #include "lldb/Utility/SelectHelper.h"
1774a628f7SDimitry Andric #include "lldb/Utility/LLDBAssert.h"
18b76161e4SDimitry Andric #include "lldb/Utility/Status.h"
1994994d37SDimitry Andric #include "lldb/lldb-enumerations.h"
2094994d37SDimitry Andric #include "lldb/lldb-types.h"
2174a628f7SDimitry Andric 
2294994d37SDimitry Andric #include "llvm/ADT/DenseMap.h"
2374a628f7SDimitry Andric 
2474a628f7SDimitry Andric #include <algorithm>
2594994d37SDimitry Andric #include <chrono>
26e3b55780SDimitry Andric #include <optional>
2774a628f7SDimitry Andric 
28344a3780SDimitry Andric #include <cerrno>
2914f1b3e8SDimitry Andric #if defined(_WIN32)
3014f1b3e8SDimitry Andric // Define NOMINMAX to avoid macros that conflict with std::min and std::max
3114f1b3e8SDimitry Andric #define NOMINMAX
3214f1b3e8SDimitry Andric #include <winsock2.h>
3314f1b3e8SDimitry Andric #else
34ef5d0b5eSDimitry Andric #include <sys/time.h>
3514f1b3e8SDimitry Andric #include <sys/select.h>
3614f1b3e8SDimitry Andric #endif
3714f1b3e8SDimitry Andric 
3814f1b3e8SDimitry Andric 
SelectHelper()3914f1b3e8SDimitry Andric SelectHelper::SelectHelper()
4014f1b3e8SDimitry Andric     : m_fd_map(), m_end_time() // Infinite timeout unless
4114f1b3e8SDimitry Andric                                // SelectHelper::SetTimeout() gets called
4214f1b3e8SDimitry Andric {}
4314f1b3e8SDimitry Andric 
SetTimeout(const std::chrono::microseconds & timeout)4414f1b3e8SDimitry Andric void SelectHelper::SetTimeout(const std::chrono::microseconds &timeout) {
4514f1b3e8SDimitry Andric   using namespace std::chrono;
4614f1b3e8SDimitry Andric   m_end_time = steady_clock::time_point(steady_clock::now() + timeout);
4714f1b3e8SDimitry Andric }
4814f1b3e8SDimitry Andric 
FDSetRead(lldb::socket_t fd)4914f1b3e8SDimitry Andric void SelectHelper::FDSetRead(lldb::socket_t fd) {
5014f1b3e8SDimitry Andric   m_fd_map[fd].read_set = true;
5114f1b3e8SDimitry Andric }
5214f1b3e8SDimitry Andric 
FDSetWrite(lldb::socket_t fd)5314f1b3e8SDimitry Andric void SelectHelper::FDSetWrite(lldb::socket_t fd) {
5414f1b3e8SDimitry Andric   m_fd_map[fd].write_set = true;
5514f1b3e8SDimitry Andric }
5614f1b3e8SDimitry Andric 
FDSetError(lldb::socket_t fd)5714f1b3e8SDimitry Andric void SelectHelper::FDSetError(lldb::socket_t fd) {
5814f1b3e8SDimitry Andric   m_fd_map[fd].error_set = true;
5914f1b3e8SDimitry Andric }
6014f1b3e8SDimitry Andric 
FDIsSetRead(lldb::socket_t fd) const6114f1b3e8SDimitry Andric bool SelectHelper::FDIsSetRead(lldb::socket_t fd) const {
6214f1b3e8SDimitry Andric   auto pos = m_fd_map.find(fd);
6314f1b3e8SDimitry Andric   if (pos != m_fd_map.end())
6414f1b3e8SDimitry Andric     return pos->second.read_is_set;
6514f1b3e8SDimitry Andric   else
6614f1b3e8SDimitry Andric     return false;
6714f1b3e8SDimitry Andric }
6814f1b3e8SDimitry Andric 
FDIsSetWrite(lldb::socket_t fd) const6914f1b3e8SDimitry Andric bool SelectHelper::FDIsSetWrite(lldb::socket_t fd) const {
7014f1b3e8SDimitry Andric   auto pos = m_fd_map.find(fd);
7114f1b3e8SDimitry Andric   if (pos != m_fd_map.end())
7214f1b3e8SDimitry Andric     return pos->second.write_is_set;
7314f1b3e8SDimitry Andric   else
7414f1b3e8SDimitry Andric     return false;
7514f1b3e8SDimitry Andric }
7614f1b3e8SDimitry Andric 
FDIsSetError(lldb::socket_t fd) const7714f1b3e8SDimitry Andric bool SelectHelper::FDIsSetError(lldb::socket_t fd) const {
7814f1b3e8SDimitry Andric   auto pos = m_fd_map.find(fd);
7914f1b3e8SDimitry Andric   if (pos != m_fd_map.end())
8014f1b3e8SDimitry Andric     return pos->second.error_is_set;
8114f1b3e8SDimitry Andric   else
8214f1b3e8SDimitry Andric     return false;
8314f1b3e8SDimitry Andric }
8414f1b3e8SDimitry Andric 
updateMaxFd(std::optional<lldb::socket_t> & vold,lldb::socket_t vnew)85e3b55780SDimitry Andric static void updateMaxFd(std::optional<lldb::socket_t> &vold,
8614f1b3e8SDimitry Andric                         lldb::socket_t vnew) {
87145449b1SDimitry Andric   if (!vold)
8814f1b3e8SDimitry Andric     vold = vnew;
8914f1b3e8SDimitry Andric   else
9014f1b3e8SDimitry Andric     vold = std::max(*vold, vnew);
9114f1b3e8SDimitry Andric }
9214f1b3e8SDimitry Andric 
Select()93b76161e4SDimitry Andric lldb_private::Status SelectHelper::Select() {
94b76161e4SDimitry Andric   lldb_private::Status error;
95ead24645SDimitry Andric #ifdef _WIN32
9614f1b3e8SDimitry Andric   // On windows FD_SETSIZE limits the number of file descriptors, not their
9714f1b3e8SDimitry Andric   // numeric value.
9814f1b3e8SDimitry Andric   lldbassert(m_fd_map.size() <= FD_SETSIZE);
9914f1b3e8SDimitry Andric   if (m_fd_map.size() > FD_SETSIZE)
100b76161e4SDimitry Andric     return lldb_private::Status("Too many file descriptors for select()");
10114f1b3e8SDimitry Andric #endif
10214f1b3e8SDimitry Andric 
103e3b55780SDimitry Andric   std::optional<lldb::socket_t> max_read_fd;
104e3b55780SDimitry Andric   std::optional<lldb::socket_t> max_write_fd;
105e3b55780SDimitry Andric   std::optional<lldb::socket_t> max_error_fd;
106e3b55780SDimitry Andric   std::optional<lldb::socket_t> max_fd;
10714f1b3e8SDimitry Andric   for (auto &pair : m_fd_map) {
10814f1b3e8SDimitry Andric     pair.second.PrepareForSelect();
10914f1b3e8SDimitry Andric     const lldb::socket_t fd = pair.first;
110ead24645SDimitry Andric #if !defined(__APPLE__) && !defined(_WIN32)
111f73363f1SDimitry Andric     lldbassert(fd < static_cast<int>(FD_SETSIZE));
112f73363f1SDimitry Andric     if (fd >= static_cast<int>(FD_SETSIZE)) {
11314f1b3e8SDimitry Andric       error.SetErrorStringWithFormat("%i is too large for select()", fd);
11414f1b3e8SDimitry Andric       return error;
11514f1b3e8SDimitry Andric     }
11614f1b3e8SDimitry Andric #endif
11714f1b3e8SDimitry Andric     if (pair.second.read_set)
11814f1b3e8SDimitry Andric       updateMaxFd(max_read_fd, fd);
11914f1b3e8SDimitry Andric     if (pair.second.write_set)
12014f1b3e8SDimitry Andric       updateMaxFd(max_write_fd, fd);
12114f1b3e8SDimitry Andric     if (pair.second.error_set)
12214f1b3e8SDimitry Andric       updateMaxFd(max_error_fd, fd);
12314f1b3e8SDimitry Andric     updateMaxFd(max_fd, fd);
12414f1b3e8SDimitry Andric   }
12514f1b3e8SDimitry Andric 
126145449b1SDimitry Andric   if (!max_fd) {
12714f1b3e8SDimitry Andric     error.SetErrorString("no valid file descriptors");
12814f1b3e8SDimitry Andric     return error;
12914f1b3e8SDimitry Andric   }
13014f1b3e8SDimitry Andric 
13114f1b3e8SDimitry Andric   const unsigned nfds = static_cast<unsigned>(*max_fd) + 1;
13214f1b3e8SDimitry Andric   fd_set *read_fdset_ptr = nullptr;
13314f1b3e8SDimitry Andric   fd_set *write_fdset_ptr = nullptr;
13414f1b3e8SDimitry Andric   fd_set *error_fdset_ptr = nullptr;
13514f1b3e8SDimitry Andric // Initialize and zero out the fdsets
13614f1b3e8SDimitry Andric #if defined(__APPLE__)
13714f1b3e8SDimitry Andric   llvm::SmallVector<fd_set, 1> read_fdset;
13814f1b3e8SDimitry Andric   llvm::SmallVector<fd_set, 1> write_fdset;
13914f1b3e8SDimitry Andric   llvm::SmallVector<fd_set, 1> error_fdset;
14014f1b3e8SDimitry Andric 
141e3b55780SDimitry Andric   if (max_read_fd.has_value()) {
14214f1b3e8SDimitry Andric     read_fdset.resize((nfds / FD_SETSIZE) + 1);
14314f1b3e8SDimitry Andric     read_fdset_ptr = read_fdset.data();
14414f1b3e8SDimitry Andric   }
145e3b55780SDimitry Andric   if (max_write_fd.has_value()) {
14614f1b3e8SDimitry Andric     write_fdset.resize((nfds / FD_SETSIZE) + 1);
14714f1b3e8SDimitry Andric     write_fdset_ptr = write_fdset.data();
14814f1b3e8SDimitry Andric   }
149e3b55780SDimitry Andric   if (max_error_fd.has_value()) {
15014f1b3e8SDimitry Andric     error_fdset.resize((nfds / FD_SETSIZE) + 1);
15114f1b3e8SDimitry Andric     error_fdset_ptr = error_fdset.data();
15214f1b3e8SDimitry Andric   }
15314f1b3e8SDimitry Andric   for (auto &fd_set : read_fdset)
15414f1b3e8SDimitry Andric     FD_ZERO(&fd_set);
15514f1b3e8SDimitry Andric   for (auto &fd_set : write_fdset)
15614f1b3e8SDimitry Andric     FD_ZERO(&fd_set);
15714f1b3e8SDimitry Andric   for (auto &fd_set : error_fdset)
15814f1b3e8SDimitry Andric     FD_ZERO(&fd_set);
15914f1b3e8SDimitry Andric #else
16014f1b3e8SDimitry Andric   fd_set read_fdset;
16114f1b3e8SDimitry Andric   fd_set write_fdset;
16214f1b3e8SDimitry Andric   fd_set error_fdset;
16314f1b3e8SDimitry Andric 
164145449b1SDimitry Andric   if (max_read_fd) {
16514f1b3e8SDimitry Andric     FD_ZERO(&read_fdset);
16614f1b3e8SDimitry Andric     read_fdset_ptr = &read_fdset;
16714f1b3e8SDimitry Andric   }
168145449b1SDimitry Andric   if (max_write_fd) {
16914f1b3e8SDimitry Andric     FD_ZERO(&write_fdset);
17014f1b3e8SDimitry Andric     write_fdset_ptr = &write_fdset;
17114f1b3e8SDimitry Andric   }
172145449b1SDimitry Andric   if (max_error_fd) {
17314f1b3e8SDimitry Andric     FD_ZERO(&error_fdset);
17414f1b3e8SDimitry Andric     error_fdset_ptr = &error_fdset;
17514f1b3e8SDimitry Andric   }
17614f1b3e8SDimitry Andric #endif
17714f1b3e8SDimitry Andric   // Set the FD bits in the fdsets for read/write/error
17814f1b3e8SDimitry Andric   for (auto &pair : m_fd_map) {
17914f1b3e8SDimitry Andric     const lldb::socket_t fd = pair.first;
18014f1b3e8SDimitry Andric 
18114f1b3e8SDimitry Andric     if (pair.second.read_set)
18214f1b3e8SDimitry Andric       FD_SET(fd, read_fdset_ptr);
18314f1b3e8SDimitry Andric 
18414f1b3e8SDimitry Andric     if (pair.second.write_set)
18514f1b3e8SDimitry Andric       FD_SET(fd, write_fdset_ptr);
18614f1b3e8SDimitry Andric 
18714f1b3e8SDimitry Andric     if (pair.second.error_set)
18814f1b3e8SDimitry Andric       FD_SET(fd, error_fdset_ptr);
18914f1b3e8SDimitry Andric   }
19014f1b3e8SDimitry Andric 
19114f1b3e8SDimitry Andric   // Setup our timeout time value if needed
19214f1b3e8SDimitry Andric   struct timeval *tv_ptr = nullptr;
19314f1b3e8SDimitry Andric   struct timeval tv = {0, 0};
19414f1b3e8SDimitry Andric 
1955f29bb8aSDimitry Andric   while (true) {
19614f1b3e8SDimitry Andric     using namespace std::chrono;
19714f1b3e8SDimitry Andric     // Setup out relative timeout based on the end time if we have one
198145449b1SDimitry Andric     if (m_end_time) {
19914f1b3e8SDimitry Andric       tv_ptr = &tv;
2004b4fe385SDimitry Andric       const auto remaining_dur =
201e3b55780SDimitry Andric           duration_cast<microseconds>(*m_end_time - steady_clock::now());
20214f1b3e8SDimitry Andric       if (remaining_dur.count() > 0) {
20314f1b3e8SDimitry Andric         // Wait for a specific amount of time
20414f1b3e8SDimitry Andric         const auto dur_secs = duration_cast<seconds>(remaining_dur);
20514f1b3e8SDimitry Andric         const auto dur_usecs = remaining_dur % seconds(1);
20614f1b3e8SDimitry Andric         tv.tv_sec = dur_secs.count();
20714f1b3e8SDimitry Andric         tv.tv_usec = dur_usecs.count();
20814f1b3e8SDimitry Andric       } else {
20914f1b3e8SDimitry Andric         // Just poll once with no timeout
21014f1b3e8SDimitry Andric         tv.tv_sec = 0;
21114f1b3e8SDimitry Andric         tv.tv_usec = 0;
21214f1b3e8SDimitry Andric       }
21314f1b3e8SDimitry Andric     }
21414f1b3e8SDimitry Andric     const int num_set_fds = ::select(nfds, read_fdset_ptr, write_fdset_ptr,
21514f1b3e8SDimitry Andric                                      error_fdset_ptr, tv_ptr);
21614f1b3e8SDimitry Andric     if (num_set_fds < 0) {
21714f1b3e8SDimitry Andric       // We got an error
21814f1b3e8SDimitry Andric       error.SetErrorToErrno();
21914f1b3e8SDimitry Andric       if (error.GetError() == EINTR) {
22014f1b3e8SDimitry Andric         error.Clear();
22114f1b3e8SDimitry Andric         continue; // Keep calling select if we get EINTR
22214f1b3e8SDimitry Andric       } else
22314f1b3e8SDimitry Andric         return error;
22414f1b3e8SDimitry Andric     } else if (num_set_fds == 0) {
22514f1b3e8SDimitry Andric       // Timeout
22614f1b3e8SDimitry Andric       error.SetError(ETIMEDOUT, lldb::eErrorTypePOSIX);
22714f1b3e8SDimitry Andric       error.SetErrorString("timed out");
22814f1b3e8SDimitry Andric       return error;
22914f1b3e8SDimitry Andric     } else {
230f73363f1SDimitry Andric       // One or more descriptors were set, update the FDInfo::select_is_set
231f73363f1SDimitry Andric       // mask so users can ask the SelectHelper class so clients can call one
232f73363f1SDimitry Andric       // of:
23314f1b3e8SDimitry Andric 
23414f1b3e8SDimitry Andric       for (auto &pair : m_fd_map) {
23514f1b3e8SDimitry Andric         const int fd = pair.first;
23614f1b3e8SDimitry Andric 
23714f1b3e8SDimitry Andric         if (pair.second.read_set) {
23814f1b3e8SDimitry Andric           if (FD_ISSET(fd, read_fdset_ptr))
23914f1b3e8SDimitry Andric             pair.second.read_is_set = true;
24014f1b3e8SDimitry Andric         }
24114f1b3e8SDimitry Andric         if (pair.second.write_set) {
24214f1b3e8SDimitry Andric           if (FD_ISSET(fd, write_fdset_ptr))
24314f1b3e8SDimitry Andric             pair.second.write_is_set = true;
24414f1b3e8SDimitry Andric         }
24514f1b3e8SDimitry Andric         if (pair.second.error_set) {
24614f1b3e8SDimitry Andric           if (FD_ISSET(fd, error_fdset_ptr))
24714f1b3e8SDimitry Andric             pair.second.error_is_set = true;
24814f1b3e8SDimitry Andric         }
24914f1b3e8SDimitry Andric       }
25014f1b3e8SDimitry Andric       break;
25114f1b3e8SDimitry Andric     }
25214f1b3e8SDimitry Andric   }
25314f1b3e8SDimitry Andric   return error;
25414f1b3e8SDimitry Andric }
255