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 } 261