forked from OSchip/llvm-project
301 lines
8.3 KiB
C++
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;
|
|
}
|