Add Pipe::WriteWithTimeout method
authorPavel Labath <labath@google.com>
Tue, 21 Jul 2015 09:23:34 +0000 (09:23 +0000)
committerPavel Labath <labath@google.com>
Tue, 21 Jul 2015 09:23:34 +0000 (09:23 +0000)
Summary:
This commit adds a WriteWithTimeout method to time Pipe class, analogous to the existing
ReadWithTimeout(). It also changes the meaning of passing zero as a timeout value. Previously,
zero was used as an infinite timeout value. Now, the meaning of zero timeout to return the data
avaiable without sleeping (basically, a non-blocking operation). This makes the behaviour of Pipe
consistent with the Communication/Connection classes. For blocking operatios with infinite
timeout, I introduce a special constant for this purpose.

Reviewers: ovyalov, zturner

Subscribers: lldb-commits

Differential Revision: http://reviews.llvm.org/D11358

llvm-svn: 242764

lldb/include/lldb/Host/PipeBase.h
lldb/include/lldb/Host/posix/PipePosix.h
lldb/include/lldb/Host/windows/PipeWindows.h
lldb/source/Host/common/PipeBase.cpp
lldb/source/Host/posix/PipePosix.cpp
lldb/source/Host/windows/PipeWindows.cpp

index 8680a25..8b11d70 100644 (file)
@@ -49,9 +49,13 @@ class PipeBase
     // Delete named pipe.
     virtual Error Delete(llvm::StringRef name) = 0;
 
-    virtual Error Write(const void *buf, size_t size, size_t &bytes_written) = 0;
+    virtual Error WriteWithTimeout(const void *buf, size_t size, const std::chrono::microseconds &timeout, size_t &bytes_written) = 0;
     virtual Error ReadWithTimeout(void *buf, size_t size, const std::chrono::microseconds &timeout, size_t &bytes_read) = 0;
+
     Error Read(void *buf, size_t size, size_t &bytes_read);
+    Error Write(const void *buf, size_t size, size_t &bytes_written);
+
+    static const std::chrono::microseconds kInfiniteTimeout;
 };
 }
 
index 710b77d..889da93 100644 (file)
@@ -74,7 +74,7 @@ public:
     Delete(llvm::StringRef name) override;
 
     Error
-    Write(const void *buf, size_t size, size_t &bytes_written) override;
+    WriteWithTimeout(const void *buf, size_t size, const std::chrono::microseconds &timeout, size_t &bytes_written) override;
     Error
     ReadWithTimeout(void *buf, size_t size, const std::chrono::microseconds &timeout, size_t &bytes_read) override;
 
index 7170c7c..be8bf02 100644 (file)
@@ -49,7 +49,7 @@ class PipeWindows : public PipeBase
 
     Error Delete(llvm::StringRef name) override;
 
-    Error Write(const void *buf, size_t size, size_t &bytes_written) override;
+    Error WriteWithTimeout(const void *buf, size_t size, const std::chrono::microseconds &timeout, size_t &bytes_written) override;
     Error ReadWithTimeout(void *buf, size_t size, const std::chrono::microseconds &timeout, size_t &bytes_read) override;
 
     // PipeWindows specific methods.  These allow access to the underlying OS handle.
index a9d6e6f..4b607a2 100644 (file)
 
 using namespace lldb_private;
 
+const std::chrono::microseconds PipeBase::kInfiniteTimeout(-1);
 
 PipeBase::~PipeBase() = default;
 
 Error
 PipeBase::OpenAsWriter(llvm::StringRef name, bool child_process_inherit)
 {
-    return OpenAsWriterWithTimeout(name, child_process_inherit, std::chrono::microseconds::zero());
+    return OpenAsWriterWithTimeout(name, child_process_inherit, kInfiniteTimeout);
 }
 
 Error
 PipeBase::Read(void *buf, size_t size, size_t &bytes_read)
 {
-    return ReadWithTimeout(buf, size, std::chrono::microseconds::zero(), bytes_read);
+    return ReadWithTimeout(buf, size, kInfiniteTimeout, bytes_read);
+}
+
+Error
+PipeBase::Write(const void *buf, size_t size, size_t &bytes_written)
+{
+    return WriteWithTimeout(buf, size, kInfiniteTimeout, bytes_written);
 }
