Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/lldb/source/Utility/SelectHelper.cpp
39587 views
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(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(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(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(*vold, 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(max_read_fd, fd);
119
if (pair.second.write_set)
120
updateMaxFd(max_write_fd, fd);
121
if (pair.second.error_set)
122
updateMaxFd(max_error_fd, fd);
123
updateMaxFd(max_fd, 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 = {0, 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>(*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>(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, read_fdset_ptr, write_fdset_ptr,
215
error_fdset_ptr, 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, 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
256