Remove duplicated code

We preivously had two copies of ::BytesAvailable with only trivial
differences between them, and fixes have been applied to only one of
them.

Instead of duplicating the whole function, hide the FD_SET differences
behind a macro.  This leaves only one small __APPLE__-specific #if
block, and fixes ^C on non-__APPLE__ platforms.

llvm-svn: 210592
This commit is contained in:
Ed Maste 2014-06-10 21:33:43 +00:00
parent f5d07fa586
commit f57dcbb615
1 changed files with 21 additions and 180 deletions

View File

@ -707,18 +707,19 @@ ConnectionFileDescriptor::Write (const void *src, size_t src_len, ConnectionStat
#if defined(__APPLE__)
// This ConnectionFileDescriptor::BytesAvailable() uses select(). // This ConnectionFileDescriptor::BytesAvailable() uses select().
// //
// PROS: // PROS:
// - select is consistent across most unix platforms // - select is consistent across most unix platforms
// - this Apple specific version allows for unlimited fds in the fd_sets by // - The Apple specific version allows for unlimited fds in the fd_sets by
// setting the _DARWIN_UNLIMITED_SELECT define prior to including the // setting the _DARWIN_UNLIMITED_SELECT define prior to including the
// required header files. // required header files.
// CONS: #if defined(__APPLE__)
// - Darwin only #define FD_SET_DATA(fds) fds.data()
#else
#define FD_SET_DATA(fds) &fds
#endif
ConnectionStatus ConnectionStatus
ConnectionFileDescriptor::BytesAvailable (uint32_t timeout_usec, Error *error_ptr) ConnectionFileDescriptor::BytesAvailable (uint32_t timeout_usec, Error *error_ptr)
@ -755,10 +756,16 @@ ConnectionFileDescriptor::BytesAvailable (uint32_t timeout_usec, Error *error_pt
if (data_fd >= 0) if (data_fd >= 0)
{ {
const bool have_pipe_fd = pipe_fd >= 0; const bool have_pipe_fd = pipe_fd >= 0;
#if !defined(__APPLE__) && !defined(_MSC_VER)
assert (data_fd < FD_SETSIZE);
if (have_pipe_fd)
assert (pipe_fd < FD_SETSIZE);
#endif
while (data_fd == m_fd_recv) while (data_fd == m_fd_recv)
{ {
const int nfds = std::max<int>(data_fd, pipe_fd) + 1; const int nfds = std::max<int>(data_fd, pipe_fd) + 1;
#if defined(__APPLE__)
llvm::SmallVector<fd_set, 1> read_fds; llvm::SmallVector<fd_set, 1> read_fds;
read_fds.resize((nfds/FD_SETSIZE) + 1); read_fds.resize((nfds/FD_SETSIZE) + 1);
for (size_t i=0; i<read_fds.size(); ++i) for (size_t i=0; i<read_fds.size(); ++i)
@ -766,9 +773,13 @@ ConnectionFileDescriptor::BytesAvailable (uint32_t timeout_usec, Error *error_pt
// FD_SET doesn't bounds check, it just happily walks off the end // FD_SET doesn't bounds check, it just happily walks off the end
// but we have taken care of making the extra storage with our // but we have taken care of making the extra storage with our
// SmallVector of fd_set objects // SmallVector of fd_set objects
FD_SET (data_fd, read_fds.data()); #else
fd_set read_fds;
FD_ZERO (&read_fds);
#endif
FD_SET (data_fd, FD_SET_DATA(read_fds));
if (have_pipe_fd) if (have_pipe_fd)
FD_SET (pipe_fd, read_fds.data()); FD_SET (pipe_fd, FD_SET_DATA(read_fds));
Error error; Error error;
@ -784,7 +795,7 @@ ConnectionFileDescriptor::BytesAvailable (uint32_t timeout_usec, Error *error_pt
static_cast<void*>(tv_ptr)); static_cast<void*>(tv_ptr));
} }
const int num_set_fds = ::select (nfds, read_fds.data(), NULL, NULL, tv_ptr); const int num_set_fds = ::select (nfds, FD_SET_DATA(read_fds), NULL, NULL, tv_ptr);
if (num_set_fds < 0) if (num_set_fds < 0)
error.SetErrorToErrno(); error.SetErrorToErrno();
else else
@ -833,11 +844,9 @@ ConnectionFileDescriptor::BytesAvailable (uint32_t timeout_usec, Error *error_pt
} }
else if (num_set_fds > 0) else if (num_set_fds > 0)
{ {
// FD_ISSET is happy to deal with a something larger than if (FD_ISSET(data_fd, FD_SET_DATA(read_fds)))
// a single fd_set.
if (FD_ISSET(data_fd, read_fds.data()))
return eConnectionStatusSuccess; return eConnectionStatusSuccess;
if (have_pipe_fd && FD_ISSET(pipe_fd, read_fds.data())) if (have_pipe_fd && FD_ISSET(pipe_fd, FD_SET_DATA(read_fds)))
{ {
// We got a command to exit. Read the data from that pipe: // We got a command to exit. Read the data from that pipe:
char buffer[16]; char buffer[16];
@ -869,174 +878,6 @@ ConnectionFileDescriptor::BytesAvailable (uint32_t timeout_usec, Error *error_pt
error_ptr->SetErrorString("not connected"); error_ptr->SetErrorString("not connected");
return eConnectionStatusLostConnection; return eConnectionStatusLostConnection;
} }
#else
// This ConnectionFileDescriptor::BytesAvailable() uses select().
//
// PROS:
// - select is consistent across most unix platforms
// CONS:
// - only supports file descriptors up to FD_SETSIZE. This implementation
// will assert if it runs into that hard limit to let users know that
// another ConnectionFileDescriptor::BytesAvailable() should be used
// or a new version of ConnectionFileDescriptor::BytesAvailable() should
// be written for the system that is running into the limitations. MacOSX
// uses kqueues, and there is a poll() based implementation below.
ConnectionStatus
ConnectionFileDescriptor::BytesAvailable (uint32_t timeout_usec, Error *error_ptr)
{
// Don't need to take the mutex here separately since we are only called from Read. If we
// ever get used more generally we will need to lock here as well.
Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION));
if (log)
log->Printf("%p ConnectionFileDescriptor::BytesAvailable (timeout_usec = %u)",
static_cast<void*>(this), timeout_usec);
struct timeval *tv_ptr;
struct timeval tv;
if (timeout_usec == UINT32_MAX)
{
// Infinite wait...
tv_ptr = NULL;
}
else
{
TimeValue time_value;
time_value.OffsetWithMicroSeconds (timeout_usec);
tv.tv_sec = time_value.seconds();
tv.tv_usec = time_value.microseconds();
tv_ptr = &tv;
}
// Make a copy of the file descriptors to make sure we don't
// have another thread change these values out from under us
// and cause problems in the loop below where like in FS_SET()
const int data_fd = m_fd_recv;
const int pipe_fd = m_pipe_read;
if (data_fd >= 0)
{
// If this assert fires off on MacOSX, we will need to switch to using
// libdispatch to read from file descriptors because poll() is causing
// kernel panics and if we exceed FD_SETSIZE we will have no choice...
#ifndef _MSC_VER
assert (data_fd < FD_SETSIZE);
#endif
const bool have_pipe_fd = pipe_fd >= 0;
if (have_pipe_fd)
{
assert (pipe_fd < FD_SETSIZE);
}
while (data_fd == m_fd_recv)
{
fd_set read_fds;
FD_ZERO (&read_fds);
FD_SET (data_fd, &read_fds);
if (have_pipe_fd)
FD_SET (pipe_fd, &read_fds);
const int nfds = std::max<int>(data_fd, pipe_fd) + 1;
Error error;
if (log)
{
if (have_pipe_fd)
log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i, %i}, NULL, NULL, timeout=%p)...",
static_cast<void*>(this), nfds, data_fd, pipe_fd,
static_cast<void*>(tv_ptr));
else
log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i}, NULL, NULL, timeout=%p)...",
static_cast<void*>(this), nfds, data_fd,
static_cast<void*>(tv_ptr));
}
const int num_set_fds = ::select (nfds, &read_fds, NULL, NULL, tv_ptr);
if (num_set_fds < 0)
error.SetErrorToErrno();
else
error.Clear();
if (log)
{
if (have_pipe_fd)
log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i, %i}, NULL, NULL, timeout=%p) => %d, error = %s",
static_cast<void*>(this), nfds, data_fd, pipe_fd,
static_cast<void*>(tv_ptr), num_set_fds,
error.AsCString());
else
log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i}, NULL, NULL, timeout=%p) => %d, error = %s",
static_cast<void*>(this), nfds, data_fd,
static_cast<void*>(tv_ptr), num_set_fds,
error.AsCString());
}
if (error_ptr)
*error_ptr = error;
if (error.Fail())
{
switch (error.GetError())
{
case EBADF: // One of the descriptor sets specified an invalid descriptor.
return eConnectionStatusLostConnection;
case EINVAL: // The specified time limit is invalid. One of its components is negative or too large.
default: // Other unknown error
return eConnectionStatusError;
case EAGAIN: // The kernel was (perhaps temporarily) unable to
// allocate the requested number of file descriptors,
// or we have non-blocking IO
case EINTR: // A signal was delivered before the time limit
// expired and before any of the selected events
// occurred.
break; // Lets keep reading to until we timeout
}
}
else if (num_set_fds == 0)
{
return eConnectionStatusTimedOut;
}
else if (num_set_fds > 0)
{
if (FD_ISSET(data_fd, &read_fds))
return eConnectionStatusSuccess;
if (have_pipe_fd && FD_ISSET(pipe_fd, &read_fds))
{
// We got a command to exit. Read the data from that pipe:
char buffer[16];
ssize_t bytes_read;
do
{
bytes_read = ::read (pipe_fd, buffer, sizeof(buffer));
} while (bytes_read < 0 && errno == EINTR);
assert (bytes_read == 1 && buffer[0] == 'q');
if (log)
log->Printf("%p ConnectionFileDescriptor::BytesAvailable() got data: %*s from the command channel.",
static_cast<void*>(this),
static_cast<int>(bytes_read), buffer);
return eConnectionStatusEndOfFile;
}
}
}
}
if (error_ptr)
error_ptr->SetErrorString("not connected");
return eConnectionStatusLostConnection;
}
#endif
#if 0 #if 0
#include <poll.h> #include <poll.h>