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