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