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