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