libchromeos: Add ability to pass timeout to Stream::WaitForDataBlocking

To control the timeout when performing blocking I/O, added a |timeout|
parameter to Stream::WaitForDataBlocking.

BUG=brillo:1162
TEST=`FEATURES=test emerge-link libchromeos`

Change-Id: Icee2f7b0a8a93daf15b05bf5fbee2a4ec9410a17
Reviewed-on: https://chromium-review.googlesource.com/275027
Tested-by: Alex Vakulenko <avakulenko@chromium.org>
Reviewed-by: Vitaly Buka <vitalybuka@chromium.org>
Commit-Queue: Vitaly Buka <vitalybuka@chromium.org>
diff --git a/chromeos/streams/fake_stream.cc b/chromeos/streams/fake_stream.cc
index 6e806c7..c786bca 100644
--- a/chromeos/streams/fake_stream.cc
+++ b/chromeos/streams/fake_stream.cc
@@ -378,6 +378,7 @@
 }
 
 bool FakeStream::WaitForDataBlocking(AccessMode in_mode,
+                                     base::TimeDelta timeout,
                                      AccessMode* out_mode,
                                      ErrorPtr* error) {
   const base::TimeDelta zero_delay;
@@ -391,6 +392,9 @@
   GetMinDelayAndMode(clock_->Now(), read_requested, delay_input_until_,
                      write_requested, delay_output_until_, out_mode, &delay);
 
+  if (timeout < delay)
+    return stream_utils::ErrorOperationTimeout(FROM_HERE, error);
+
   LOG(INFO) << "TEST: Would have blocked for " << delay.InMilliseconds()
             << " ms.";
 
diff --git a/chromeos/streams/fake_stream.h b/chromeos/streams/fake_stream.h
index 0990e28..690366e 100644
--- a/chromeos/streams/fake_stream.h
+++ b/chromeos/streams/fake_stream.h
@@ -112,6 +112,7 @@
                    const base::Callback<void(AccessMode)>& callback,
                    ErrorPtr* error) override;
   bool WaitForDataBlocking(AccessMode in_mode,
+                           base::TimeDelta timeout,
                            AccessMode* out_mode,
                            ErrorPtr* error) override;
 
diff --git a/chromeos/streams/file_stream.cc b/chromeos/streams/file_stream.cc
index c1a4e49..d9ad3e6 100644
--- a/chromeos/streams/file_stream.cc
+++ b/chromeos/streams/file_stream.cc
@@ -104,6 +104,7 @@
   }
 
   int WaitForDataBlocking(Stream::AccessMode in_mode,
+                          base::TimeDelta timeout,
                           Stream::AccessMode* out_mode) override {
     fd_set read_fds;
     fd_set write_fds;
@@ -120,9 +121,14 @@
       FD_SET(fd_, &write_fds);
 
     FD_SET(fd_, &error_fds);
+    timeval timeout_val = {};
+    if (!timeout.is_max()) {
+      const timespec ts = timeout.ToTimeSpec();
+      TIMESPEC_TO_TIMEVAL(&timeout_val, &ts);
+    }
     int res = HANDLE_EINTR(select(fd_ + 1, &read_fds, &write_fds, &error_fds,
-                                  nullptr));
-    if (res >= 0 && out_mode) {
+                                  timeout.is_max() ? nullptr : &timeout_val));
+    if (res > 0 && out_mode) {
       *out_mode = stream_utils::MakeAccessMode(FD_ISSET(fd_, &read_fds),
                                                FD_ISSET(fd_, &write_fds));
     }
