LLDB mainline
SelectHelper.cpp
Go to the documentation of this file.
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
18#include "lldb/Utility/Status.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
40 : m_fd_map(), m_end_time() // Infinite timeout unless
41 // SelectHelper::SetTimeout() gets called
42{}
43
44void 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
50 m_fd_map[fd].read_set = true;
51}
52
54 m_fd_map[fd].write_set = true;
55}
56
58 m_fd_map[fd].error_set = true;
59}
60
62 auto pos = m_fd_map.find(fd);
63 if (pos != m_fd_map.end())
64 return pos->second.read_is_set;
65 else
66 return false;
67}
68
70 auto pos = m_fd_map.find(fd);
71 if (pos != m_fd_map.end())
72 return pos->second.write_is_set;
73 else
74 return false;
75}
76
78 auto pos = m_fd_map.find(fd);
79 if (pos != m_fd_map.end())
80 return pos->second.error_is_set;
81 else
82 return false;
83}
84
85static 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(*vold, vnew);
91}
92
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)
101 "Too many file descriptors for select()");
102#endif
103
104 std::optional<lldb::socket_t> max_read_fd;
105 std::optional<lldb::socket_t> max_write_fd;
106 std::optional<lldb::socket_t> max_error_fd;
107 std::optional<lldb::socket_t> max_fd;
108 for (auto &pair : m_fd_map) {
109 pair.second.PrepareForSelect();
110 const lldb::socket_t fd = pair.first;
111#if !defined(__APPLE__) && !defined(_WIN32)
112 lldbassert(fd < static_cast<int>(FD_SETSIZE));
113 if (fd >= static_cast<int>(FD_SETSIZE)) {
115 "%i is too large for select()", fd);
116 return error;
117 }
118#endif
119 if (pair.second.read_set)
120 updateMaxFd(max_read_fd, fd);
121 if (pair.second.write_set)
122 updateMaxFd(max_write_fd, fd);
123 if (pair.second.error_set)
124 updateMaxFd(max_error_fd, fd);
125 updateMaxFd(max_fd, fd);
126 }
127
128 if (!max_fd) {
129 return lldb_private::Status::FromErrorString("no valid file descriptors");
130 }
131
132 const unsigned nfds = static_cast<unsigned>(*max_fd) + 1;
133 fd_set *read_fdset_ptr = nullptr;
134 fd_set *write_fdset_ptr = nullptr;
135 fd_set *error_fdset_ptr = nullptr;
136// Initialize and zero out the fdsets
137#if defined(__APPLE__)
138 llvm::SmallVector<fd_set, 1> read_fdset;
139 llvm::SmallVector<fd_set, 1> write_fdset;
140 llvm::SmallVector<fd_set, 1> error_fdset;
141
142 if (max_read_fd.has_value()) {
143 read_fdset.resize((nfds / FD_SETSIZE) + 1);
144 read_fdset_ptr = read_fdset.data();
145 }
146 if (max_write_fd.has_value()) {
147 write_fdset.resize((nfds / FD_SETSIZE) + 1);
148 write_fdset_ptr = write_fdset.data();
149 }
150 if (max_error_fd.has_value()) {
151 error_fdset.resize((nfds / FD_SETSIZE) + 1);
152 error_fdset_ptr = error_fdset.data();
153 }
154 for (auto &fd_set : read_fdset)
155 FD_ZERO(&fd_set);
156 for (auto &fd_set : write_fdset)
157 FD_ZERO(&fd_set);
158 for (auto &fd_set : error_fdset)
159 FD_ZERO(&fd_set);
160#else
161 fd_set read_fdset;
162 fd_set write_fdset;
163 fd_set error_fdset;
164
165 if (max_read_fd) {
166 FD_ZERO(&read_fdset);
167 read_fdset_ptr = &read_fdset;
168 }
169 if (max_write_fd) {
170 FD_ZERO(&write_fdset);
171 write_fdset_ptr = &write_fdset;
172 }
173 if (max_error_fd) {
174 FD_ZERO(&error_fdset);
175 error_fdset_ptr = &error_fdset;
176 }
177#endif
178 // Set the FD bits in the fdsets for read/write/error
179 for (auto &pair : m_fd_map) {
180 const lldb::socket_t fd = pair.first;
181
182 if (pair.second.read_set)
183 FD_SET(fd, read_fdset_ptr);
184
185 if (pair.second.write_set)
186 FD_SET(fd, write_fdset_ptr);
187
188 if (pair.second.error_set)
189 FD_SET(fd, error_fdset_ptr);
190 }
191
192 // Setup our timeout time value if needed
193 struct timeval *tv_ptr = nullptr;
194 struct timeval tv = {0, 0};
195
196 while (true) {
197 using namespace std::chrono;
198 // Setup out relative timeout based on the end time if we have one
199 if (m_end_time) {
200 tv_ptr = &tv;
201 const auto remaining_dur =
202 duration_cast<microseconds>(*m_end_time - steady_clock::now());
203 if (remaining_dur.count() > 0) {
204 // Wait for a specific amount of time
205 const auto dur_secs = duration_cast<seconds>(remaining_dur);
206 const auto dur_usecs = remaining_dur % seconds(1);
207 tv.tv_sec = dur_secs.count();
208 tv.tv_usec = dur_usecs.count();
209 } else {
210 // Just poll once with no timeout
211 tv.tv_sec = 0;
212 tv.tv_usec = 0;
213 }
214 }
215 const int num_set_fds = ::select(nfds, read_fdset_ptr, write_fdset_ptr,
216 error_fdset_ptr, tv_ptr);
217 if (num_set_fds < 0) {
218 // We got an error
220 if (error.GetError() == EINTR) {
221 error.Clear();
222 continue; // Keep calling select if we get EINTR
223 } else
224 return error;
225 } else if (num_set_fds == 0) {
226 // Timeout
228 "timed out");
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}
static llvm::raw_ostream & error(Stream &strm)
#define lldbassert(x)
Definition: LLDBAssert.h:15
static void updateMaxFd(std::optional< lldb::socket_t > &vold, lldb::socket_t vnew)
bool FDIsSetWrite(lldb::socket_t fd) const
lldb_private::Status Select()
void FDSetRead(lldb::socket_t fd)
bool FDIsSetError(lldb::socket_t fd) const
llvm::DenseMap< lldb::socket_t, FDInfo > m_fd_map
Definition: SelectHelper.h:67
void SetTimeout(const std::chrono::microseconds &timeout)
std::optional< std::chrono::steady_clock::time_point > m_end_time
Definition: SelectHelper.h:68
void FDSetError(lldb::socket_t fd)
void FDSetWrite(lldb::socket_t fd)
bool FDIsSetRead(lldb::socket_t fd) const
An error handling class.
Definition: Status.h:118
static Status FromErrno()
Set the current error to errno.
Definition: Status.cpp:300
static Status FromErrorStringWithFormat(const char *format,...) __attribute__((format(printf
Definition: Status.cpp:106
static Status FromErrorString(const char *str)
Definition: Status.h:141
@ eErrorTypePOSIX
POSIX error codes.
int socket_t
Definition: lldb-types.h:60