summaryrefslogtreecommitdiff
path: root/fastboot/socket.cpp
diff options
context:
space:
mode:
authorDavid Pursell <dpursell@google.com>2016-02-04 15:21:58 -0800
committerDavid Pursell <dpursell@google.com>2016-02-05 13:22:06 -0800
commitc742a7f17475e22739c09a5b19862b8d1ec6050a (patch)
tree9b416525227029a3366064d0328e9ca8e8539a76 /fastboot/socket.cpp
parentaae1eb2c4f10f3d2c49455eb37c4ae4b38ffa47d (diff)
fastboot: add Socket timeout detection.
UDP fastboot will require re-transmission in the case of datagrams getting lost. This CL adds Socket functionality to easily distinguish between a normal timeout and a socket failure. I also found some Windows docs that indicate sockets may become invalid after a call to recv() times out. This has never occurred in my testing, but to be safe this switches the timeout implementation to use select() instead of SO_RCVTIMEO. Bug: http://b/26154914 Change-Id: Id7b598f8aea5df1a3676d24702b489042d5f9e3a
Diffstat (limited to 'fastboot/socket.cpp')
-rw-r--r--fastboot/socket.cpp50
1 files changed, 36 insertions, 14 deletions
diff --git a/fastboot/socket.cpp b/fastboot/socket.cpp
index d49f47ff2..14ecd937a 100644
--- a/fastboot/socket.cpp
+++ b/fastboot/socket.cpp
@@ -48,18 +48,6 @@ int Socket::Close() {
return ret;
}
-bool Socket::SetReceiveTimeout(int timeout_ms) {
- if (timeout_ms != receive_timeout_ms_) {
- if (socket_set_receive_timeout(sock_, timeout_ms) == 0) {
- receive_timeout_ms_ = timeout_ms;
- return true;
- }
- return false;
- }
-
- return true;
-}
-
ssize_t Socket::ReceiveAll(void* data, size_t length, int timeout_ms) {
size_t total = 0;
@@ -82,6 +70,40 @@ int Socket::GetLocalPort() {
return socket_get_local_port(sock_);
}
+// According to Windows setsockopt() documentation, if a Windows socket times out during send() or
+// recv() the state is indeterminate and should not be used. Our UDP protocol relies on being able
+// to re-send after a timeout, so we must use select() rather than SO_RCVTIMEO.
+// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms740476(v=vs.85).aspx.
+bool Socket::WaitForRecv(int timeout_ms) {
+ receive_timed_out_ = false;
+
+ // In our usage |timeout_ms| <= 0 means block forever, so just return true immediately and let
+ // the subsequent recv() do the blocking.
+ if (timeout_ms <= 0) {
+ return true;
+ }
+
+ // select() doesn't always check this case and will block for |timeout_ms| if we let it.
+ if (sock_ == INVALID_SOCKET) {
+ return false;
+ }
+
+ fd_set read_set;
+ FD_ZERO(&read_set);
+ FD_SET(sock_, &read_set);
+
+ timeval timeout;
+ timeout.tv_sec = timeout_ms / 1000;
+ timeout.tv_usec = (timeout_ms % 1000) * 1000;
+
+ int result = TEMP_FAILURE_RETRY(select(sock_ + 1, &read_set, nullptr, nullptr, &timeout));
+
+ if (result == 0) {
+ receive_timed_out_ = true;
+ }
+ return result == 1;
+}
+
// Implements the Socket interface for UDP.
class UdpSocket : public Socket {
public:
@@ -127,7 +149,7 @@ bool UdpSocket::Send(std::vector<cutils_socket_buffer_t> buffers) {
}
ssize_t UdpSocket::Receive(void* data, size_t length, int timeout_ms) {
- if (!SetReceiveTimeout(timeout_ms)) {
+ if (!WaitForRecv(timeout_ms)) {
return -1;
}
@@ -206,7 +228,7 @@ bool TcpSocket::Send(std::vector<cutils_socket_buffer_t> buffers) {
}
ssize_t TcpSocket::Receive(void* data, size_t length, int timeout_ms) {
- if (!SetReceiveTimeout(timeout_ms)) {
+ if (!WaitForRecv(timeout_ms)) {
return -1;
}