@@ -492,15 +498,20 @@
 }
 
 bool FileStream::WaitForDataBlocking(AccessMode in_mode,
+                                     base::TimeDelta timeout,
                                      AccessMode* out_mode,
                                      ErrorPtr* error) {
   if (!IsOpen())
     return stream_utils::ErrorStreamClosed(FROM_HERE, error);
 
-  if (fd_interface_->WaitForDataBlocking(in_mode, out_mode) < 0) {
+  int ret = fd_interface_->WaitForDataBlocking(in_mode, timeout, out_mode);
+  if (ret < 0) {
     errors::system::AddSystemError(error, FROM_HERE, errno);
     return false;
   }
+  if (ret == 0)
+    return stream_utils::ErrorOperationTimeout(FROM_HERE, error);
+
   return true;
 }
 
diff --git a/chromeos/streams/file_stream.h b/chromeos/streams/file_stream.h
index 8113a59..b793bd4 100644
--- a/chromeos/streams/file_stream.h
+++ b/chromeos/streams/file_stream.h
@@ -48,6 +48,7 @@
     virtual void WaitForData(AccessMode mode,
                              const DataCallback& data_callback) = 0;
     virtual int WaitForDataBlocking(AccessMode in_mode,
+                                    base::TimeDelta timeout,
                                     AccessMode* out_mode) = 0;
     virtual void CancelPendingAsyncOperations() = 0;
   };
@@ -137,6 +138,7 @@
   // Runs select() on the file descriptor to wait until we can do non-blocking
   // I/O on it.
   bool WaitForDataBlocking(AccessMode in_mode,
+                           base::TimeDelta timeout,
                            AccessMode* out_mode,
                            ErrorPtr* error) override;
 
diff --git a/chromeos/streams/file_stream_unittest.cc b/chromeos/streams/file_stream_unittest.cc
index a6fda48..0ca8acb 100644
--- a/chromeos/streams/file_stream_unittest.cc
+++ b/chromeos/streams/file_stream_unittest.cc
@@ -122,8 +122,8 @@
   MOCK_METHOD0(Flush, int());
   MOCK_METHOD0(Close, int());
   MOCK_METHOD2(WaitForData, void(Stream::AccessMode, const DataCallback&));
-  MOCK_METHOD2(WaitForDataBlocking,
-               int(Stream::AccessMode, Stream::AccessMode*));
+  MOCK_METHOD3(WaitForDataBlocking,
+               int(Stream::AccessMode, base::TimeDelta, Stream::AccessMode*));
   MOCK_METHOD0(CancelPendingAsyncOperations, void());
 };
 
@@ -430,8 +430,8 @@
     InSequence seq;
     EXPECT_CALL(fd_mock(), Read(test_read_buffer_, 80))
         .WillOnce(SetErrnoAndReturn(EAGAIN, -1));
-    EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::READ, _))
-        .WillOnce(Return(0));
+    EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::READ, _, _))
+        .WillOnce(Return(1));
     EXPECT_CALL(fd_mock(), Read(test_read_buffer_, 80)).WillOnce(Return(45));
   }
   EXPECT_TRUE(stream_->ReadBlocking(test_read_buffer_, 80, &size, nullptr));
@@ -447,7 +447,7 @@
     InSequence seq;
     EXPECT_CALL(fd_mock(), Read(test_read_buffer_, 80))
         .WillOnce(SetErrnoAndReturn(EAGAIN, -1));
-    EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::READ, _))
+    EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::READ, _, _))
         .WillOnce(SetErrnoAndReturn(EBADF, -1));
   }
   chromeos::ErrorPtr error;
@@ -463,18 +463,18 @@
     EXPECT_CALL(fd_mock(), Read(test_read_buffer_, 100)).WillOnce(Return(20));
     EXPECT_CALL(fd_mock(), Read(test_read_buffer_ + 20, 80))
         .WillOnce(SetErrnoAndReturn(EAGAIN, -1));
-    EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::READ, _))
-        .WillOnce(Return(0));
+    EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::READ, _, _))
+        .WillOnce(Return(1));
     EXPECT_CALL(fd_mock(), Read(test_read_buffer_ + 20, 80))
         .WillOnce(Return(45));
     EXPECT_CALL(fd_mock(), Read(test_read_buffer_ + 65, 35))
         .WillOnce(SetErrnoAndReturn(EAGAIN, -1));
-    EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::READ, _))
-        .WillOnce(Return(0));
+    EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::READ, _, _))
+        .WillOnce(Return(1));
     EXPECT_CALL(fd_mock(), Read(test_read_buffer_ + 65, 35))
         .WillOnce(SetErrnoAndReturn(EAGAIN, -1));
-    EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::READ, _))
-        .WillOnce(Return(0));
+    EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::READ, _, _))
+        .WillOnce(Return(1));
     EXPECT_CALL(fd_mock(), Read(test_read_buffer_ + 65, 35))
         .WillOnce(Return(35));
   }
