Index: lldb/trunk/include/lldb/Host/PipeBase.h =================================================================== --- lldb/trunk/include/lldb/Host/PipeBase.h +++ lldb/trunk/include/lldb/Host/PipeBase.h @@ -49,9 +49,13 @@ // 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: lldb/trunk/include/lldb/Host/posix/PipePosix.h =================================================================== --- lldb/trunk/include/lldb/Host/posix/PipePosix.h +++ lldb/trunk/include/lldb/Host/posix/PipePosix.h @@ -74,7 +74,7 @@ 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: lldb/trunk/include/lldb/Host/windows/PipeWindows.h =================================================================== --- lldb/trunk/include/lldb/Host/windows/PipeWindows.h +++ lldb/trunk/include/lldb/Host/windows/PipeWindows.h @@ -49,7 +49,7 @@ 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: lldb/trunk/source/Host/common/PipeBase.cpp =================================================================== --- lldb/trunk/source/Host/common/PipeBase.cpp +++ lldb/trunk/source/Host/common/PipeBase.cpp @@ -11,17 +11,24 @@ 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: lldb/trunk/source/Host/posix/PipePosix.cpp =================================================================== --- lldb/trunk/source/Host/posix/PipePosix.cpp +++ lldb/trunk/source/Host/posix/PipePosix.cpp @@ -73,7 +73,8 @@ 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(finish_time - Now()); if (remaining_dur.count() <= 0) @@ -88,7 +89,7 @@ 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 @@ 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 @@ while (!CanWrite()) { - if (timeout != microseconds::zero()) + if (timeout != kInfiniteTimeout) { const auto dur = duration_cast(finish_time - Now()).count(); if (dur <= 0) @@ -401,7 +402,10 @@ } 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 @@ return error; }, - std::chrono::microseconds::zero()); + timeout); } Index: lldb/trunk/source/Host/windows/PipeWindows.cpp =================================================================== --- lldb/trunk/source/Host/windows/PipeWindows.cpp +++ lldb/trunk/source/Host/windows/PipeWindows.cpp @@ -289,7 +289,7 @@ 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 @@ } 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 @@ 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(); }