1 //===-- SelectHelper.cpp ----------------------------------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 #if defined(__APPLE__)
11 // Enable this special support for Apple builds where we can have unlimited
12 // select bounds. We tried switching to poll() and kqueue and we were panicing
13 // the kernel, so we have to stick with select for now.
14 #define _DARWIN_UNLIMITED_SELECT
20 // Define NOMINMAX to avoid macros that conflict with std::min and std::max
24 #include <sys/select.h>
30 // Other libraries and framework includes
31 #include "llvm/ADT/SmallVector.h"
34 #include "lldb/Core/Error.h"
35 #include "lldb/Utility/LLDBAssert.h"
36 #include "lldb/Utility/SelectHelper.h"
38 SelectHelper::SelectHelper()
39 : m_fd_map(), m_end_time() // Infinite timeout unless
40 // SelectHelper::SetTimeout() gets called
43 void SelectHelper::SetTimeout(const std::chrono::microseconds &timeout) {
44 using namespace std::chrono;
45 m_end_time = steady_clock::time_point(steady_clock::now() + timeout);
48 void SelectHelper::FDSetRead(lldb::socket_t fd) {
49 m_fd_map[fd].read_set = true;
52 void SelectHelper::FDSetWrite(lldb::socket_t fd) {
53 m_fd_map[fd].write_set = true;
56 void SelectHelper::FDSetError(lldb::socket_t fd) {
57 m_fd_map[fd].error_set = true;
60 bool SelectHelper::FDIsSetRead(lldb::socket_t fd) const {
61 auto pos = m_fd_map.find(fd);
62 if (pos != m_fd_map.end())
63 return pos->second.read_is_set;
68 bool SelectHelper::FDIsSetWrite(lldb::socket_t fd) const {
69 auto pos = m_fd_map.find(fd);
70 if (pos != m_fd_map.end())
71 return pos->second.write_is_set;
76 bool SelectHelper::FDIsSetError(lldb::socket_t fd) const {
77 auto pos = m_fd_map.find(fd);
78 if (pos != m_fd_map.end())
79 return pos->second.error_is_set;
84 static void updateMaxFd(llvm::Optional<lldb::socket_t> &vold,
85 lldb::socket_t vnew) {
89 vold = std::max(*vold, vnew);
92 lldb_private::Error SelectHelper::Select() {
93 lldb_private::Error error;
95 // On windows FD_SETSIZE limits the number of file descriptors, not their
97 lldbassert(m_fd_map.size() <= FD_SETSIZE);
98 if (m_fd_map.size() > FD_SETSIZE)
99 return lldb_private::Error("Too many file descriptors for select()");
102 llvm::Optional<lldb::socket_t> max_read_fd;
103 llvm::Optional<lldb::socket_t> max_write_fd;
104 llvm::Optional<lldb::socket_t> max_error_fd;
105 llvm::Optional<lldb::socket_t> max_fd;
106 for (auto &pair : m_fd_map) {
107 pair.second.PrepareForSelect();
108 const lldb::socket_t fd = pair.first;
109 #if !defined(__APPLE__) && !defined(_MSC_VER)
110 lldbassert(fd < FD_SETSIZE);
111 if (fd >= FD_SETSIZE) {
112 error.SetErrorStringWithFormat("%i is too large for select()", fd);
116 if (pair.second.read_set)
117 updateMaxFd(max_read_fd, fd);
118 if (pair.second.write_set)
119 updateMaxFd(max_write_fd, fd);
120 if (pair.second.error_set)
121 updateMaxFd(max_error_fd, fd);
122 updateMaxFd(max_fd, fd);
125 if (!max_fd.hasValue()) {
126 error.SetErrorString("no valid file descriptors");
130 const unsigned nfds = static_cast<unsigned>(*max_fd) + 1;
131 fd_set *read_fdset_ptr = nullptr;
132 fd_set *write_fdset_ptr = nullptr;
133 fd_set *error_fdset_ptr = nullptr;
134 //----------------------------------------------------------------------
135 // Initialize and zero out the fdsets
136 //----------------------------------------------------------------------
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.hasValue()) {
143 read_fdset.resize((nfds / FD_SETSIZE) + 1);
144 read_fdset_ptr = read_fdset.data();
146 if (max_write_fd.hasValue()) {
147 write_fdset.resize((nfds / FD_SETSIZE) + 1);
148 write_fdset_ptr = write_fdset.data();
150 if (max_error_fd.hasValue()) {
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)
165 if (max_read_fd.hasValue()) {
166 FD_ZERO(&read_fdset);
167 read_fdset_ptr = &read_fdset;
169 if (max_write_fd.hasValue()) {
170 FD_ZERO(&write_fdset);
171 write_fdset_ptr = &write_fdset;
173 if (max_error_fd.hasValue()) {
174 FD_ZERO(&error_fdset);
175 error_fdset_ptr = &error_fdset;
178 //----------------------------------------------------------------------
179 // Set the FD bits in the fdsets for read/write/error
180 //----------------------------------------------------------------------
181 for (auto &pair : m_fd_map) {
182 const lldb::socket_t fd = pair.first;
184 if (pair.second.read_set)
185 FD_SET(fd, read_fdset_ptr);
187 if (pair.second.write_set)
188 FD_SET(fd, write_fdset_ptr);
190 if (pair.second.error_set)
191 FD_SET(fd, error_fdset_ptr);
194 //----------------------------------------------------------------------
195 // Setup our timeout time value if needed
196 //----------------------------------------------------------------------
197 struct timeval *tv_ptr = nullptr;
198 struct timeval tv = {0, 0};
201 using namespace std::chrono;
202 //------------------------------------------------------------------
203 // Setup out relative timeout based on the end time if we have one
204 //------------------------------------------------------------------
205 if (m_end_time.hasValue()) {
207 const auto remaining_dur = duration_cast<microseconds>(
208 m_end_time.getValue() - steady_clock::now());
209 if (remaining_dur.count() > 0) {
210 // Wait for a specific amount of time
211 const auto dur_secs = duration_cast<seconds>(remaining_dur);
212 const auto dur_usecs = remaining_dur % seconds(1);
213 tv.tv_sec = dur_secs.count();
214 tv.tv_usec = dur_usecs.count();
216 // Just poll once with no timeout
221 const int num_set_fds = ::select(nfds, read_fdset_ptr, write_fdset_ptr,
222 error_fdset_ptr, tv_ptr);
223 if (num_set_fds < 0) {
225 error.SetErrorToErrno();
226 if (error.GetError() == EINTR) {
228 continue; // Keep calling select if we get EINTR
231 } else if (num_set_fds == 0) {
233 error.SetError(ETIMEDOUT, lldb::eErrorTypePOSIX);
234 error.SetErrorString("timed out");
237 // One or more descriptors were set, update the FDInfo::select_is_set mask
238 // so users can ask the SelectHelper class so clients can call one of:
240 for (auto &pair : m_fd_map) {
241 const int fd = pair.first;
243 if (pair.second.read_set) {
244 if (FD_ISSET(fd, read_fdset_ptr))
245 pair.second.read_is_set = true;
247 if (pair.second.write_set) {
248 if (FD_ISSET(fd, write_fdset_ptr))
249 pair.second.write_is_set = true;
251 if (pair.second.error_set) {
252 if (FD_ISSET(fd, error_fdset_ptr))
253 pair.second.error_is_set = true;