180814287SRaphael Isemann //===-- SelectHelper.cpp --------------------------------------------------===//
2ee1f578dSGreg Clayton //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6ee1f578dSGreg Clayton //
7ee1f578dSGreg Clayton //===----------------------------------------------------------------------===//
8ee1f578dSGreg Clayton 
9ee1f578dSGreg Clayton #if defined(__APPLE__)
10ee1f578dSGreg Clayton // Enable this special support for Apple builds where we can have unlimited
11ee1f578dSGreg Clayton // select bounds. We tried switching to poll() and kqueue and we were panicing
12ee1f578dSGreg Clayton // the kernel, so we have to stick with select for now.
13ee1f578dSGreg Clayton #define _DARWIN_UNLIMITED_SELECT
14ee1f578dSGreg Clayton #endif
15ee1f578dSGreg Clayton 
164479ac15SZachary Turner #include "lldb/Utility/SelectHelper.h"
174479ac15SZachary Turner #include "lldb/Utility/LLDBAssert.h"
1897206d57SZachary Turner #include "lldb/Utility/Status.h"
19672d2c12SJonas Devlieghere #include "lldb/lldb-enumerations.h"
20672d2c12SJonas Devlieghere #include "lldb/lldb-types.h"
214479ac15SZachary Turner 
22672d2c12SJonas Devlieghere #include "llvm/ADT/DenseMap.h"
23672d2c12SJonas Devlieghere #include "llvm/ADT/Optional.h"
244479ac15SZachary Turner 
254479ac15SZachary Turner #include <algorithm>
26672d2c12SJonas Devlieghere #include <chrono>
274479ac15SZachary Turner 
2876e47d48SRaphael Isemann #include <cerrno>
29ee1f578dSGreg Clayton #if defined(_WIN32)
30ee1f578dSGreg Clayton // Define NOMINMAX to avoid macros that conflict with std::min and std::max
31ee1f578dSGreg Clayton #define NOMINMAX
32ee1f578dSGreg Clayton #include <winsock2.h>
33ee1f578dSGreg Clayton #else
34c8e1c094SVedant Kumar #include <sys/time.h>
35ee1f578dSGreg Clayton #include <sys/select.h>
36ee1f578dSGreg Clayton #endif
37ee1f578dSGreg Clayton 
38ee1f578dSGreg Clayton 
SelectHelper()39b9c1b51eSKate Stone SelectHelper::SelectHelper()
40b9c1b51eSKate Stone     : m_fd_map(), m_end_time() // Infinite timeout unless
41b9c1b51eSKate Stone                                // SelectHelper::SetTimeout() gets called
42b9c1b51eSKate Stone {}
43ee1f578dSGreg Clayton 
SetTimeout(const std::chrono::microseconds & timeout)44b9c1b51eSKate Stone void SelectHelper::SetTimeout(const std::chrono::microseconds &timeout) {
45ee1f578dSGreg Clayton   using namespace std::chrono;
46ee1f578dSGreg Clayton   m_end_time = steady_clock::time_point(steady_clock::now() + timeout);
47ee1f578dSGreg Clayton }
48ee1f578dSGreg Clayton 
FDSetRead(lldb::socket_t fd)495a8ad459SZachary Turner void SelectHelper::FDSetRead(lldb::socket_t fd) {
505a8ad459SZachary Turner   m_fd_map[fd].read_set = true;
515a8ad459SZachary Turner }
52ee1f578dSGreg Clayton 
FDSetWrite(lldb::socket_t fd)535a8ad459SZachary Turner void SelectHelper::FDSetWrite(lldb::socket_t fd) {
545a8ad459SZachary Turner   m_fd_map[fd].write_set = true;
555a8ad459SZachary Turner }
56ee1f578dSGreg Clayton 
FDSetError(lldb::socket_t fd)575a8ad459SZachary Turner void SelectHelper::FDSetError(lldb::socket_t fd) {
585a8ad459SZachary Turner   m_fd_map[fd].error_set = true;
595a8ad459SZachary Turner }
60ee1f578dSGreg Clayton 
FDIsSetRead(lldb::socket_t fd) const615a8ad459SZachary Turner bool SelectHelper::FDIsSetRead(lldb::socket_t fd) const {
62ee1f578dSGreg Clayton   auto pos = m_fd_map.find(fd);
63ee1f578dSGreg Clayton   if (pos != m_fd_map.end())
64ee1f578dSGreg Clayton     return pos->second.read_is_set;
65ee1f578dSGreg Clayton   else
66ee1f578dSGreg Clayton     return false;
67ee1f578dSGreg Clayton }
68ee1f578dSGreg Clayton 
FDIsSetWrite(lldb::socket_t fd) const695a8ad459SZachary Turner bool SelectHelper::FDIsSetWrite(lldb::socket_t fd) const {
70ee1f578dSGreg Clayton   auto pos = m_fd_map.find(fd);
71ee1f578dSGreg Clayton   if (pos != m_fd_map.end())
72ee1f578dSGreg Clayton     return pos->second.write_is_set;
73ee1f578dSGreg Clayton   else
74ee1f578dSGreg Clayton     return false;
75ee1f578dSGreg Clayton }
76ee1f578dSGreg Clayton 
FDIsSetError(lldb::socket_t fd) const775a8ad459SZachary Turner bool SelectHelper::FDIsSetError(lldb::socket_t fd) const {
78ee1f578dSGreg Clayton   auto pos = m_fd_map.find(fd);
79ee1f578dSGreg Clayton   if (pos != m_fd_map.end())
80ee1f578dSGreg Clayton     return pos->second.error_is_set;
81ee1f578dSGreg Clayton   else
82ee1f578dSGreg Clayton     return false;
83ee1f578dSGreg Clayton }
84ee1f578dSGreg Clayton 
updateMaxFd(llvm::Optional<lldb::socket_t> & vold,lldb::socket_t vnew)855a8ad459SZachary Turner static void updateMaxFd(llvm::Optional<lldb::socket_t> &vold,
865a8ad459SZachary Turner                         lldb::socket_t vnew) {
875413bf1bSKazu Hirata   if (!vold)
885a8ad459SZachary Turner     vold = vnew;
895a8ad459SZachary Turner   else
905a8ad459SZachary Turner     vold = std::max(*vold, vnew);
915a8ad459SZachary Turner }
925a8ad459SZachary Turner 
Select()9397206d57SZachary Turner lldb_private::Status SelectHelper::Select() {
9497206d57SZachary Turner   lldb_private::Status error;
958b98f12aSMartin Storsjo #ifdef _WIN32
96b9c1b51eSKate Stone   // On windows FD_SETSIZE limits the number of file descriptors, not their
97b9c1b51eSKate Stone   // numeric value.
98fdb2d99eSPavel Labath   lldbassert(m_fd_map.size() <= FD_SETSIZE);
99fdb2d99eSPavel Labath   if (m_fd_map.size() > FD_SETSIZE)
10097206d57SZachary Turner     return lldb_private::Status("Too many file descriptors for select()");
101fdb2d99eSPavel Labath #endif
102ee1f578dSGreg Clayton 
1035a8ad459SZachary Turner   llvm::Optional<lldb::socket_t> max_read_fd;
1045a8ad459SZachary Turner   llvm::Optional<lldb::socket_t> max_write_fd;
1055a8ad459SZachary Turner   llvm::Optional<lldb::socket_t> max_error_fd;
1065a8ad459SZachary Turner   llvm::Optional<lldb::socket_t> max_fd;
107b9c1b51eSKate Stone   for (auto &pair : m_fd_map) {
108ee1f578dSGreg Clayton     pair.second.PrepareForSelect();
1095a8ad459SZachary Turner     const lldb::socket_t fd = pair.first;
1108b98f12aSMartin Storsjo #if !defined(__APPLE__) && !defined(_WIN32)
1111c79e4e9SDavid Carlier     lldbassert(fd < static_cast<int>(FD_SETSIZE));
1121c79e4e9SDavid Carlier     if (fd >= static_cast<int>(FD_SETSIZE)) {
113ee1f578dSGreg Clayton       error.SetErrorStringWithFormat("%i is too large for select()", fd);
114ee1f578dSGreg Clayton       return error;
115ee1f578dSGreg Clayton     }
116ee1f578dSGreg Clayton #endif
1175a8ad459SZachary Turner     if (pair.second.read_set)
1185a8ad459SZachary Turner       updateMaxFd(max_read_fd, fd);
1195a8ad459SZachary Turner     if (pair.second.write_set)
1205a8ad459SZachary Turner       updateMaxFd(max_write_fd, fd);
1215a8ad459SZachary Turner     if (pair.second.error_set)
1225a8ad459SZachary Turner       updateMaxFd(max_error_fd, fd);
1235a8ad459SZachary Turner     updateMaxFd(max_fd, fd);
124ee1f578dSGreg Clayton   }
125ee1f578dSGreg Clayton 
1265413bf1bSKazu Hirata   if (!max_fd) {
127ee1f578dSGreg Clayton     error.SetErrorString("no valid file descriptors");
128ee1f578dSGreg Clayton     return error;
129ee1f578dSGreg Clayton   }
130ee1f578dSGreg Clayton 
1315a8ad459SZachary Turner   const unsigned nfds = static_cast<unsigned>(*max_fd) + 1;
132ee1f578dSGreg Clayton   fd_set *read_fdset_ptr = nullptr;
133ee1f578dSGreg Clayton   fd_set *write_fdset_ptr = nullptr;
134ee1f578dSGreg Clayton   fd_set *error_fdset_ptr = nullptr;
135ee1f578dSGreg Clayton // Initialize and zero out the fdsets
136ee1f578dSGreg Clayton #if defined(__APPLE__)
137ee1f578dSGreg Clayton   llvm::SmallVector<fd_set, 1> read_fdset;
138ee1f578dSGreg Clayton   llvm::SmallVector<fd_set, 1> write_fdset;
139ee1f578dSGreg Clayton   llvm::SmallVector<fd_set, 1> error_fdset;
140ee1f578dSGreg Clayton 
1415a8ad459SZachary Turner   if (max_read_fd.hasValue()) {
142ee1f578dSGreg Clayton     read_fdset.resize((nfds / FD_SETSIZE) + 1);
143ee1f578dSGreg Clayton     read_fdset_ptr = read_fdset.data();
144ee1f578dSGreg Clayton   }
1455a8ad459SZachary Turner   if (max_write_fd.hasValue()) {
146ee1f578dSGreg Clayton     write_fdset.resize((nfds / FD_SETSIZE) + 1);
147ee1f578dSGreg Clayton     write_fdset_ptr = write_fdset.data();
148ee1f578dSGreg Clayton   }
1495a8ad459SZachary Turner   if (max_error_fd.hasValue()) {
150ee1f578dSGreg Clayton     error_fdset.resize((nfds / FD_SETSIZE) + 1);
151ee1f578dSGreg Clayton     error_fdset_ptr = error_fdset.data();
152ee1f578dSGreg Clayton   }
153ee1f578dSGreg Clayton   for (auto &fd_set : read_fdset)
154ee1f578dSGreg Clayton     FD_ZERO(&fd_set);
155ee1f578dSGreg Clayton   for (auto &fd_set : write_fdset)
156ee1f578dSGreg Clayton     FD_ZERO(&fd_set);
157ee1f578dSGreg Clayton   for (auto &fd_set : error_fdset)
158ee1f578dSGreg Clayton     FD_ZERO(&fd_set);
159ee1f578dSGreg Clayton #else
160ee1f578dSGreg Clayton   fd_set read_fdset;
161ee1f578dSGreg Clayton   fd_set write_fdset;
162ee1f578dSGreg Clayton   fd_set error_fdset;
163ee1f578dSGreg Clayton 
16496d1b4ddSKazu Hirata   if (max_read_fd) {
165ee1f578dSGreg Clayton     FD_ZERO(&read_fdset);
166ee1f578dSGreg Clayton     read_fdset_ptr = &read_fdset;
167ee1f578dSGreg Clayton   }
16896d1b4ddSKazu Hirata   if (max_write_fd) {
169ee1f578dSGreg Clayton     FD_ZERO(&write_fdset);
170ee1f578dSGreg Clayton     write_fdset_ptr = &write_fdset;
171ee1f578dSGreg Clayton   }
17296d1b4ddSKazu Hirata   if (max_error_fd) {
173ee1f578dSGreg Clayton     FD_ZERO(&error_fdset);
174ee1f578dSGreg Clayton     error_fdset_ptr = &error_fdset;
175ee1f578dSGreg Clayton   }
176ee1f578dSGreg Clayton #endif
177ee1f578dSGreg Clayton   // Set the FD bits in the fdsets for read/write/error
178b9c1b51eSKate Stone   for (auto &pair : m_fd_map) {
1795a8ad459SZachary Turner     const lldb::socket_t fd = pair.first;
180ee1f578dSGreg Clayton 
181ee1f578dSGreg Clayton     if (pair.second.read_set)
182ee1f578dSGreg Clayton       FD_SET(fd, read_fdset_ptr);
183ee1f578dSGreg Clayton 
184ee1f578dSGreg Clayton     if (pair.second.write_set)
185ee1f578dSGreg Clayton       FD_SET(fd, write_fdset_ptr);
186ee1f578dSGreg Clayton 
187ee1f578dSGreg Clayton     if (pair.second.error_set)
188ee1f578dSGreg Clayton       FD_SET(fd, error_fdset_ptr);
189ee1f578dSGreg Clayton   }
190ee1f578dSGreg Clayton 
191ee1f578dSGreg Clayton   // Setup our timeout time value if needed
192ee1f578dSGreg Clayton   struct timeval *tv_ptr = nullptr;
193ee1f578dSGreg Clayton   struct timeval tv = {0, 0};
194ee1f578dSGreg Clayton 
19509ad8c8fSJonas Devlieghere   while (true) {
196ee1f578dSGreg Clayton     using namespace std::chrono;
197ee1f578dSGreg Clayton     // Setup out relative timeout based on the end time if we have one
19896d1b4ddSKazu Hirata     if (m_end_time) {
199ee1f578dSGreg Clayton       tv_ptr = &tv;
200*5cff5142SKazu Hirata       const auto remaining_dur =
201*5cff5142SKazu Hirata           duration_cast<microseconds>(m_end_time.value() - steady_clock::now());
202b9c1b51eSKate Stone       if (remaining_dur.count() > 0) {
203ee1f578dSGreg Clayton         // Wait for a specific amount of time
204ee1f578dSGreg Clayton         const auto dur_secs = duration_cast<seconds>(remaining_dur);
205ee1f578dSGreg Clayton         const auto dur_usecs = remaining_dur % seconds(1);
206ee1f578dSGreg Clayton         tv.tv_sec = dur_secs.count();
207ee1f578dSGreg Clayton         tv.tv_usec = dur_usecs.count();
208b9c1b51eSKate Stone       } else {
209ee1f578dSGreg Clayton         // Just poll once with no timeout
210ee1f578dSGreg Clayton         tv.tv_sec = 0;
211ee1f578dSGreg Clayton         tv.tv_usec = 0;
212ee1f578dSGreg Clayton       }
213ee1f578dSGreg Clayton     }
214b9c1b51eSKate Stone     const int num_set_fds = ::select(nfds, read_fdset_ptr, write_fdset_ptr,
215b9c1b51eSKate Stone                                      error_fdset_ptr, tv_ptr);
216b9c1b51eSKate Stone     if (num_set_fds < 0) {
217ee1f578dSGreg Clayton       // We got an error
218ee1f578dSGreg Clayton       error.SetErrorToErrno();
219b9c1b51eSKate Stone       if (error.GetError() == EINTR) {
220ee1f578dSGreg Clayton         error.Clear();
221ee1f578dSGreg Clayton         continue; // Keep calling select if we get EINTR
222b9c1b51eSKate Stone       } else
223ee1f578dSGreg Clayton         return error;
224b9c1b51eSKate Stone     } else if (num_set_fds == 0) {
225ee1f578dSGreg Clayton       // Timeout
226ee1f578dSGreg Clayton       error.SetError(ETIMEDOUT, lldb::eErrorTypePOSIX);
227ee1f578dSGreg Clayton       error.SetErrorString("timed out");
228ee1f578dSGreg Clayton       return error;
229b9c1b51eSKate Stone     } else {
23005097246SAdrian Prantl       // One or more descriptors were set, update the FDInfo::select_is_set
23105097246SAdrian Prantl       // mask so users can ask the SelectHelper class so clients can call one
23205097246SAdrian Prantl       // of:
233ee1f578dSGreg Clayton 
234b9c1b51eSKate Stone       for (auto &pair : m_fd_map) {
235ee1f578dSGreg Clayton         const int fd = pair.first;
236ee1f578dSGreg Clayton 
237b9c1b51eSKate Stone         if (pair.second.read_set) {
238ee1f578dSGreg Clayton           if (FD_ISSET(fd, read_fdset_ptr))
239ee1f578dSGreg Clayton             pair.second.read_is_set = true;
240ee1f578dSGreg Clayton         }
241b9c1b51eSKate Stone         if (pair.second.write_set) {
242ee1f578dSGreg Clayton           if (FD_ISSET(fd, write_fdset_ptr))
243ee1f578dSGreg Clayton             pair.second.write_is_set = true;
244ee1f578dSGreg Clayton         }
245b9c1b51eSKate Stone         if (pair.second.error_set) {
246ee1f578dSGreg Clayton           if (FD_ISSET(fd, error_fdset_ptr))
247ee1f578dSGreg Clayton             pair.second.error_is_set = true;
248ee1f578dSGreg Clayton         }
249ee1f578dSGreg Clayton       }
250ee1f578dSGreg Clayton       break;
251ee1f578dSGreg Clayton     }
252ee1f578dSGreg Clayton   }
253ee1f578dSGreg Clayton   return error;
254ee1f578dSGreg Clayton }
255