SelectHelper.cpp revision 311145
1//===-- SelectHelper.cpp ----------------------------------------*- C++ -*-===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
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
15#endif
16
17// C Includes
18#include <errno.h>
19#if defined(_WIN32)
20// Define NOMINMAX to avoid macros that conflict with std::min and std::max
21#define NOMINMAX
22#include <winsock2.h>
23#else
24#include <sys/select.h>
25#endif
26
27// C++ Includes
28#include <algorithm>
29
30// Other libraries and framework includes
31#include "llvm/ADT/SmallVector.h"
32
33// Project includes
34#include "lldb/Core/Error.h"
35#include "lldb/Utility/LLDBAssert.h"
36#include "lldb/Utility/SelectHelper.h"
37
38SelectHelper::SelectHelper()
39    : m_fd_map(), m_end_time() // Infinite timeout unless
40                               // SelectHelper::SetTimeout() gets called
41{}
42
43void SelectHelper::SetTimeout(const std::chrono::microseconds &timeout) {
44  using namespace std::chrono;
45  m_end_time = steady_clock::time_point(steady_clock::now() + timeout);
46}
47
48void SelectHelper::FDSetRead(lldb::socket_t fd) {
49  m_fd_map[fd].read_set = true;
50}
51
52void SelectHelper::FDSetWrite(lldb::socket_t fd) {
53  m_fd_map[fd].write_set = true;
54}
55
56void SelectHelper::FDSetError(lldb::socket_t fd) {
57  m_fd_map[fd].error_set = true;
58}
59
60bool 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;
64  else
65    return false;
66}
67
68bool 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;
72  else
73    return false;
74}
75
76bool 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;
80  else
81    return false;
82}
83
84static void updateMaxFd(llvm::Optional<lldb::socket_t> &vold,
85                        lldb::socket_t vnew) {
86  if (!vold.hasValue())
87    vold = vnew;
88  else
89    vold = std::max(*vold, vnew);
90}
91
92lldb_private::Error SelectHelper::Select() {
93  lldb_private::Error error;
94#ifdef _MSC_VER
95  // On windows FD_SETSIZE limits the number of file descriptors, not their
96  // numeric value.
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()");
100#endif
101
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);
113      return error;
114    }
115#endif
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);
123  }
124
125  if (!max_fd.hasValue()) {
126    error.SetErrorString("no valid file descriptors");
127    return error;
128  }
129
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;
141
142  if (max_read_fd.hasValue()) {
143    read_fdset.resize((nfds / FD_SETSIZE) + 1);
144    read_fdset_ptr = read_fdset.data();
145  }
146  if (max_write_fd.hasValue()) {
147    write_fdset.resize((nfds / FD_SETSIZE) + 1);
148    write_fdset_ptr = write_fdset.data();
149  }
150  if (max_error_fd.hasValue()) {
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.hasValue()) {
166    FD_ZERO(&read_fdset);
167    read_fdset_ptr = &read_fdset;
168  }
169  if (max_write_fd.hasValue()) {
170    FD_ZERO(&write_fdset);
171    write_fdset_ptr = &write_fdset;
172  }
173  if (max_error_fd.hasValue()) {
174    FD_ZERO(&error_fdset);
175    error_fdset_ptr = &error_fdset;
176  }
177#endif
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;
183
184    if (pair.second.read_set)
185      FD_SET(fd, read_fdset_ptr);
186
187    if (pair.second.write_set)
188      FD_SET(fd, write_fdset_ptr);
189
190    if (pair.second.error_set)
191      FD_SET(fd, error_fdset_ptr);
192  }
193
194  //----------------------------------------------------------------------
195  // Setup our timeout time value if needed
196  //----------------------------------------------------------------------
197  struct timeval *tv_ptr = nullptr;
198  struct timeval tv = {0, 0};
199
200  while (1) {
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()) {
206      tv_ptr = &tv;
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();
215      } else {
216        // Just poll once with no timeout
217        tv.tv_sec = 0;
218        tv.tv_usec = 0;
219      }
220    }
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) {
224      // We got an error
225      error.SetErrorToErrno();
226      if (error.GetError() == EINTR) {
227        error.Clear();
228        continue; // Keep calling select if we get EINTR
229      } else
230        return error;
231    } else if (num_set_fds == 0) {
232      // Timeout
233      error.SetError(ETIMEDOUT, lldb::eErrorTypePOSIX);
234      error.SetErrorString("timed out");
235      return error;
236    } else {
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:
239
240      for (auto &pair : m_fd_map) {
241        const int fd = pair.first;
242
243        if (pair.second.read_set) {
244          if (FD_ISSET(fd, read_fdset_ptr))
245            pair.second.read_is_set = true;
246        }
247        if (pair.second.write_set) {
248          if (FD_ISSET(fd, write_fdset_ptr))
249            pair.second.write_is_set = true;
250        }
251        if (pair.second.error_set) {
252          if (FD_ISSET(fd, error_fdset_ptr))
253            pair.second.error_is_set = true;
254        }
255      }
256      break;
257    }
258  }
259  return error;
260}
261