1dda28197Spatrick //===-- SelectHelper.cpp --------------------------------------------------===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick
9061da546Spatrick #if defined(__APPLE__)
10061da546Spatrick // Enable this special support for Apple builds where we can have unlimited
11061da546Spatrick // select bounds. We tried switching to poll() and kqueue and we were panicing
12061da546Spatrick // the kernel, so we have to stick with select for now.
13061da546Spatrick #define _DARWIN_UNLIMITED_SELECT
14061da546Spatrick #endif
15061da546Spatrick
16061da546Spatrick #include "lldb/Utility/SelectHelper.h"
17061da546Spatrick #include "lldb/Utility/LLDBAssert.h"
18061da546Spatrick #include "lldb/Utility/Status.h"
19061da546Spatrick #include "lldb/lldb-enumerations.h"
20061da546Spatrick #include "lldb/lldb-types.h"
21061da546Spatrick
22061da546Spatrick #include "llvm/ADT/DenseMap.h"
23061da546Spatrick
24061da546Spatrick #include <algorithm>
25061da546Spatrick #include <chrono>
26*f6aab3d8Srobert #include <optional>
27061da546Spatrick
28be691f3bSpatrick #include <cerrno>
29061da546Spatrick #if defined(_WIN32)
30061da546Spatrick // Define NOMINMAX to avoid macros that conflict with std::min and std::max
31061da546Spatrick #define NOMINMAX
32061da546Spatrick #include <winsock2.h>
33061da546Spatrick #else
34061da546Spatrick #include <sys/time.h>
35061da546Spatrick #include <sys/select.h>
36061da546Spatrick #endif
37061da546Spatrick
38061da546Spatrick
SelectHelper()39061da546Spatrick SelectHelper::SelectHelper()
40061da546Spatrick : m_fd_map(), m_end_time() // Infinite timeout unless
41061da546Spatrick // SelectHelper::SetTimeout() gets called
42061da546Spatrick {}
43061da546Spatrick
SetTimeout(const std::chrono::microseconds & timeout)44061da546Spatrick void SelectHelper::SetTimeout(const std::chrono::microseconds &timeout) {
45061da546Spatrick using namespace std::chrono;
46061da546Spatrick m_end_time = steady_clock::time_point(steady_clock::now() + timeout);
47061da546Spatrick }
48061da546Spatrick
FDSetRead(lldb::socket_t fd)49061da546Spatrick void SelectHelper::FDSetRead(lldb::socket_t fd) {
50061da546Spatrick m_fd_map[fd].read_set = true;
51061da546Spatrick }
52061da546Spatrick
FDSetWrite(lldb::socket_t fd)53061da546Spatrick void SelectHelper::FDSetWrite(lldb::socket_t fd) {
54061da546Spatrick m_fd_map[fd].write_set = true;
55061da546Spatrick }
56061da546Spatrick
FDSetError(lldb::socket_t fd)57061da546Spatrick void SelectHelper::FDSetError(lldb::socket_t fd) {
58061da546Spatrick m_fd_map[fd].error_set = true;
59061da546Spatrick }
60061da546Spatrick
FDIsSetRead(lldb::socket_t fd) const61061da546Spatrick bool SelectHelper::FDIsSetRead(lldb::socket_t fd) const {
62061da546Spatrick auto pos = m_fd_map.find(fd);
63061da546Spatrick if (pos != m_fd_map.end())
64061da546Spatrick return pos->second.read_is_set;
65061da546Spatrick else
66061da546Spatrick return false;
67061da546Spatrick }
68061da546Spatrick
FDIsSetWrite(lldb::socket_t fd) const69061da546Spatrick bool SelectHelper::FDIsSetWrite(lldb::socket_t fd) const {
70061da546Spatrick auto pos = m_fd_map.find(fd);
71061da546Spatrick if (pos != m_fd_map.end())
72061da546Spatrick return pos->second.write_is_set;
73061da546Spatrick else
74061da546Spatrick return false;
75061da546Spatrick }
76061da546Spatrick
FDIsSetError(lldb::socket_t fd) const77061da546Spatrick bool SelectHelper::FDIsSetError(lldb::socket_t fd) const {
78061da546Spatrick auto pos = m_fd_map.find(fd);
79061da546Spatrick if (pos != m_fd_map.end())
80061da546Spatrick return pos->second.error_is_set;
81061da546Spatrick else
82061da546Spatrick return false;
83061da546Spatrick }
84061da546Spatrick
updateMaxFd(std::optional<lldb::socket_t> & vold,lldb::socket_t vnew)85*f6aab3d8Srobert static void updateMaxFd(std::optional<lldb::socket_t> &vold,
86061da546Spatrick lldb::socket_t vnew) {
87*f6aab3d8Srobert if (!vold)
88061da546Spatrick vold = vnew;
89061da546Spatrick else
90061da546Spatrick vold = std::max(*vold, vnew);
91061da546Spatrick }
92061da546Spatrick
Select()93061da546Spatrick lldb_private::Status SelectHelper::Select() {
94061da546Spatrick lldb_private::Status error;
95061da546Spatrick #ifdef _WIN32
96061da546Spatrick // On windows FD_SETSIZE limits the number of file descriptors, not their
97061da546Spatrick // numeric value.
98061da546Spatrick lldbassert(m_fd_map.size() <= FD_SETSIZE);
99061da546Spatrick if (m_fd_map.size() > FD_SETSIZE)
100061da546Spatrick return lldb_private::Status("Too many file descriptors for select()");
101061da546Spatrick #endif
102061da546Spatrick
103*f6aab3d8Srobert std::optional<lldb::socket_t> max_read_fd;
104*f6aab3d8Srobert std::optional<lldb::socket_t> max_write_fd;
105*f6aab3d8Srobert std::optional<lldb::socket_t> max_error_fd;
106*f6aab3d8Srobert std::optional<lldb::socket_t> max_fd;
107061da546Spatrick for (auto &pair : m_fd_map) {
108061da546Spatrick pair.second.PrepareForSelect();
109061da546Spatrick const lldb::socket_t fd = pair.first;
110061da546Spatrick #if !defined(__APPLE__) && !defined(_WIN32)
111061da546Spatrick lldbassert(fd < static_cast<int>(FD_SETSIZE));
112061da546Spatrick if (fd >= static_cast<int>(FD_SETSIZE)) {
113061da546Spatrick error.SetErrorStringWithFormat("%i is too large for select()", fd);
114061da546Spatrick return error;
115061da546Spatrick }
116061da546Spatrick #endif
117061da546Spatrick if (pair.second.read_set)
118061da546Spatrick updateMaxFd(max_read_fd, fd);
119061da546Spatrick if (pair.second.write_set)
120061da546Spatrick updateMaxFd(max_write_fd, fd);
121061da546Spatrick if (pair.second.error_set)
122061da546Spatrick updateMaxFd(max_error_fd, fd);
123061da546Spatrick updateMaxFd(max_fd, fd);
124061da546Spatrick }
125061da546Spatrick
126*f6aab3d8Srobert if (!max_fd) {
127061da546Spatrick error.SetErrorString("no valid file descriptors");
128061da546Spatrick return error;
129061da546Spatrick }
130061da546Spatrick
131061da546Spatrick const unsigned nfds = static_cast<unsigned>(*max_fd) + 1;
132061da546Spatrick fd_set *read_fdset_ptr = nullptr;
133061da546Spatrick fd_set *write_fdset_ptr = nullptr;
134061da546Spatrick fd_set *error_fdset_ptr = nullptr;
135061da546Spatrick // Initialize and zero out the fdsets
136061da546Spatrick #if defined(__APPLE__)
137061da546Spatrick llvm::SmallVector<fd_set, 1> read_fdset;
138061da546Spatrick llvm::SmallVector<fd_set, 1> write_fdset;
139061da546Spatrick llvm::SmallVector<fd_set, 1> error_fdset;
140061da546Spatrick
141*f6aab3d8Srobert if (max_read_fd.has_value()) {
142061da546Spatrick read_fdset.resize((nfds / FD_SETSIZE) + 1);
143061da546Spatrick read_fdset_ptr = read_fdset.data();
144061da546Spatrick }
145*f6aab3d8Srobert if (max_write_fd.has_value()) {
146061da546Spatrick write_fdset.resize((nfds / FD_SETSIZE) + 1);
147061da546Spatrick write_fdset_ptr = write_fdset.data();
148061da546Spatrick }
149*f6aab3d8Srobert if (max_error_fd.has_value()) {
150061da546Spatrick error_fdset.resize((nfds / FD_SETSIZE) + 1);
151061da546Spatrick error_fdset_ptr = error_fdset.data();
152061da546Spatrick }
153061da546Spatrick for (auto &fd_set : read_fdset)
154061da546Spatrick FD_ZERO(&fd_set);
155061da546Spatrick for (auto &fd_set : write_fdset)
156061da546Spatrick FD_ZERO(&fd_set);
157061da546Spatrick for (auto &fd_set : error_fdset)
158061da546Spatrick FD_ZERO(&fd_set);
159061da546Spatrick #else
160061da546Spatrick fd_set read_fdset;
161061da546Spatrick fd_set write_fdset;
162061da546Spatrick fd_set error_fdset;
163061da546Spatrick
164*f6aab3d8Srobert if (max_read_fd) {
165061da546Spatrick FD_ZERO(&read_fdset);
166061da546Spatrick read_fdset_ptr = &read_fdset;
167061da546Spatrick }
168*f6aab3d8Srobert if (max_write_fd) {
169061da546Spatrick FD_ZERO(&write_fdset);
170061da546Spatrick write_fdset_ptr = &write_fdset;
171061da546Spatrick }
172*f6aab3d8Srobert if (max_error_fd) {
173061da546Spatrick FD_ZERO(&error_fdset);
174061da546Spatrick error_fdset_ptr = &error_fdset;
175061da546Spatrick }
176061da546Spatrick #endif
177061da546Spatrick // Set the FD bits in the fdsets for read/write/error
178061da546Spatrick for (auto &pair : m_fd_map) {
179061da546Spatrick const lldb::socket_t fd = pair.first;
180061da546Spatrick
181061da546Spatrick if (pair.second.read_set)
182061da546Spatrick FD_SET(fd, read_fdset_ptr);
183061da546Spatrick
184061da546Spatrick if (pair.second.write_set)
185061da546Spatrick FD_SET(fd, write_fdset_ptr);
186061da546Spatrick
187061da546Spatrick if (pair.second.error_set)
188061da546Spatrick FD_SET(fd, error_fdset_ptr);
189061da546Spatrick }
190061da546Spatrick
191061da546Spatrick // Setup our timeout time value if needed
192061da546Spatrick struct timeval *tv_ptr = nullptr;
193061da546Spatrick struct timeval tv = {0, 0};
194061da546Spatrick
195061da546Spatrick while (true) {
196061da546Spatrick using namespace std::chrono;
197061da546Spatrick // Setup out relative timeout based on the end time if we have one
198*f6aab3d8Srobert if (m_end_time) {
199061da546Spatrick tv_ptr = &tv;
200*f6aab3d8Srobert const auto remaining_dur =
201*f6aab3d8Srobert duration_cast<microseconds>(*m_end_time - steady_clock::now());
202061da546Spatrick if (remaining_dur.count() > 0) {
203061da546Spatrick // Wait for a specific amount of time
204061da546Spatrick const auto dur_secs = duration_cast<seconds>(remaining_dur);
205061da546Spatrick const auto dur_usecs = remaining_dur % seconds(1);
206061da546Spatrick tv.tv_sec = dur_secs.count();
207061da546Spatrick tv.tv_usec = dur_usecs.count();
208061da546Spatrick } else {
209061da546Spatrick // Just poll once with no timeout
210061da546Spatrick tv.tv_sec = 0;
211061da546Spatrick tv.tv_usec = 0;
212061da546Spatrick }
213061da546Spatrick }
214061da546Spatrick const int num_set_fds = ::select(nfds, read_fdset_ptr, write_fdset_ptr,
215061da546Spatrick error_fdset_ptr, tv_ptr);
216061da546Spatrick if (num_set_fds < 0) {
217061da546Spatrick // We got an error
218061da546Spatrick error.SetErrorToErrno();
219061da546Spatrick if (error.GetError() == EINTR) {
220061da546Spatrick error.Clear();
221061da546Spatrick continue; // Keep calling select if we get EINTR
222061da546Spatrick } else
223061da546Spatrick return error;
224061da546Spatrick } else if (num_set_fds == 0) {
225061da546Spatrick // Timeout
226061da546Spatrick error.SetError(ETIMEDOUT, lldb::eErrorTypePOSIX);
227061da546Spatrick error.SetErrorString("timed out");
228061da546Spatrick return error;
229061da546Spatrick } else {
230061da546Spatrick // One or more descriptors were set, update the FDInfo::select_is_set
231061da546Spatrick // mask so users can ask the SelectHelper class so clients can call one
232061da546Spatrick // of:
233061da546Spatrick
234061da546Spatrick for (auto &pair : m_fd_map) {
235061da546Spatrick const int fd = pair.first;
236061da546Spatrick
237061da546Spatrick if (pair.second.read_set) {
238061da546Spatrick if (FD_ISSET(fd, read_fdset_ptr))
239061da546Spatrick pair.second.read_is_set = true;
240061da546Spatrick }
241061da546Spatrick if (pair.second.write_set) {
242061da546Spatrick if (FD_ISSET(fd, write_fdset_ptr))
243061da546Spatrick pair.second.write_is_set = true;
244061da546Spatrick }
245061da546Spatrick if (pair.second.error_set) {
246061da546Spatrick if (FD_ISSET(fd, error_fdset_ptr))
247061da546Spatrick pair.second.error_is_set = true;
248061da546Spatrick }
249061da546Spatrick }
250061da546Spatrick break;
251061da546Spatrick }
252061da546Spatrick }
253061da546Spatrick return error;
254061da546Spatrick }
255