@@ -561,8 +561,8 @@
     InSequence seq;
     EXPECT_CALL(fd_mock(), Write(test_write_buffer_, 80))
         .WillOnce(SetErrnoAndReturn(EAGAIN, -1));
-    EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::WRITE, _))
-        .WillOnce(Return(0));
+    EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::WRITE, _, _))
+        .WillOnce(Return(1));
     EXPECT_CALL(fd_mock(), Write(test_write_buffer_, 80)).WillOnce(Return(45));
   }
   EXPECT_TRUE(stream_->WriteBlocking(test_write_buffer_, 80, &size, nullptr));
@@ -571,8 +571,8 @@
   {
     InSequence seq;
     EXPECT_CALL(fd_mock(), Write(test_write_buffer_, 50)).WillOnce(Return(0));
-    EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::WRITE, _))
-        .WillOnce(Return(0));
+    EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::WRITE, _, _))
+        .WillOnce(Return(1));
     EXPECT_CALL(fd_mock(), Write(test_write_buffer_, 50)).WillOnce(Return(1));
   }
   EXPECT_TRUE(stream_->WriteBlocking(test_write_buffer_, 50, &size, nullptr));
@@ -584,7 +584,7 @@
     InSequence seq;
     EXPECT_CALL(fd_mock(), Write(test_write_buffer_, 80))
         .WillOnce(SetErrnoAndReturn(EAGAIN, -1));
-    EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::WRITE, _))
+    EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::WRITE, _, _))
         .WillOnce(SetErrnoAndReturn(EBADF, -1));
   }
   chromeos::ErrorPtr error;
@@ -600,18 +600,18 @@
     EXPECT_CALL(fd_mock(), Write(test_write_buffer_, 100)).WillOnce(Return(20));
     EXPECT_CALL(fd_mock(), Write(test_write_buffer_ + 20, 80))
         .WillOnce(SetErrnoAndReturn(EAGAIN, -1));
-    EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::WRITE, _))
-        .WillOnce(Return(0));
+    EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::WRITE, _, _))
+        .WillOnce(Return(1));
     EXPECT_CALL(fd_mock(), Write(test_write_buffer_ + 20, 80))
         .WillOnce(Return(45));
     EXPECT_CALL(fd_mock(), Write(test_write_buffer_ + 65, 35))
         .WillOnce(SetErrnoAndReturn(EAGAIN, -1));
-    EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::WRITE, _))
-        .WillOnce(Return(0));
+    EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::WRITE, _, _))
+        .WillOnce(Return(1));
     EXPECT_CALL(fd_mock(), Write(test_write_buffer_ + 65, 35))
         .WillOnce(SetErrnoAndReturn(EAGAIN, -1));
-    EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::WRITE, _))
-        .WillOnce(Return(0));
+    EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::WRITE, _, _))
+        .WillOnce(Return(1));
     EXPECT_CALL(fd_mock(), Write(test_write_buffer_ + 65, 35))
         .WillOnce(Return(35));
   }
@@ -623,7 +623,7 @@
     InSequence seq;
     EXPECT_CALL(fd_mock(), Write(test_write_buffer_, 80))
         .WillOnce(SetErrnoAndReturn(EAGAIN, -1));
-    EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::WRITE, _))
+    EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::WRITE, _, _))
         .WillOnce(SetErrnoAndReturn(EBADF, -1));
   }
   chromeos::ErrorPtr error;
@@ -632,6 +632,16 @@
   EXPECT_EQ("EBADF", error->GetCode());
 }
 
+TEST_F(FileStreamTest, WaitForDataBlocking_Timeout) {
+  EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::WRITE, _, _))
+      .WillOnce(Return(0));
+  chromeos::ErrorPtr error;
+  EXPECT_FALSE(stream_->WaitForDataBlocking(Stream::AccessMode::WRITE, {},
+                                            nullptr, &error));
+  EXPECT_EQ(errors::stream::kDomain, error->GetDomain());
+  EXPECT_EQ(errors::stream::kTimeout, error->GetCode());
+}
+
 TEST_F(FileStreamTest, FlushBlocking) {
   EXPECT_TRUE(stream_->FlushBlocking(nullptr));
 }
