LLDB  mainline
SelectHelper.cpp
Go to the documentation of this file.
1 //===-- SelectHelper.cpp ----------------------------------------*- C++ -*-===//
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"
19 #include "lldb/lldb-enumerations.h"
20 #include "lldb/lldb-types.h"
21 
22 #include "llvm/ADT/DenseMap.h"
23 #include "llvm/ADT/Optional.h"
24 
25 #include <algorithm>
26 #include <chrono>
27 
28 #include <errno.h>
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 
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 
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 
85 static void updateMaxFd(llvm::Optional<lldb::socket_t> &vold,
86  lldb::socket_t vnew) {
87  if (!vold.hasValue())
88  vold = vnew;
89  else
90  vold = std::max(*vold, vnew);
91 }
92 
95 #ifdef _MSC_VER
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  llvm::Optional<lldb::socket_t> max_read_fd;
104  llvm::Optional<lldb::socket_t> max_write_fd;
105  llvm::Optional<lldb::socket_t> max_error_fd;
106  llvm::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(_MSC_VER)
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.hasValue()) {
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.hasValue()) {
142  read_fdset.resize((nfds / FD_SETSIZE) + 1);
143  read_fdset_ptr = read_fdset.data();
144  }
145  if (max_write_fd.hasValue()) {
146  write_fdset.resize((nfds / FD_SETSIZE) + 1);
147  write_fdset_ptr = write_fdset.data();
148  }
149  if (max_error_fd.hasValue()) {
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.hasValue()) {
165  FD_ZERO(&read_fdset);
166  read_fdset_ptr = &read_fdset;
167  }
168  if (max_write_fd.hasValue()) {
169  FD_ZERO(&write_fdset);
170  write_fdset_ptr = &write_fdset;
171  }
172  if (max_error_fd.hasValue()) {
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 (1) {
196  using namespace std::chrono;
197  // Setup out relative timeout based on the end time if we have one
198  if (m_end_time.hasValue()) {
199  tv_ptr = &tv;
200  const auto remaining_dur = duration_cast<microseconds>(
201  m_end_time.getValue() - 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 }
void FDSetRead(lldb::socket_t fd)
void FDSetWrite(lldb::socket_t fd)
#define lldbassert(x)
Definition: LLDBAssert.h:15
bool FDIsSetError(lldb::socket_t fd) const
void SetTimeout(const std::chrono::microseconds &timeout)
static void updateMaxFd(llvm::Optional< lldb::socket_t > &vold, lldb::socket_t vnew)
POSIX error codes.
void SetErrorToErrno()
Set the current error to errno.
Definition: Status.cpp:223
int socket_t
Definition: lldb-types.h:60
lldb_private::Status Select()
void FDSetError(lldb::socket_t fd)
void Clear()
Clear the object state.
Definition: Status.cpp:167
void SetErrorString(llvm::StringRef err_str)
Set the current error string to err_str.
Definition: Status.cpp:241
bool FDIsSetRead(lldb::socket_t fd) const
llvm::Optional< std::chrono::steady_clock::time_point > m_end_time
Definition: SelectHelper.h:68
bool FDIsSetWrite(lldb::socket_t fd) const
int void SetError(ValueType err, lldb::ErrorType type)
Set accesssor with an error value and type.
Definition: Status.cpp:216
int SetErrorStringWithFormat(const char *format,...) __attribute__((format(printf
Set the current error string to a formatted error string.
Definition: Status.cpp:255
ValueType GetError() const
Access the error value.
Definition: Status.cpp:174
llvm::DenseMap< lldb::socket_t, FDInfo > m_fd_map
Definition: SelectHelper.h:67
An error handling class.
Definition: Status.h:44