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