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 #include "lldb/Utility/SelectHelper.h"
18 #include "lldb/Utility/LLDBAssert.h"
19 #include "lldb/Utility/Status.h"
20 #include "lldb/lldb-enumerations.h" // for ErrorType::eErrorTypePOSIX
21 #include "lldb/lldb-types.h"        // for socket_t
22 
23 #include "llvm/ADT/DenseMap.h" // for DenseMapPair, DenseMap, Dense...
24 #include "llvm/ADT/Optional.h" // for Optional
25 
26 #include <algorithm>
27 #include <chrono> // for microseconds, seconds, steady...
28 
29 #include <errno.h>
30 #if defined(_WIN32)
31 // Define NOMINMAX to avoid macros that conflict with std::min and std::max
32 #define NOMINMAX
33 #include <winsock2.h>
34 #else
35 #include <sys/time.h>
36 #include <sys/select.h>
37 #endif
38 
39 
40 SelectHelper::SelectHelper()
41     : m_fd_map(), m_end_time() // Infinite timeout unless
42                                // SelectHelper::SetTimeout() gets called
43 {}
44 
45 void SelectHelper::SetTimeout(const std::chrono::microseconds &timeout) {
46   using namespace std::chrono;
47   m_end_time = steady_clock::time_point(steady_clock::now() + timeout);
48 }
49 
50 void SelectHelper::FDSetRead(lldb::socket_t fd) {
51   m_fd_map[fd].read_set = true;
52 }
53 
54 void SelectHelper::FDSetWrite(lldb::socket_t fd) {
55   m_fd_map[fd].write_set = true;
56 }
57 
58 void SelectHelper::FDSetError(lldb::socket_t fd) {
59   m_fd_map[fd].error_set = true;
60 }
61 
62 bool SelectHelper::FDIsSetRead(lldb::socket_t fd) const {
63   auto pos = m_fd_map.find(fd);
64   if (pos != m_fd_map.end())
65     return pos->second.read_is_set;
66   else
67     return false;
68 }
69 
70 bool SelectHelper::FDIsSetWrite(lldb::socket_t fd) const {
71   auto pos = m_fd_map.find(fd);
72   if (pos != m_fd_map.end())
73     return pos->second.write_is_set;
74   else
75     return false;
76 }
77 
78 bool SelectHelper::FDIsSetError(lldb::socket_t fd) const {
79   auto pos = m_fd_map.find(fd);
80   if (pos != m_fd_map.end())
81     return pos->second.error_is_set;
82   else
83     return false;
84 }
85 
86 static void updateMaxFd(llvm::Optional<lldb::socket_t> &vold,
87                         lldb::socket_t vnew) {
88   if (!vold.hasValue())
89     vold = vnew;
90   else
91     vold = std::max(*vold, vnew);
92 }
93 
94 lldb_private::Status SelectHelper::Select() {
95   lldb_private::Status error;
96 #ifdef _MSC_VER
97   // On windows FD_SETSIZE limits the number of file descriptors, not their
98   // numeric value.
99   lldbassert(m_fd_map.size() <= FD_SETSIZE);
100   if (m_fd_map.size() > FD_SETSIZE)
101     return lldb_private::Status("Too many file descriptors for select()");
102 #endif
103 
104   llvm::Optional<lldb::socket_t> max_read_fd;
105   llvm::Optional<lldb::socket_t> max_write_fd;
106   llvm::Optional<lldb::socket_t> max_error_fd;
107   llvm::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(_MSC_VER)
112     lldbassert(fd < FD_SETSIZE);
113     if (fd >= FD_SETSIZE) {
114       error.SetErrorStringWithFormat("%i is too large for select()", fd);
115       return error;
116     }
117 #endif
118     if (pair.second.read_set)
119       updateMaxFd(max_read_fd, fd);
120     if (pair.second.write_set)
121       updateMaxFd(max_write_fd, fd);
122     if (pair.second.error_set)
123       updateMaxFd(max_error_fd, fd);
124     updateMaxFd(max_fd, fd);
125   }
126 
127   if (!max_fd.hasValue()) {
128     error.SetErrorString("no valid file descriptors");
129     return error;
130   }
131 
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 //----------------------------------------------------------------------
137 // Initialize and zero out the fdsets
138 //----------------------------------------------------------------------
139 #if defined(__APPLE__)
140   llvm::SmallVector<fd_set, 1> read_fdset;
141   llvm::SmallVector<fd_set, 1> write_fdset;
142   llvm::SmallVector<fd_set, 1> error_fdset;
143 
144   if (max_read_fd.hasValue()) {
145     read_fdset.resize((nfds / FD_SETSIZE) + 1);
146     read_fdset_ptr = read_fdset.data();
147   }
148   if (max_write_fd.hasValue()) {
149     write_fdset.resize((nfds / FD_SETSIZE) + 1);
150     write_fdset_ptr = write_fdset.data();
151   }
152   if (max_error_fd.hasValue()) {
153     error_fdset.resize((nfds / FD_SETSIZE) + 1);
154     error_fdset_ptr = error_fdset.data();
155   }
156   for (auto &fd_set : read_fdset)
157     FD_ZERO(&fd_set);
158   for (auto &fd_set : write_fdset)
159     FD_ZERO(&fd_set);
160   for (auto &fd_set : error_fdset)
161     FD_ZERO(&fd_set);
162 #else
163   fd_set read_fdset;
164   fd_set write_fdset;
165   fd_set error_fdset;
166 
167   if (max_read_fd.hasValue()) {
168     FD_ZERO(&read_fdset);
169     read_fdset_ptr = &read_fdset;
170   }
171   if (max_write_fd.hasValue()) {
172     FD_ZERO(&write_fdset);
173     write_fdset_ptr = &write_fdset;
174   }
175   if (max_error_fd.hasValue()) {
176     FD_ZERO(&error_fdset);
177     error_fdset_ptr = &error_fdset;
178   }
179 #endif
180   //----------------------------------------------------------------------
181   // Set the FD bits in the fdsets for read/write/error
182   //----------------------------------------------------------------------
183   for (auto &pair : m_fd_map) {
184     const lldb::socket_t fd = pair.first;
185 
186     if (pair.second.read_set)
187       FD_SET(fd, read_fdset_ptr);
188 
189     if (pair.second.write_set)
190       FD_SET(fd, write_fdset_ptr);
191 
192     if (pair.second.error_set)
193       FD_SET(fd, error_fdset_ptr);
194   }
195 
196   //----------------------------------------------------------------------
197   // Setup our timeout time value if needed
198   //----------------------------------------------------------------------
199   struct timeval *tv_ptr = nullptr;
200   struct timeval tv = {0, 0};
201 
202   while (1) {
203     using namespace std::chrono;
204     //------------------------------------------------------------------
205     // Setup out relative timeout based on the end time if we have one
206     //------------------------------------------------------------------
207     if (m_end_time.hasValue()) {
208       tv_ptr = &tv;
209       const auto remaining_dur = duration_cast<microseconds>(
210           m_end_time.getValue() - steady_clock::now());
211       if (remaining_dur.count() > 0) {
212         // Wait for a specific amount of time
213         const auto dur_secs = duration_cast<seconds>(remaining_dur);
214         const auto dur_usecs = remaining_dur % seconds(1);
215         tv.tv_sec = dur_secs.count();
216         tv.tv_usec = dur_usecs.count();
217       } else {
218         // Just poll once with no timeout
219         tv.tv_sec = 0;
220         tv.tv_usec = 0;
221       }
222     }
223     const int num_set_fds = ::select(nfds, read_fdset_ptr, write_fdset_ptr,
224                                      error_fdset_ptr, tv_ptr);
225     if (num_set_fds < 0) {
226       // We got an error
227       error.SetErrorToErrno();
228       if (error.GetError() == EINTR) {
229         error.Clear();
230         continue; // Keep calling select if we get EINTR
231       } else
232         return error;
233     } else if (num_set_fds == 0) {
234       // Timeout
235       error.SetError(ETIMEDOUT, lldb::eErrorTypePOSIX);
236       error.SetErrorString("timed out");
237       return error;
238     } else {
239       // One or more descriptors were set, update the FDInfo::select_is_set mask
240       // so users can ask the SelectHelper class so clients can call one of:
241 
242       for (auto &pair : m_fd_map) {
243         const int fd = pair.first;
244 
245         if (pair.second.read_set) {
246           if (FD_ISSET(fd, read_fdset_ptr))
247             pair.second.read_is_set = true;
248         }
249         if (pair.second.write_set) {
250           if (FD_ISSET(fd, write_fdset_ptr))
251             pair.second.write_is_set = true;
252         }
253         if (pair.second.error_set) {
254           if (FD_ISSET(fd, error_fdset_ptr))
255             pair.second.error_is_set = true;
256         }
257       }
258       break;
259     }
260   }
261   return error;
262 }
263