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