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