diff --git a/chromeos/streams/input_stream_set.cc b/chromeos/streams/input_stream_set.cc
index a2ba2ae..2a6e719 100644
--- a/chromeos/streams/input_stream_set.cc
+++ b/chromeos/streams/input_stream_set.cc
@@ -178,6 +178,7 @@
 }
 
 bool InputStreamSet::WaitForDataBlocking(AccessMode in_mode,
+                                         base::TimeDelta timeout,
                                          AccessMode* out_mode,
                                          ErrorPtr* error) {
   if (!IsOpen())
@@ -188,7 +189,7 @@
 
   if (!source_streams_.empty()) {
     Stream* stream = source_streams_.front();
-    return stream->WaitForDataBlocking(in_mode, out_mode, error);
+    return stream->WaitForDataBlocking(in_mode, timeout, out_mode, error);
   }
 
   if (out_mode)
diff --git a/chromeos/streams/input_stream_set.h b/chromeos/streams/input_stream_set.h
index ec29ccb..a37ab08 100644
--- a/chromeos/streams/input_stream_set.h
+++ b/chromeos/streams/input_stream_set.h
@@ -100,6 +100,7 @@
                    ErrorPtr* error) override;
 
   bool WaitForDataBlocking(AccessMode in_mode,
+                           base::TimeDelta timeout,
                            AccessMode* out_mode,
                            ErrorPtr* error) override;
 
diff --git a/chromeos/streams/input_stream_set_unittest.cc b/chromeos/streams/input_stream_set_unittest.cc
index de6e717..82ac60c 100644
--- a/chromeos/streams/input_stream_set_unittest.cc
+++ b/chromeos/streams/input_stream_set_unittest.cc
@@ -150,7 +150,7 @@
       .WillOnce(DoAll(SetArgPointee<2>(0),
                       SetArgPointee<3>(false),
                       Return(true)));
-  EXPECT_CALL(*itf2_, WaitForDataBlocking(Stream::AccessMode::READ, _, _))
+  EXPECT_CALL(*itf2_, WaitForDataBlocking(Stream::AccessMode::READ, _, _, _))
       .WillOnce(Return(true));
   EXPECT_CALL(*itf2_, ReadNonBlocking(IntToPtr(1000), 100, _, _, _))
       .WillOnce(DoAll(SetArgPointee<2>(100),
diff --git a/chromeos/streams/memory_stream.cc b/chromeos/streams/memory_stream.cc
index 3a9fdff..eb2f829 100644
--- a/chromeos/streams/memory_stream.cc
+++ b/chromeos/streams/memory_stream.cc
@@ -195,6 +195,7 @@
 }
 
 bool MemoryStream::WaitForDataBlocking(AccessMode in_mode,
+                                       base::TimeDelta timeout,
                                        AccessMode* out_mode,
                                        ErrorPtr* error) {
   if (out_mode)
diff --git a/chromeos/streams/memory_stream.h b/chromeos/streams/memory_stream.h
index f36ee54..ac03440 100644
--- a/chromeos/streams/memory_stream.h
+++ b/chromeos/streams/memory_stream.h
@@ -183,6 +183,7 @@
                    ErrorPtr* error) override;
 
   bool WaitForDataBlocking(AccessMode in_mode,
+                           base::TimeDelta timeout,
                            AccessMode* out_mode,
                            ErrorPtr* error) override;
 
diff --git a/chromeos/streams/mock_stream.h b/chromeos/streams/mock_stream.h
index 4fe3562..a1303c2 100644
--- a/chromeos/streams/mock_stream.h
+++ b/chromeos/streams/mock_stream.h
@@ -63,7 +63,8 @@
   MOCK_METHOD3(WaitForData, bool(AccessMode,
                                  const base::Callback<void(AccessMode)>&,
                                  ErrorPtr*));
-  MOCK_METHOD3(WaitForDataBlocking, bool(AccessMode, AccessMode*, ErrorPtr*));
+  MOCK_METHOD4(WaitForDataBlocking,
+               bool(AccessMode, base::TimeDelta, AccessMode*, ErrorPtr*));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockStream);
diff --git a/chromeos/streams/stream.cc b/chromeos/streams/stream.cc
index 759360b..0e10f24 100644
--- a/chromeos/streams/stream.cc
+++ b/chromeos/streams/stream.cc
@@ -68,8 +68,10 @@
     if (*size_read > 0 || eos)
       break;
 
