llvm-project/lldb/source/Utility/SelectHelper.cpp

301 lines
8.3 KiB
C++

//===-- SelectHelper.cpp ----------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#if defined(__APPLE__)
// Enable this special support for Apple builds where we can have unlimited
// select bounds. We tried switching to poll() and kqueue and we were panicing
// the kernel, so we have to stick with select for now.
#define _DARWIN_UNLIMITED_SELECT
#endif
// C Includes
#include <errno.h>
#if defined(_WIN32)
// Define NOMINMAX to avoid macros that conflict with std::min and std::max
#define NOMINMAX
#include <winsock2.h>
#else
#include <sys/select.h>
#endif
// C++ Includes
#include <algorithm>
// Other libraries and framework includes
#include "llvm/ADT/SmallVector.h"
// Project includes
#include "lldb/Core/Error.h"
#include "lldb/Utility/SelectHelper.h"
#include "lldb/Utility/LLDBAssert.h"
SelectHelper::SelectHelper() :
m_fd_map(),
m_end_time() // Infinite timeout unless SelectHelper::SetTimeout() gets called
{
}
void
SelectHelper::SetTimeout(const std::chrono::microseconds &timeout)
{
using namespace std::chrono;
m_end_time = steady_clock::time_point(steady_clock::now() + timeout);
}
void
SelectHelper::FDSetRead(int fd)
{
m_fd_map[fd].read_set = true;
}
void
SelectHelper::FDSetWrite(int fd)
{
m_fd_map[fd].write_set = true;
}
void
SelectHelper::FDSetError(int fd)
{
m_fd_map[fd].error_set = true;
}
bool
SelectHelper::FDIsSetRead(int fd) const
{
auto pos = m_fd_map.find(fd);
if (pos != m_fd_map.end())
return pos->second.read_is_set;
else
return false;
}
bool
SelectHelper::FDIsSetWrite(int fd) const
{
auto pos = m_fd_map.find(fd);
if (pos != m_fd_map.end())
return pos->second.write_is_set;
else
return false;
}
bool
SelectHelper::FDIsSetError(int fd) const
{
auto pos = m_fd_map.find(fd);
if (pos != m_fd_map.end())
return pos->second.error_is_set;
else
return false;
}
lldb_private::Error
SelectHelper::Select()
{
lldb_private::Error error;
#ifdef _MSC_VER
// On windows FD_SETSIZE limits the number of file descriptors, not their numeric value.
lldbassert(m_fd_map.size() <= FD_SETSIZE);
if (m_fd_map.size() > FD_SETSIZE)
return lldb_private::Error("Too many file descriptors for select()");
#endif
int max_read_fd = -1;
int max_write_fd = -1;
int max_error_fd = -1;
int max_fd = -1;
for (auto &pair : m_fd_map)
{
pair.second.PrepareForSelect();
const int fd = pair.first;
#if !defined(__APPLE__) && !defined(_MSC_VER)
lldbassert(fd < FD_SETSIZE);
if (fd >= FD_SETSIZE)
{
error.SetErrorStringWithFormat("%i is too large for select()", fd);
return error;
}
#endif
if (pair.second.read_set)
{
max_read_fd = std::max<int>(fd, max_read_fd);
max_fd = std::max<int>(fd, max_fd);
}
if (pair.second.write_set)
{
max_write_fd = std::max<int>(fd, max_write_fd);
max_fd = std::max<int>(fd, max_fd);
}
if (pair.second.error_set)
{
max_error_fd = std::max<int>(fd, max_error_fd);
max_fd = std::max<int>(fd, max_fd);
}
}
if (max_fd == -1)
{
error.SetErrorString("no valid file descriptors");
return error;
}
const int nfds = max_fd + 1;
fd_set *read_fdset_ptr = nullptr;
fd_set *write_fdset_ptr = nullptr;
fd_set *error_fdset_ptr = nullptr;
//----------------------------------------------------------------------
// Initialize and zero out the fdsets
//----------------------------------------------------------------------
#if defined(__APPLE__)
llvm::SmallVector<fd_set, 1> read_fdset;
llvm::SmallVector<fd_set, 1> write_fdset;
llvm::SmallVector<fd_set, 1> error_fdset;
if (max_read_fd >= 0)
{
read_fdset.resize((nfds / FD_SETSIZE) + 1);
read_fdset_ptr = read_fdset.data();
}
if (max_write_fd >= 0)
{
write_fdset.resize((nfds / FD_SETSIZE) + 1);
write_fdset_ptr = write_fdset.data();
}
if (max_error_fd >= 0)
{
error_fdset.resize((nfds / FD_SETSIZE) + 1);
error_fdset_ptr = error_fdset.data();
}
for (auto &fd_set : read_fdset)
FD_ZERO(&fd_set);
for (auto &fd_set : write_fdset)
FD_ZERO(&fd_set);
for (auto &fd_set : error_fdset)
FD_ZERO(&fd_set);
#else
fd_set read_fdset;
fd_set write_fdset;
fd_set error_fdset;
if (max_read_fd >= 0)
{
FD_ZERO(&read_fdset);
read_fdset_ptr = &read_fdset;
}
if (max_write_fd >= 0)
{
FD_ZERO(&write_fdset);
write_fdset_ptr = &write_fdset;
}
if (max_error_fd >= 0)
{
FD_ZERO(&error_fdset);
error_fdset_ptr = &error_fdset;
}
#endif
//----------------------------------------------------------------------
// Set the FD bits in the fdsets for read/write/error
//----------------------------------------------------------------------
for (auto &pair : m_fd_map)
{
const int fd = pair.first;
if (pair.second.read_set)
FD_SET(fd, read_fdset_ptr);
if (pair.second.write_set)
FD_SET(fd, write_fdset_ptr);
if (pair.second.error_set)
FD_SET(fd, error_fdset_ptr);
}
//----------------------------------------------------------------------
// Setup our timeout time value if needed
//----------------------------------------------------------------------
struct timeval *tv_ptr = nullptr;
struct timeval tv = {0, 0};
while (1)
{
using namespace std::chrono;
//------------------------------------------------------------------
// Setup out relative timeout based on the end time if we have one
//------------------------------------------------------------------
if (m_end_time.hasValue())
{
tv_ptr = &tv;
const auto remaining_dur = duration_cast<microseconds>(m_end_time.getValue() - steady_clock::now());
if (remaining_dur.count() > 0)
{
// Wait for a specific amount of time
const auto dur_secs = duration_cast<seconds>(remaining_dur);
const auto dur_usecs = remaining_dur % seconds(1);
tv.tv_sec = dur_secs.count();
tv.tv_usec = dur_usecs.count();
}
else
{
// Just poll once with no timeout
tv.tv_sec = 0;
tv.tv_usec = 0;
}
}
const int num_set_fds = ::select(nfds, read_fdset_ptr, write_fdset_ptr, error_fdset_ptr, tv_ptr);
if (num_set_fds < 0)
{
// We got an error
error.SetErrorToErrno();
if (error.GetError() == EINTR)
{
error.Clear();
continue; // Keep calling select if we get EINTR
}
else
return error;
}
else if (num_set_fds == 0)
{
// Timeout
error.SetError(ETIMEDOUT, lldb::eErrorTypePOSIX);
error.SetErrorString("timed out");
return error;
}
else
{
// One or more descriptors were set, update the FDInfo::select_is_set mask
// so users can ask the SelectHelper class so clients can call one of:
for (auto &pair : m_fd_map)
{
const int fd = pair.first;
if (pair.second.read_set)
{
if (FD_ISSET(fd, read_fdset_ptr))
pair.second.read_is_set = true;
}
if (pair.second.write_set)
{
if (FD_ISSET(fd, write_fdset_ptr))
pair.second.write_is_set = true;
}
if (pair.second.error_set)
{
if (FD_ISSET(fd, error_fdset_ptr))
pair.second.error_is_set = true;
}
}
break;
}
}
return error;
}