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 
104     int max_read_fd = -1;
105     int max_write_fd = -1;
106     int max_error_fd = -1;
107     int max_fd = -1;
108     for (auto &pair : m_fd_map)
109     {
110         pair.second.PrepareForSelect();
111         const int fd = pair.first;
112 #if !defined(__APPLE__)
113         lldbassert(fd < FD_SETSIZE);
114         if (fd >= FD_SETSIZE)
115         {
116             error.SetErrorStringWithFormat("%i is too large for select()", fd);
117             return error;
118         }
119 #endif
120         if (pair.second.read_set)
121         {
122             max_read_fd = std::max<int>(fd, max_read_fd);
123             max_fd = std::max<int>(fd, max_fd);
124         }
125         if (pair.second.write_set)
126         {
127             max_write_fd = std::max<int>(fd, max_write_fd);
128             max_fd = std::max<int>(fd, max_fd);
129         }
130         if (pair.second.error_set)
131         {
132             max_error_fd = std::max<int>(fd, max_error_fd);
133             max_fd = std::max<int>(fd, max_fd);
134         }
135     }
136 
137     if (max_fd == -1)
138     {
139         error.SetErrorString("no valid file descriptors");
140         return error;
141     }
142 
143     const int nfds = max_fd + 1;
144     fd_set *read_fdset_ptr = nullptr;
145     fd_set *write_fdset_ptr = nullptr;
146     fd_set *error_fdset_ptr = nullptr;
147     //----------------------------------------------------------------------
148     // Initialize and zero out the fdsets
149     //----------------------------------------------------------------------
150 #if defined(__APPLE__)
151     llvm::SmallVector<fd_set, 1> read_fdset;
152     llvm::SmallVector<fd_set, 1> write_fdset;
153     llvm::SmallVector<fd_set, 1> error_fdset;
154 
155     if (max_read_fd >= 0)
156     {
157         read_fdset.resize((nfds / FD_SETSIZE) + 1);
158         read_fdset_ptr = read_fdset.data();
159     }
160     if (max_write_fd >= 0)
161     {
162         write_fdset.resize((nfds / FD_SETSIZE) + 1);
163         write_fdset_ptr = write_fdset.data();
164     }
165     if (max_error_fd >= 0)
166     {
167         error_fdset.resize((nfds / FD_SETSIZE) + 1);
168         error_fdset_ptr = error_fdset.data();
169     }
170     for (auto &fd_set : read_fdset)
171         FD_ZERO(&fd_set);
172     for (auto &fd_set : write_fdset)
173         FD_ZERO(&fd_set);
174     for (auto &fd_set : error_fdset)
175         FD_ZERO(&fd_set);
176 #else
177     fd_set read_fdset;
178     fd_set write_fdset;
179     fd_set error_fdset;
180 
181     if (max_read_fd >= 0)
182     {
183         FD_ZERO(&read_fdset);
184         read_fdset_ptr = &read_fdset;
185     }
186     if (max_write_fd >= 0)
187     {
188         FD_ZERO(&write_fdset);
189         write_fdset_ptr = &write_fdset;
190     }
191     if (max_error_fd >= 0)
192     {
193         FD_ZERO(&error_fdset);
194         error_fdset_ptr = &error_fdset;
195     }
196 #endif
197     //----------------------------------------------------------------------
198     // Set the FD bits in the fdsets for read/write/error
199     //----------------------------------------------------------------------
200     for (auto &pair : m_fd_map)
201     {
202         const int fd = pair.first;
203 
204         if (pair.second.read_set)
205             FD_SET(fd, read_fdset_ptr);
206 
207         if (pair.second.write_set)
208             FD_SET(fd, write_fdset_ptr);
209 
210         if (pair.second.error_set)
211             FD_SET(fd, error_fdset_ptr);
212     }
213 
214     //----------------------------------------------------------------------
215     // Setup our timeout time value if needed
216     //----------------------------------------------------------------------
217     struct timeval *tv_ptr = nullptr;
218     struct timeval tv = {0, 0};
219 
220     while (1)
221     {
222         using namespace std::chrono;
223         //------------------------------------------------------------------
224         // Setup out relative timeout based on the end time if we have one
225         //------------------------------------------------------------------
226         if (m_end_time.hasValue())
227         {
228             tv_ptr = &tv;
229             const auto remaining_dur = duration_cast<microseconds>(m_end_time.getValue() - steady_clock::now());
230             if (remaining_dur.count() > 0)
231             {
232                 // Wait for a specific amount of time
233                 const auto dur_secs = duration_cast<seconds>(remaining_dur);
234                 const auto dur_usecs = remaining_dur % seconds(1);
235                 tv.tv_sec = dur_secs.count();
236                 tv.tv_usec = dur_usecs.count();
237             }
238             else
239             {
240                 // Just poll once with no timeout
241                 tv.tv_sec = 0;
242                 tv.tv_usec = 0;
243             }
244         }
245         const int num_set_fds = ::select(nfds, read_fdset_ptr, write_fdset_ptr, error_fdset_ptr, tv_ptr);
246         if (num_set_fds < 0)
247         {
248             // We got an error
249             error.SetErrorToErrno();
250             if (error.GetError() == EINTR)
251             {
252                 error.Clear();
253                 continue; // Keep calling select if we get EINTR
254             }
255             else
256                 return error;
257         }
258         else if (num_set_fds == 0)
259         {
260             // Timeout
261             error.SetError(ETIMEDOUT, lldb::eErrorTypePOSIX);
262             error.SetErrorString("timed out");
263             return error;
264         }
265         else
266         {
267             // One or more descriptors were set, update the FDInfo::select_is_set mask
268             // so users can ask the SelectHelper class so clients can call one of:
269 
270             for (auto &pair : m_fd_map)
271             {
272                 const int fd = pair.first;
273 
274                 if (pair.second.read_set)
275                 {
276                     if (FD_ISSET(fd, read_fdset_ptr))
277                         pair.second.read_is_set = true;
278                 }
279                 if (pair.second.write_set)
280                 {
281                     if (FD_ISSET(fd, write_fdset_ptr))
282                         pair.second.write_is_set = true;
283                 }
284                 if (pair.second.error_set)
285                 {
286                     if (FD_ISSET(fd, error_fdset_ptr))
287                         pair.second.error_is_set = true;
288                 }
289             }
290             break;
291         }
292     }
293     return error;
294 }
295