-    if (!WaitForDataBlocking(AccessMode::READ, nullptr, error))
+    if (!WaitForDataBlocking(AccessMode::READ, base::TimeDelta::Max(), nullptr,
+                             error)) {
       return false;
+    }
   }
   return true;
 }
@@ -133,8 +135,10 @@
     if (*size_written > 0 || size_to_write == 0)
       break;
 
-    if (!WaitForDataBlocking(AccessMode::WRITE, nullptr, error))
+    if (!WaitForDataBlocking(AccessMode::WRITE, base::TimeDelta::Max(), nullptr,
+                             error)) {
       return false;
+    }
   }
   return true;
 }
diff --git a/chromeos/streams/stream.h b/chromeos/streams/stream.h
index 4886fe6..13530b7 100644
--- a/chromeos/streams/stream.h
+++ b/chromeos/streams/stream.h
@@ -11,6 +11,7 @@
 #include <base/callback.h>
 #include <base/macros.h>
 #include <base/memory/weak_ptr.h>
+#include <base/time/time.h>
 #include <chromeos/errors/error.h>
 #include <chromeos/chromeos_export.h>
 
@@ -379,7 +380,10 @@
   // performed. For example, watching a stream for READ_WRITE while only
   // READ can be performed, |out_mode| would contain READ even though |in_mode|
   // was set to READ_WRITE.
+  // |timeout| is the maximum amount of time to wait. Set it to TimeDelta::Max()
+  // to wait indefinitely.
   virtual bool WaitForDataBlocking(AccessMode in_mode,
+                                   base::TimeDelta timeout,
                                    AccessMode* out_mode,
                                    ErrorPtr* error) = 0;
 
diff --git a/chromeos/streams/stream_errors.cc b/chromeos/streams/stream_errors.cc
index 32def0f..35911c5 100644
--- a/chromeos/streams/stream_errors.cc
+++ b/chromeos/streams/stream_errors.cc
@@ -14,6 +14,7 @@
 const char kOperationNotSupported[] = "operation_not_supported";
 const char kPartialData[] = "partial_data";
 const char kInvalidParameter[] = "invalid_parameter";
+const char kTimeout[] = "time_out";
 
 }  // namespace stream
 }  // namespace errors
diff --git a/chromeos/streams/stream_errors.h b/chromeos/streams/stream_errors.h
index 1c9c9e6..61f8a6f 100644
--- a/chromeos/streams/stream_errors.h
+++ b/chromeos/streams/stream_errors.h
@@ -18,6 +18,7 @@
 CHROMEOS_EXPORT extern const char kOperationNotSupported[];
 CHROMEOS_EXPORT extern const char kPartialData[];
 CHROMEOS_EXPORT extern const char kInvalidParameter[];
+CHROMEOS_EXPORT extern const char kTimeout[];
 
 }  // namespace stream
 }  // namespace errors
diff --git a/chromeos/streams/stream_unittest.cc b/chromeos/streams/stream_unittest.cc
index ca1242a..9630d97 100644
--- a/chromeos/streams/stream_unittest.cc
+++ b/chromeos/streams/stream_unittest.cc
@@ -63,7 +63,8 @@
   MOCK_METHOD3(WaitForData, bool(AccessMode,
                                  const base::Callback<void(AccessMode)>&,
                                  ErrorPtr*));
-  MOCK_METHOD3(WaitForDataBlocking, bool(AccessMode, AccessMode*, ErrorPtr*));
+  MOCK_METHOD4(WaitForDataBlocking,
+               bool(AccessMode, base::TimeDelta, AccessMode*, ErrorPtr*));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockStreamImpl);
@@ -232,13 +233,13 @@
         .WillOnce(DoAll(SetArgPointee<2>(0),
                         SetArgPointee<3>(false),
                         Return(true)));
