Change SyncSocket::ReceiveWithTimeout() to use poll() instead of select() on Posix platforms.
Removing the special case for fd > 1023 which can lead to audio glitches.
Code originally written by ossu@.
BUG=722720
Review-Url: https://codereview.chromium.org/2888403005
Cr-Commit-Position: refs/heads/master@{#473856}
diff --git a/base/sync_socket_posix.cc b/base/sync_socket_posix.cc
index da995f4b..a675928 100644
--- a/base/sync_socket_posix.cc
+++ b/base/sync_socket_posix.cc
@@ -7,6 +7,7 @@
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
+#include <poll.h>
#include <stddef.h>
#include <stdio.h>
#include <sys/ioctl.h>
@@ -142,44 +143,42 @@
DCHECK_LE(length, kMaxMessageLength);
DCHECK_NE(handle_, kInvalidHandle);
- // TODO(dalecurtis): There's an undiagnosed issue on OSX where we're seeing
- // large numbers of open files which prevents select() from being used. In
- // this case, the best we can do is Peek() to see if we can Receive() now or
- // return a timeout error (0) if not. See http://crbug.com/314364.
- if (handle_ >= FD_SETSIZE)
- return Peek() < length ? 0 : Receive(buffer, length);
-
// Only timeouts greater than zero and less than one second are allowed.
DCHECK_GT(timeout.InMicroseconds(), 0);
DCHECK_LT(timeout.InMicroseconds(),
- base::TimeDelta::FromSeconds(1).InMicroseconds());
+ TimeDelta::FromSeconds(1).InMicroseconds());
// Track the start time so we can reduce the timeout as data is read.
TimeTicks start_time = TimeTicks::Now();
const TimeTicks finish_time = start_time + timeout;
- fd_set read_fds;
- size_t bytes_read_total;
- for (bytes_read_total = 0;
- bytes_read_total < length && timeout.InMicroseconds() > 0;
- timeout = finish_time - base::TimeTicks::Now()) {
- FD_ZERO(&read_fds);
- FD_SET(handle_, &read_fds);
+ struct pollfd pollfd;
+ pollfd.fd = handle_;
+ pollfd.events = POLLIN;
+ pollfd.revents = 0;
- // Wait for data to become available.
- struct timeval timeout_struct =
- { 0, static_cast<suseconds_t>(timeout.InMicroseconds()) };
- const int select_result =
- select(handle_ + 1, &read_fds, NULL, NULL, &timeout_struct);
+ size_t bytes_read_total = 0;
+ while (bytes_read_total < length) {
+ const TimeDelta this_timeout = finish_time - TimeTicks::Now();
+ const int timeout_ms =
+ static_cast<int>(this_timeout.InMillisecondsRoundedUp());
+ if (timeout_ms <= 0)
+ break;
+ const int poll_result = poll(&pollfd, 1, timeout_ms);
// Handle EINTR manually since we need to update the timeout value.
- if (select_result == -1 && errno == EINTR)
+ if (poll_result == -1 && errno == EINTR)
continue;
- if (select_result <= 0)
+ // Return if other type of error or a timeout.
+ if (poll_result <= 0)
return bytes_read_total;
- // select() only tells us that data is ready for reading, not how much. We
+ // poll() only tells us that data is ready for reading, not how much. We
// must Peek() for the amount ready for reading to avoid blocking.
- DCHECK(FD_ISSET(handle_, &read_fds));
+ // At hang up (POLLHUP), the write end has been closed and there might still
+ // be data to be read.
+ // No special handling is needed for error (POLLERR); we can let any of the
+ // following operations fail and handle it there.
+ DCHECK(pollfd.revents & (POLLIN | POLLHUP | POLLERR)) << pollfd.revents;
const size_t bytes_to_read = std::min(Peek(), length - bytes_read_total);
// There may be zero bytes to read if the socket at the other end closed.