SelectHelper.cpp revision 341825
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 40SelectHelper::SelectHelper() 41 : m_fd_map(), m_end_time() // Infinite timeout unless 42 // SelectHelper::SetTimeout() gets called 43{} 44 45void 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 50void SelectHelper::FDSetRead(lldb::socket_t fd) { 51 m_fd_map[fd].read_set = true; 52} 53 54void SelectHelper::FDSetWrite(lldb::socket_t fd) { 55 m_fd_map[fd].write_set = true; 56} 57 58void SelectHelper::FDSetError(lldb::socket_t fd) { 59 m_fd_map[fd].error_set = true; 60} 61 62bool 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 70bool 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 78bool 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 86static 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 94lldb_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 < static_cast<int>(FD_SETSIZE)); 113 if (fd >= static_cast<int>(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 240 // mask so users can ask the SelectHelper class so clients can call one 241 // of: 242 243 for (auto &pair : m_fd_map) { 244 const int fd = pair.first; 245 246 if (pair.second.read_set) { 247 if (FD_ISSET(fd, read_fdset_ptr)) 248 pair.second.read_is_set = true; 249 } 250 if (pair.second.write_set) { 251 if (FD_ISSET(fd, write_fdset_ptr)) 252 pair.second.write_is_set = true; 253 } 254 if (pair.second.error_set) { 255 if (FD_ISSET(fd, error_fdset_ptr)) 256 pair.second.error_is_set = true; 257 } 258 } 259 break; 260 } 261 } 262 return error; 263} 264