-    EXPECT_CALL(stream_mock, WaitForDataBlocking(AccessMode::READ, _, _))
+    EXPECT_CALL(stream_mock, WaitForDataBlocking(AccessMode::READ, _, _, _))
         .WillOnce(Return(true));
     EXPECT_CALL(stream_mock, ReadNonBlocking(buf, 1024, _, _, _))
         .WillOnce(DoAll(SetArgPointee<2>(0),
                         SetArgPointee<3>(false),
                         Return(true)));
-    EXPECT_CALL(stream_mock, WaitForDataBlocking(AccessMode::READ, _, _))
+    EXPECT_CALL(stream_mock, WaitForDataBlocking(AccessMode::READ, _, _, _))
         .WillOnce(Return(true));
     EXPECT_CALL(stream_mock, ReadNonBlocking(buf, 1024, _, _, _))
         .WillOnce(DoAll(SetArgPointee<2>(124),
@@ -254,7 +255,7 @@
         .WillOnce(DoAll(SetArgPointee<2>(0),
                         SetArgPointee<3>(false),
                         Return(true)));
-    EXPECT_CALL(stream_mock, WaitForDataBlocking(AccessMode::READ, _, _))
+    EXPECT_CALL(stream_mock, WaitForDataBlocking(AccessMode::READ, _, _, _))
         .WillOnce(Return(false));
   }
   EXPECT_FALSE(stream_mock.ReadBlocking(buf, sizeof(buf), &read, nullptr));
@@ -366,11 +367,11 @@
     InSequence seq;
     EXPECT_CALL(stream_mock, WriteNonBlocking(buf, 1024, _, _))
           .WillOnce(DoAll(SetArgPointee<2>(0), Return(true)));
-    EXPECT_CALL(stream_mock, WaitForDataBlocking(AccessMode::WRITE, _, _))
+    EXPECT_CALL(stream_mock, WaitForDataBlocking(AccessMode::WRITE, _, _, _))
         .WillOnce(Return(true));
     EXPECT_CALL(stream_mock, WriteNonBlocking(buf, 1024, _, _))
           .WillOnce(DoAll(SetArgPointee<2>(0), Return(true)));
-    EXPECT_CALL(stream_mock, WaitForDataBlocking(AccessMode::WRITE, _, _))
+    EXPECT_CALL(stream_mock, WaitForDataBlocking(AccessMode::WRITE, _, _, _))
         .WillOnce(Return(true));
     EXPECT_CALL(stream_mock, WriteNonBlocking(buf, 1024, _, _))
           .WillOnce(DoAll(SetArgPointee<2>(124), Return(true)));
@@ -382,7 +383,7 @@
     InSequence seq;
     EXPECT_CALL(stream_mock, WriteNonBlocking(buf, 1024, _, _))
           .WillOnce(DoAll(SetArgPointee<2>(0), Return(true)));
-    EXPECT_CALL(stream_mock, WaitForDataBlocking(AccessMode::WRITE, _, _))
+    EXPECT_CALL(stream_mock, WaitForDataBlocking(AccessMode::WRITE, _, _, _))
         .WillOnce(Return(false));
   }
   EXPECT_FALSE(stream_mock.WriteBlocking(buf, sizeof(buf), &written, nullptr));
diff --git a/chromeos/streams/stream_utils.cc b/chromeos/streams/stream_utils.cc
index 7e83737..1b2004c 100644
--- a/chromeos/streams/stream_utils.cc
+++ b/chromeos/streams/stream_utils.cc
@@ -41,6 +41,17 @@
   return false;
 }
 
