]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/tools/lldb/source/Utility/SelectHelper.cpp
Merge from vendor branch importing dtc 1.4.3
[FreeBSD/FreeBSD.git] / contrib / llvm / tools / lldb / source / Utility / SelectHelper.cpp
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(lldb::socket_t fd) {
49   m_fd_map[fd].read_set = true;
50 }
51
52 void SelectHelper::FDSetWrite(lldb::socket_t fd) {
53   m_fd_map[fd].write_set = true;
54 }
55
56 void SelectHelper::FDSetError(lldb::socket_t fd) {
57   m_fd_map[fd].error_set = true;
58 }
59
60 bool 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
68 bool 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
76 bool 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
84 static 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
92 lldb_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 }