index 0ed319f..f1f4e66 100644 (file)
@@ -73,7 +73,8 @@ SelectIO(int handle, bool is_read, const std::function<Error(bool&)> &io_handler
     while (!done)
     {
         struct timeval tv = {0, 0};
-        if (timeout != microseconds::zero())
+        struct timeval *tv_p = &tv;
+        if (timeout != PipeBase::kInfiniteTimeout)
         {
             const auto remaining_dur = duration_cast<microseconds>(finish_time - Now());
             if (remaining_dur.count() <= 0)
@@ -88,7 +89,7 @@ SelectIO(int handle, bool is_read, const std::function<Error(bool&)> &io_handler
             tv.tv_usec = dur_usecs.count();
         }
         else
-            tv.tv_sec = 1;
+            tv_p = nullptr;
 
         FD_ZERO(&fds);
         FD_SET(handle, &fds);
@@ -96,7 +97,7 @@ SelectIO(int handle, bool is_read, const std::function<Error(bool&)> &io_handler
         const auto retval = ::select(handle + 1,
                                      (is_read) ? &fds : nullptr,
                                      (is_read) ? nullptr : &fds,
-                                     nullptr, &tv);
+                                     nullptr, tv_p);
         if (retval == -1)
         {
             if (errno == EINTR)
@@ -270,7 +271,7 @@ PipePosix::OpenAsWriterWithTimeout(llvm::StringRef name, bool child_process_inhe
 
     while (!CanWrite())
     {
-        if (timeout != microseconds::zero())
+        if (timeout != kInfiniteTimeout)
         {
             const auto dur = duration_cast<microseconds>(finish_time - Now()).count();
             if (dur <= 0)
@@ -401,7 +402,10 @@ PipePosix::ReadWithTimeout(void *buf, size_t size, const std::chrono::microsecon
 }
 
 Error
-PipePosix::Write(const void *buf, size_t size, size_t &bytes_written)
+PipePosix::WriteWithTimeout(const void *buf,
+                            size_t size,
+                            const std::chrono::microseconds &timeout,
+                            size_t &bytes_written)
 {
     bytes_written = 0;
     if (!CanWrite())
@@ -427,5 +431,5 @@ PipePosix::Write(const void *buf, size_t size, size_t &bytes_written)
 
                         return error;
                     },
-                    std::chrono::microseconds::zero());
+                    timeout);
 }
index d4afd6e..e202997 100644 (file)
@@ -289,7 +289,7 @@ PipeWindows::ReadWithTimeout(void *buf, size_t size, const std::chrono::microsec
     if (!result && GetLastError() != ERROR_IO_PENDING)
         return Error(::GetLastError(), eErrorTypeWin32);
 
-    DWORD timeout = (duration == std::chrono::microseconds::zero()) ? INFINITE : duration.count() * 1000;
+    DWORD timeout = (duration == kInfiniteTimeout) ? INFINITE : duration.count() * 1000;
     DWORD wait_result = ::WaitForSingleObject(m_read_overlapped.hEvent, timeout);
     if (wait_result != WAIT_OBJECT_0)
     {
@@ -319,7 +319,10 @@ PipeWindows::ReadWithTimeout(void *buf, size_t size, const std::chrono::microsec
 }
 
 Error
-PipeWindows::Write(const void *buf, size_t num_bytes, size_t &bytes_written)
+PipeWindows::WriteWithTimeout(const void *buf,
+                              size_t num_bytes,
+                              const std::chrono::microseconds &duration,
+                              size_t &bytes_written)
 {
     if (!CanWrite())
         return Error(ERROR_INVALID_HANDLE, eErrorTypeWin32);
@@ -329,8 +332,32 @@ PipeWindows::Write(const void *buf, size_t num_bytes, size_t &bytes_written)
     if (!write_result && GetLastError() != ERROR_IO_PENDING)
         return Error(::GetLastError(), eErrorTypeWin32);
 
-    BOOL result = GetOverlappedResult(m_write, &m_write_overlapped, &sys_bytes_written, TRUE);
+    DWORD timeout = (duration == kInfiniteTimeout) ? INFINITE : duration.count() * 1000;
+    DWORD wait_result = ::WaitForSingleObject(m_write_overlapped.hEvent, timeout);
+    if (wait_result != WAIT_OBJECT_0)
+    {
+        // The operation probably failed.  However, if it timed out, we need to cancel the I/O.
+        // Between the time we returned from WaitForSingleObject and the time we call CancelIoEx,
+        // the operation may complete.  If that hapens, CancelIoEx will fail and return ERROR_NOT_FOUND.
+        // If that happens, the original operation should be considered to have been successful.
+        bool failed = true;
+        DWORD failure_error = ::GetLastError();
+        if (wait_result == WAIT_TIMEOUT)
+        {
+            BOOL cancel_result = CancelIoEx(m_read, &m_write_overlapped);
+            if (!cancel_result && GetLastError() == ERROR_NOT_FOUND)
+                failed = false;
+        }
+        if (failed)
+            return Error(failure_error, eErrorTypeWin32);
+    }
+
+    // Now we call GetOverlappedResult setting bWait to false, since we've already waited
+    // as long as we're willing to.
+    BOOL result = GetOverlappedResult(m_write, &m_write_overlapped, &sys_bytes_written, FALSE);
     if (!result)
         return Error(::GetLastError(), eErrorTypeWin32);
+
+    bytes_written = sys_bytes_written;
     return Error();
 }