1 | //===-- SelectHelper.cpp --------------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #if defined(__APPLE__) |
10 | // Enable this special support for Apple builds where we can have unlimited |
11 | // select bounds. We tried switching to poll() and kqueue and we were panicing |
12 | // the kernel, so we have to stick with select for now. |
13 | #define _DARWIN_UNLIMITED_SELECT |
14 | #endif |
15 | |
16 | #include "lldb/Utility/SelectHelper.h" |
17 | #include "lldb/Utility/LLDBAssert.h" |
18 | #include "lldb/Utility/Status.h" |
19 | #include "lldb/lldb-enumerations.h" |
20 | #include "lldb/lldb-types.h" |
21 | |
22 | #include "llvm/ADT/DenseMap.h" |
23 | |
24 | #include <algorithm> |
25 | #include <chrono> |
26 | #include <optional> |
27 | |
28 | #include <cerrno> |
29 | #if defined(_WIN32) |
30 | // Define NOMINMAX to avoid macros that conflict with std::min and std::max |
31 | #define NOMINMAX |
32 | #include <winsock2.h> |
33 | #else |
34 | #include <sys/time.h> |
35 | #include <sys/select.h> |
36 | #endif |
37 | |
38 | |
39 | SelectHelper::SelectHelper() |
40 | : m_fd_map(), m_end_time() // Infinite timeout unless |
41 | // SelectHelper::SetTimeout() gets called |
42 | {} |
43 | |
44 | void SelectHelper::SetTimeout(const std::chrono::microseconds &timeout) { |
45 | using namespace std::chrono; |
46 | m_end_time = steady_clock::time_point(steady_clock::now() + timeout); |
47 | } |
48 | |
49 | void SelectHelper::FDSetRead(lldb::socket_t fd) { |
50 | m_fd_map[fd].read_set = true; |
51 | } |
52 | |
53 | void SelectHelper::FDSetWrite(lldb::socket_t fd) { |
54 | m_fd_map[fd].write_set = true; |
55 | } |
56 | |
57 | void SelectHelper::FDSetError(lldb::socket_t fd) { |
58 | m_fd_map[fd].error_set = true; |
59 | } |
60 | |
61 | bool SelectHelper::FDIsSetRead(lldb::socket_t fd) const { |
62 | auto pos = m_fd_map.find(Val: fd); |
63 | if (pos != m_fd_map.end()) |
64 | return pos->second.read_is_set; |
65 | else |
66 | return false; |
67 | } |
68 | |
69 | bool SelectHelper::FDIsSetWrite(lldb::socket_t fd) const { |
70 | auto pos = m_fd_map.find(Val: fd); |
71 | if (pos != m_fd_map.end()) |
72 | return pos->second.write_is_set; |
73 | else |
74 | return false; |
75 | } |
76 | |
77 | bool SelectHelper::FDIsSetError(lldb::socket_t fd) const { |
78 | auto pos = m_fd_map.find(Val: fd); |
79 | if (pos != m_fd_map.end()) |
80 | return pos->second.error_is_set; |
81 | else |
82 | return false; |
83 | } |
84 | |
85 | static void updateMaxFd(std::optional<lldb::socket_t> &vold, |
86 | lldb::socket_t vnew) { |
87 | if (!vold) |
88 | vold = vnew; |
89 | else |
90 | vold = std::max(a: *vold, b: vnew); |
91 | } |
92 | |
93 | lldb_private::Status SelectHelper::Select() { |
94 | lldb_private::Status error; |
95 | #ifdef _WIN32 |
96 | // On windows FD_SETSIZE limits the number of file descriptors, not their |
97 | // numeric value. |
98 | lldbassert(m_fd_map.size() <= FD_SETSIZE); |
99 | if (m_fd_map.size() > FD_SETSIZE) |
100 | return lldb_private::Status("Too many file descriptors for select()" ); |
101 | #endif |
102 | |
103 | std::optional<lldb::socket_t> max_read_fd; |
104 | std::optional<lldb::socket_t> max_write_fd; |
105 | std::optional<lldb::socket_t> max_error_fd; |
106 | std::optional<lldb::socket_t> max_fd; |
107 | for (auto &pair : m_fd_map) { |
108 | pair.second.PrepareForSelect(); |
109 | const lldb::socket_t fd = pair.first; |
110 | #if !defined(__APPLE__) && !defined(_WIN32) |
111 | lldbassert(fd < static_cast<int>(FD_SETSIZE)); |
112 | if (fd >= static_cast<int>(FD_SETSIZE)) { |
113 | error.SetErrorStringWithFormat("%i is too large for select()" , fd); |
114 | return error; |
115 | } |
116 | #endif |
117 | if (pair.second.read_set) |
118 | updateMaxFd(vold&: max_read_fd, vnew: fd); |
119 | if (pair.second.write_set) |
120 | updateMaxFd(vold&: max_write_fd, vnew: fd); |
121 | if (pair.second.error_set) |
122 | updateMaxFd(vold&: max_error_fd, vnew: fd); |
123 | updateMaxFd(vold&: max_fd, vnew: fd); |
124 | } |
125 | |
126 | if (!max_fd) { |
127 | error.SetErrorString("no valid file descriptors" ); |
128 | return error; |
129 | } |
130 | |
131 | const unsigned nfds = static_cast<unsigned>(*max_fd) + 1; |
132 | fd_set *read_fdset_ptr = nullptr; |
133 | fd_set *write_fdset_ptr = nullptr; |
134 | fd_set *error_fdset_ptr = nullptr; |
135 | // Initialize and zero out the fdsets |
136 | #if defined(__APPLE__) |
137 | llvm::SmallVector<fd_set, 1> read_fdset; |
138 | llvm::SmallVector<fd_set, 1> write_fdset; |
139 | llvm::SmallVector<fd_set, 1> error_fdset; |
140 | |
141 | if (max_read_fd.has_value()) { |
142 | read_fdset.resize((nfds / FD_SETSIZE) + 1); |
143 | read_fdset_ptr = read_fdset.data(); |
144 | } |
145 | if (max_write_fd.has_value()) { |
146 | write_fdset.resize((nfds / FD_SETSIZE) + 1); |
147 | write_fdset_ptr = write_fdset.data(); |
148 | } |
149 | if (max_error_fd.has_value()) { |
150 | error_fdset.resize((nfds / FD_SETSIZE) + 1); |
151 | error_fdset_ptr = error_fdset.data(); |
152 | } |
153 | for (auto &fd_set : read_fdset) |
154 | FD_ZERO(&fd_set); |
155 | for (auto &fd_set : write_fdset) |
156 | FD_ZERO(&fd_set); |
157 | for (auto &fd_set : error_fdset) |
158 | FD_ZERO(&fd_set); |
159 | #else |
160 | fd_set read_fdset; |
161 | fd_set write_fdset; |
162 | fd_set error_fdset; |
163 | |
164 | if (max_read_fd) { |
165 | FD_ZERO(&read_fdset); |
166 | read_fdset_ptr = &read_fdset; |
167 | } |
168 | if (max_write_fd) { |
169 | FD_ZERO(&write_fdset); |
170 | write_fdset_ptr = &write_fdset; |
171 | } |
172 | if (max_error_fd) { |
173 | FD_ZERO(&error_fdset); |
174 | error_fdset_ptr = &error_fdset; |
175 | } |
176 | #endif |
177 | // Set the FD bits in the fdsets for read/write/error |
178 | for (auto &pair : m_fd_map) { |
179 | const lldb::socket_t fd = pair.first; |
180 | |
181 | if (pair.second.read_set) |
182 | FD_SET(fd, read_fdset_ptr); |
183 | |
184 | if (pair.second.write_set) |
185 | FD_SET(fd, write_fdset_ptr); |
186 | |
187 | if (pair.second.error_set) |
188 | FD_SET(fd, error_fdset_ptr); |
189 | } |
190 | |
191 | // Setup our timeout time value if needed |
192 | struct timeval *tv_ptr = nullptr; |
193 | struct timeval tv = {.tv_sec: 0, .tv_usec: 0}; |
194 | |
195 | while (true) { |
196 | using namespace std::chrono; |
197 | // Setup out relative timeout based on the end time if we have one |
198 | if (m_end_time) { |
199 | tv_ptr = &tv; |
200 | const auto remaining_dur = |
201 | duration_cast<microseconds>(d: *m_end_time - steady_clock::now()); |
202 | if (remaining_dur.count() > 0) { |
203 | // Wait for a specific amount of time |
204 | const auto dur_secs = duration_cast<seconds>(d: remaining_dur); |
205 | const auto dur_usecs = remaining_dur % seconds(1); |
206 | tv.tv_sec = dur_secs.count(); |
207 | tv.tv_usec = dur_usecs.count(); |
208 | } else { |
209 | // Just poll once with no timeout |
210 | tv.tv_sec = 0; |
211 | tv.tv_usec = 0; |
212 | } |
213 | } |
214 | const int num_set_fds = ::select(nfds: nfds, readfds: read_fdset_ptr, writefds: write_fdset_ptr, |
215 | exceptfds: error_fdset_ptr, timeout: tv_ptr); |
216 | if (num_set_fds < 0) { |
217 | // We got an error |
218 | error.SetErrorToErrno(); |
219 | if (error.GetError() == EINTR) { |
220 | error.Clear(); |
221 | continue; // Keep calling select if we get EINTR |
222 | } else |
223 | return error; |
224 | } else if (num_set_fds == 0) { |
225 | // Timeout |
226 | error.SetError(ETIMEDOUT, type: lldb::eErrorTypePOSIX); |
227 | error.SetErrorString("timed out" ); |
228 | return error; |
229 | } else { |
230 | // One or more descriptors were set, update the FDInfo::select_is_set |
231 | // mask so users can ask the SelectHelper class so clients can call one |
232 | // of: |
233 | |
234 | for (auto &pair : m_fd_map) { |
235 | const int fd = pair.first; |
236 | |
237 | if (pair.second.read_set) { |
238 | if (FD_ISSET(fd, read_fdset_ptr)) |
239 | pair.second.read_is_set = true; |
240 | } |
241 | if (pair.second.write_set) { |
242 | if (FD_ISSET(fd, write_fdset_ptr)) |
243 | pair.second.write_is_set = true; |
244 | } |
245 | if (pair.second.error_set) { |
246 | if (FD_ISSET(fd, error_fdset_ptr)) |
247 | pair.second.error_is_set = true; |
248 | } |
249 | } |
250 | break; |
251 | } |
252 | } |
253 | return error; |
254 | } |
255 | |