+bool ErrorOperationTimeout(const tracked_objects::Location& location,
+                           ErrorPtr* error) {
+  Error::AddTo(error,
+               location,
+               errors::stream::kDomain,
+               errors::stream::kTimeout,
+               "Operation timed out");
+  return false;
+}
+
+
 bool CheckInt64Overflow(const tracked_objects::Location& location,
                         uint64_t position,
                         int64_t offset,
diff --git a/chromeos/streams/stream_utils.h b/chromeos/streams/stream_utils.h
index 33ae0bf..09ff38c 100644
--- a/chromeos/streams/stream_utils.h
+++ b/chromeos/streams/stream_utils.h
@@ -24,6 +24,10 @@
 CHROMEOS_EXPORT bool ErrorReadPastEndOfStream(
     const tracked_objects::Location& location, ErrorPtr* error);
 
+// Generates "Operation time out" error and returns false.
+CHROMEOS_EXPORT bool ErrorOperationTimeout(
+    const tracked_objects::Location& location, ErrorPtr* error);
+
 // Checks if |position| + |offset| fit within the constraint of positive
 // signed int64_t type. We use uint64_t for absolute stream pointer positions,
 // however many implementations, including file-descriptor-based I/O do not
diff --git a/chromeos/streams/tls_stream.cc b/chromeos/streams/tls_stream.cc
index 107ea85..345a340 100644
--- a/chromeos/streams/tls_stream.cc
+++ b/chromeos/streams/tls_stream.cc
@@ -92,6 +92,7 @@
                    const base::Callback<void(AccessMode)>& callback,
                    ErrorPtr* error);
   bool WaitForDataBlocking(AccessMode in_mode,
+                           base::TimeDelta timeout,
                            AccessMode* out_mode,
                            ErrorPtr* error);
   void CancelPendingAsyncOperations();
@@ -203,6 +204,8 @@
 }
 
 bool TlsStream::TlsStreamImpl::Close(ErrorPtr* error) {
+  // 2 seconds should be plenty here.
+  const base::TimeDelta kTimeout = base::TimeDelta::FromSeconds(2);
   int retry_count = 0;
   while (retry_count < 2) {
     int ret = SSL_shutdown(ssl_.get());
@@ -216,11 +219,15 @@
 
     int err = SSL_get_error(ssl_.get(), ret);
     if (err == SSL_ERROR_WANT_READ) {
-      if (!socket_->WaitForDataBlocking(AccessMode::READ, nullptr, error))
+      if (!socket_->WaitForDataBlocking(AccessMode::READ, kTimeout, nullptr,
+                                        error)) {
         break;
+      }
     } else if (err == SSL_ERROR_WANT_WRITE) {
-      if (!socket_->WaitForDataBlocking(AccessMode::WRITE, nullptr, error))
+      if (!socket_->WaitForDataBlocking(AccessMode::WRITE, kTimeout, nullptr,
+                                        error)) {
         break;
+      }
     } else {
       ReportError(error, FROM_HERE, "Failed to shut down TLS socket");
       break;
@@ -248,6 +255,7 @@
 }
 
 bool TlsStream::TlsStreamImpl::WaitForDataBlocking(AccessMode in_mode,
+                                                   base::TimeDelta timeout,
                                                    AccessMode* out_mode,
                                                    ErrorPtr* error) {
   bool is_read = stream_utils::IsReadAccessMode(in_mode);
@@ -261,7 +269,7 @@
     return true;
   }
   in_mode = stream_utils::MakeAccessMode(is_read, is_write);
-  return socket_->WaitForDataBlocking(in_mode, out_mode, error);
+  return socket_->WaitForDataBlocking(in_mode, timeout, out_mode, error);
 }
 
 void TlsStream::TlsStreamImpl::CancelPendingAsyncOperations() {
@@ -517,11 +525,12 @@
 }
 
 bool TlsStream::WaitForDataBlocking(AccessMode in_mode,
+                                    base::TimeDelta timeout,
                                     AccessMode* out_mode,
                                     ErrorPtr* error) {
   if (!impl_)
     return stream_utils::ErrorStreamClosed(FROM_HERE, error);
-  return impl_->WaitForDataBlocking(in_mode, out_mode, error);
+  return impl_->WaitForDataBlocking(in_mode, timeout, out_mode, error);
 }
 
 void TlsStream::CancelPendingAsyncOperations() {
diff --git a/chromeos/streams/tls_stream.h b/chromeos/streams/tls_stream.h
index a888df2..fb5b323 100644
--- a/chromeos/streams/tls_stream.h
+++ b/chromeos/streams/tls_stream.h
@@ -64,6 +64,7 @@
                    const base::Callback<void(AccessMode)>& callback,
                    ErrorPtr* error) override;
   bool WaitForDataBlocking(AccessMode in_mode,
+                           base::TimeDelta timeout,
                            AccessMode* out_mode,
                            ErrorPtr* error) override;
   void CancelPendingAsyncOperations() override;