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