1 //===-- SelectHelper.cpp --------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
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
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"
22 #include "llvm/ADT/DenseMap.h"
30 // Define NOMINMAX to avoid macros that conflict with std::min and std::max
35 #include <sys/select.h>
39 SelectHelper::SelectHelper()
40 : m_fd_map(), m_end_time() // Infinite timeout unless
41 // SelectHelper::SetTimeout() gets called
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
);
49 void SelectHelper::FDSetRead(lldb::socket_t fd
) {
50 m_fd_map
[fd
].read_set
= true;
53 void SelectHelper::FDSetWrite(lldb::socket_t fd
) {
54 m_fd_map
[fd
].write_set
= true;
57 void SelectHelper::FDSetError(lldb::socket_t fd
) {
58 m_fd_map
[fd
].error_set
= true;
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
;
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
;
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
;
85 static void updateMaxFd(std::optional
<lldb::socket_t
> &vold
,
86 lldb::socket_t vnew
) {
90 vold
= std::max(*vold
, vnew
);
93 lldb_private::Status
SelectHelper::Select() {
94 lldb_private::Status error
;
96 // On windows FD_SETSIZE limits the number of file descriptors, not their
98 lldbassert(m_fd_map
.size() <= FD_SETSIZE
);
99 if (m_fd_map
.size() > FD_SETSIZE
)
100 return lldb_private::Status::FromErrorString(
101 "Too many file descriptors for select()");
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
)) {
114 error
= lldb_private::Status::FromErrorStringWithFormat(
115 "%i is too large for select()", fd
);
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
);
129 return lldb_private::Status::FromErrorString("no valid file descriptors");
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
;
142 if (max_read_fd
.has_value()) {
143 read_fdset
.resize((nfds
/ FD_SETSIZE
) + 1);
144 read_fdset_ptr
= read_fdset
.data();
146 if (max_write_fd
.has_value()) {
147 write_fdset
.resize((nfds
/ FD_SETSIZE
) + 1);
148 write_fdset_ptr
= write_fdset
.data();
150 if (max_error_fd
.has_value()) {
151 error_fdset
.resize((nfds
/ FD_SETSIZE
) + 1);
152 error_fdset_ptr
= error_fdset
.data();
154 for (auto &fd_set
: read_fdset
)
156 for (auto &fd_set
: write_fdset
)
158 for (auto &fd_set
: error_fdset
)
166 FD_ZERO(&read_fdset
);
167 read_fdset_ptr
= &read_fdset
;
170 FD_ZERO(&write_fdset
);
171 write_fdset_ptr
= &write_fdset
;
174 FD_ZERO(&error_fdset
);
175 error_fdset_ptr
= &error_fdset
;
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
;
182 if (pair
.second
.read_set
)
183 FD_SET(fd
, read_fdset_ptr
);
185 if (pair
.second
.write_set
)
186 FD_SET(fd
, write_fdset_ptr
);
188 if (pair
.second
.error_set
)
189 FD_SET(fd
, error_fdset_ptr
);
192 // Setup our timeout time value if needed
193 struct timeval
*tv_ptr
= nullptr;
194 struct timeval tv
= {0, 0};
197 using namespace std::chrono
;
198 // Setup out relative timeout based on the end time if we have one
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();
210 // Just poll once with no timeout
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) {
219 error
= lldb_private::Status::FromErrno();
220 if (error
.GetError() == EINTR
) {
222 continue; // Keep calling select if we get EINTR
225 } else if (num_set_fds
== 0) {
227 return lldb_private::Status(ETIMEDOUT
, lldb::eErrorTypePOSIX
,
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
234 for (auto &pair
: m_fd_map
) {
235 const int fd
= pair
.first
;
237 if (pair
.second
.read_set
) {
238 if (FD_ISSET(fd
, read_fdset_ptr
))
239 pair
.second
.read_is_set
= true;
241 if (pair
.second
.write_set
) {
242 if (FD_ISSET(fd
, write_fdset_ptr
))
243 pair
.second
.write_is_set
= true;
245 if (pair
.second
.error_set
) {
246 if (FD_ISSET(fd
, error_fdset_ptr
))
247 pair
.second
.error_is_set
= true;