Add Pipe::WriteWithTimeout method
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
This commit is contained in:
parent
d818e38ff9
commit
9f0701f8ca
|
@ -49,9 +49,13 @@ class PipeBase
|
||||||
// Delete named pipe.
|
// Delete named pipe.
|
||||||
virtual Error Delete(llvm::StringRef name) = 0;
|
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;
|
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 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;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -74,7 +74,7 @@ public:
|
||||||
Delete(llvm::StringRef name) override;
|
Delete(llvm::StringRef name) override;
|
||||||
|
|
||||||
Error
|
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
|
Error
|
||||||
ReadWithTimeout(void *buf, size_t size, const std::chrono::microseconds &timeout, size_t &bytes_read) override;
|
ReadWithTimeout(void *buf, size_t size, const std::chrono::microseconds &timeout, size_t &bytes_read) override;
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ class PipeWindows : public PipeBase
|
||||||
|
|
||||||
Error Delete(llvm::StringRef name) override;
|
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;
|
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.
|
// PipeWindows specific methods. These allow access to the underlying OS handle.
|
||||||
|
|
|
@ -11,17 +11,24 @@
|
||||||
|
|
||||||
using namespace lldb_private;
|
using namespace lldb_private;
|
||||||
|
|
||||||
|
const std::chrono::microseconds PipeBase::kInfiniteTimeout(-1);
|
||||||
|
|
||||||
PipeBase::~PipeBase() = default;
|
PipeBase::~PipeBase() = default;
|
||||||
|
|
||||||
Error
|
Error
|
||||||
PipeBase::OpenAsWriter(llvm::StringRef name, bool child_process_inherit)
|
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
|
Error
|
||||||
PipeBase::Read(void *buf, size_t size, size_t &bytes_read)
|
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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,7 +73,8 @@ SelectIO(int handle, bool is_read, const std::function<Error(bool&)> &io_handler
|
||||||
while (!done)
|
while (!done)
|
||||||
{
|
{
|
||||||
struct timeval tv = {0, 0};
|
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());
|
const auto remaining_dur = duration_cast<microseconds>(finish_time - Now());
|
||||||
if (remaining_dur.count() <= 0)
|
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();
|
tv.tv_usec = dur_usecs.count();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
tv.tv_sec = 1;
|
tv_p = nullptr;
|
||||||
|
|
||||||
FD_ZERO(&fds);
|
FD_ZERO(&fds);
|
||||||
FD_SET(handle, &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,
|
const auto retval = ::select(handle + 1,
|
||||||
(is_read) ? &fds : nullptr,
|
(is_read) ? &fds : nullptr,
|
||||||
(is_read) ? nullptr : &fds,
|
(is_read) ? nullptr : &fds,
|
||||||
nullptr, &tv);
|
nullptr, tv_p);
|
||||||
if (retval == -1)
|
if (retval == -1)
|
||||||
{
|
{
|
||||||
if (errno == EINTR)
|
if (errno == EINTR)
|
||||||
|
@ -270,7 +271,7 @@ PipePosix::OpenAsWriterWithTimeout(llvm::StringRef name, bool child_process_inhe
|
||||||
|
|
||||||
while (!CanWrite())
|
while (!CanWrite())
|
||||||
{
|
{
|
||||||
if (timeout != microseconds::zero())
|
if (timeout != kInfiniteTimeout)
|
||||||
{
|
{
|
||||||
const auto dur = duration_cast<microseconds>(finish_time - Now()).count();
|
const auto dur = duration_cast<microseconds>(finish_time - Now()).count();
|
||||||
if (dur <= 0)
|
if (dur <= 0)
|
||||||
|
@ -401,7 +402,10 @@ PipePosix::ReadWithTimeout(void *buf, size_t size, const std::chrono::microsecon
|
||||||
}
|
}
|
||||||
|
|
||||||
Error
|
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;
|
bytes_written = 0;
|
||||||
if (!CanWrite())
|
if (!CanWrite())
|
||||||
|
@ -427,5 +431,5 @@ PipePosix::Write(const void *buf, size_t size, size_t &bytes_written)
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
},
|
},
|
||||||
std::chrono::microseconds::zero());
|
timeout);
|
||||||
}
|
}
|
||||||
|
|
|
@ -289,7 +289,7 @@ PipeWindows::ReadWithTimeout(void *buf, size_t size, const std::chrono::microsec
|
||||||
if (!result && GetLastError() != ERROR_IO_PENDING)
|
if (!result && GetLastError() != ERROR_IO_PENDING)
|
||||||
return Error(::GetLastError(), eErrorTypeWin32);
|
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);
|
DWORD wait_result = ::WaitForSingleObject(m_read_overlapped.hEvent, timeout);
|
||||||
if (wait_result != WAIT_OBJECT_0)
|
if (wait_result != WAIT_OBJECT_0)
|
||||||
{
|
{
|
||||||
|
@ -319,7 +319,10 @@ PipeWindows::ReadWithTimeout(void *buf, size_t size, const std::chrono::microsec
|
||||||
}
|
}
|
||||||
|
|
||||||
Error
|
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())
|
if (!CanWrite())
|
||||||
return Error(ERROR_INVALID_HANDLE, eErrorTypeWin32);
|
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)
|
if (!write_result && GetLastError() != ERROR_IO_PENDING)
|
||||||
return Error(::GetLastError(), eErrorTypeWin32);
|
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)
|
if (!result)
|
||||||
return Error(::GetLastError(), eErrorTypeWin32);
|
return Error(::GetLastError(), eErrorTypeWin32);
|
||||||
|
|
||||||
|
bytes_written = sys_bytes_written;
|
||||||
return Error();
|
return Error();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue