diff options
author | Zachary Turner <zturner@google.com> | 2014-12-17 18:02:19 +0000 |
---|---|---|
committer | Zachary Turner <zturner@google.com> | 2014-12-17 18:02:19 +0000 |
commit | 0b9d3eefdbf2c9bb50e139c5272b413e8227d842 (patch) | |
tree | fa6ac73e658e94d59432d15646ce1416662954f0 /lldb/source/Host/windows/PipeWindows.cpp | |
parent | 06b2c54db9132bc7b6e599c2a3252ea7f2a8b33b (diff) | |
download | bcm5719-llvm-0b9d3eefdbf2c9bb50e139c5272b413e8227d842.tar.gz bcm5719-llvm-0b9d3eefdbf2c9bb50e139c5272b413e8227d842.zip |
Enhance the Pipe interface for better portability.
This patch makes a number of improvements to the Pipe interface.
1) An interface (PipeBase) is provided which exposes pure virtual
methods for any implementation of Pipe to override. While not
strictly necessary, this helps catch errors where the interfaces
are out of sync.
2) All methods return lldb_private::Error instead of returning bool
or void. This allows richer error information to be propagated
up to LLDB.
3) A new ReadWithTimeout() method is exposed in the base class and
implemented on Windows.
4) Support for both named and anonymous pipes is exposed through the
base interface and implemented on Windows. For creating a new
pipe, both named and anonymous pipes are supported, and for
opening an existing pipe, only named pipes are supported.
New methods described in points #3 and #4 are stubbed out on posix,
but fully implemented on Windows. These should be implemented by
someone on the linux / mac / bsd side.
Reviewed by: Greg Clayton, Oleksiy Vyalov
Differential Revision: http://reviews.llvm.org/D6686
llvm-svn: 224442
Diffstat (limited to 'lldb/source/Host/windows/PipeWindows.cpp')
-rw-r--r-- | lldb/source/Host/windows/PipeWindows.cpp | 301 |
1 files changed, 188 insertions, 113 deletions
diff --git a/lldb/source/Host/windows/PipeWindows.cpp b/lldb/source/Host/windows/PipeWindows.cpp index 572be878409..e49bbe026f0 100644 --- a/lldb/source/Host/windows/PipeWindows.cpp +++ b/lldb/source/Host/windows/PipeWindows.cpp @@ -17,6 +17,7 @@ #include <atomic> #include <string> +using namespace lldb; using namespace lldb_private; namespace @@ -24,208 +25,282 @@ namespace std::atomic<uint32_t> g_pipe_serial(0); } -Pipe::Pipe() +PipeWindows::PipeWindows() { m_read = INVALID_HANDLE_VALUE; m_write = INVALID_HANDLE_VALUE; m_read_fd = -1; m_write_fd = -1; - - m_read_overlapped = nullptr; - m_write_overlapped = nullptr; + ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped)); + ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped)); } -Pipe::~Pipe() +PipeWindows::~PipeWindows() { Close(); } -bool -Pipe::Open(bool read_overlapped, bool write_overlapped) +Error +PipeWindows::CreateNew(bool child_process_inherit) { - if (IsValid()) - return true; - + // Even for anonymous pipes, we open a named pipe. This is because you cannot get + // overlapped i/o on Windows without using a named pipe. So we synthesize a unique + // name. uint32_t serial = g_pipe_serial.fetch_add(1); std::string pipe_name; llvm::raw_string_ostream pipe_name_stream(pipe_name); - pipe_name_stream << "\\\\.\\Pipe\\lldb.pipe." << ::GetCurrentProcessId() << "." << serial; + pipe_name_stream << "lldb.pipe." << ::GetCurrentProcessId() << "." << serial; pipe_name_stream.flush(); - DWORD read_mode = 0; - DWORD write_mode = 0; - if (read_overlapped) - read_mode |= FILE_FLAG_OVERLAPPED; - if (write_overlapped) - write_mode |= FILE_FLAG_OVERLAPPED; + return CreateNew(pipe_name.c_str(), child_process_inherit); +} + +Error +PipeWindows::CreateNew(llvm::StringRef name, bool child_process_inherit) +{ + if (name.empty()) + return Error(ERROR_INVALID_PARAMETER, eErrorTypeWin32); + + if (CanRead() || CanWrite()) + return Error(ERROR_ALREADY_EXISTS, eErrorTypeWin32); + + std::string pipe_path = "\\\\.\\Pipe\\"; + pipe_path.append(name); + + // Always open for overlapped i/o. We implement blocking manually in Read and Write. + DWORD read_mode = FILE_FLAG_OVERLAPPED; m_read = - ::CreateNamedPipe(pipe_name.c_str(), PIPE_ACCESS_INBOUND | read_mode, PIPE_TYPE_BYTE | PIPE_WAIT, 1, 1024, 1024, 120 * 1000, NULL); + ::CreateNamedPipe(pipe_path.c_str(), PIPE_ACCESS_INBOUND | read_mode, PIPE_TYPE_BYTE | PIPE_WAIT, 1, 1024, 1024, 120 * 1000, NULL); if (INVALID_HANDLE_VALUE == m_read) - return false; - m_write = ::CreateFile(pipe_name.c_str(), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, write_mode, NULL); - if (INVALID_HANDLE_VALUE == m_write) + return Error(::GetLastError(), eErrorTypeWin32); + m_read_fd = _open_osfhandle((intptr_t)m_read, _O_RDONLY); + ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped)); + m_read_overlapped.hEvent = ::CreateEvent(nullptr, TRUE, FALSE, nullptr); + + // Open the write end of the pipe. + Error result = OpenNamedPipe(name, child_process_inherit, false); + if (!result.Success()) { - ::CloseHandle(m_read); - m_read = INVALID_HANDLE_VALUE; - return false; + CloseReadEndpoint(); + return result; } - m_read_fd = _open_osfhandle((intptr_t)m_read, _O_RDONLY); - m_write_fd = _open_osfhandle((intptr_t)m_write, _O_WRONLY); + return result; +} + +Error +PipeWindows::OpenAsReader(llvm::StringRef name, bool child_process_inherit) +{ + if (CanRead() || CanWrite()) + return Error(ERROR_ALREADY_EXISTS, eErrorTypeWin32); + + return OpenNamedPipe(name, child_process_inherit, true); +} - if (read_overlapped) +Error +PipeWindows::OpenAsWriter(llvm::StringRef name, bool child_process_inherit) +{ + if (CanRead() || CanWrite()) + return Error(ERROR_ALREADY_EXISTS, eErrorTypeWin32); + + return OpenNamedPipe(name, child_process_inherit, false); +} + +Error +PipeWindows::OpenNamedPipe(llvm::StringRef name, bool child_process_inherit, bool is_read) +{ + if (name.empty()) + return Error(ERROR_INVALID_PARAMETER, eErrorTypeWin32); + + assert(is_read ? !CanRead() : !CanWrite()); + + SECURITY_ATTRIBUTES attributes = {0}; + attributes.bInheritHandle = child_process_inherit; + + std::string pipe_path = "\\\\.\\Pipe\\"; + pipe_path.append(name); + + if (is_read) { - m_read_overlapped = new OVERLAPPED; - ZeroMemory(m_read_overlapped, sizeof(OVERLAPPED)); + m_read = ::CreateFile(pipe_path.c_str(), GENERIC_READ, 0, &attributes, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + if (INVALID_HANDLE_VALUE == m_read) + return Error(::GetLastError(), eErrorTypeWin32); + + m_read_fd = _open_osfhandle((intptr_t)m_read, _O_RDONLY); + + ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped)); + m_read_overlapped.hEvent = ::CreateEvent(nullptr, TRUE, FALSE, nullptr); } - if (write_overlapped) + else { - m_write_overlapped = new OVERLAPPED; - ZeroMemory(m_write_overlapped, sizeof(OVERLAPPED)); + m_write = ::CreateFile(pipe_path.c_str(), GENERIC_WRITE, 0, &attributes, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + if (INVALID_HANDLE_VALUE == m_write) + return Error(::GetLastError(), eErrorTypeWin32); + + m_write_fd = _open_osfhandle((intptr_t)m_write, _O_WRONLY); + + ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped)); } - return true; + + return Error(); } int -Pipe::GetReadFileDescriptor() const +PipeWindows::GetReadFileDescriptor() const { return m_read_fd; } int -Pipe::GetWriteFileDescriptor() const +PipeWindows::GetWriteFileDescriptor() const { return m_write_fd; } int -Pipe::ReleaseReadFileDescriptor() +PipeWindows::ReleaseReadFileDescriptor() { + if (!CanRead()) + return -1; int result = m_read_fd; m_read_fd = -1; + if (m_read_overlapped.hEvent) + ::CloseHandle(m_read_overlapped.hEvent); m_read = INVALID_HANDLE_VALUE; - if (m_read_overlapped) - { - delete m_read_overlapped; - m_read_overlapped = nullptr; - } + ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped)); return result; } int -Pipe::ReleaseWriteFileDescriptor() +PipeWindows::ReleaseWriteFileDescriptor() { + if (!CanWrite()) + return -1; int result = m_write_fd; m_write_fd = -1; m_write = INVALID_HANDLE_VALUE; - if (m_write_overlapped) - { - delete m_write_overlapped; - m_write_overlapped = nullptr; - } + ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped)); return result; } void -Pipe::Close() +PipeWindows::CloseReadEndpoint() { - CloseReadFileDescriptor(); - CloseWriteFileDescriptor(); -} + if (!CanRead()) + return; -bool -Pipe::ReadDescriptorIsValid() const -{ - return m_read_fd != -1; + if (m_read_overlapped.hEvent) + ::CloseHandle(m_read_overlapped.hEvent); + _close(m_read_fd); + m_read = INVALID_HANDLE_VALUE; + m_read_fd = -1; + ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped)); } -bool -Pipe::WriteDescriptorIsValid() const +void +PipeWindows::CloseWriteEndpoint() { - return m_write_fd != -1; + if (!CanWrite()) + return; + + _close(m_write_fd); + m_write = INVALID_HANDLE_VALUE; + m_write_fd = -1; + ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped)); } -bool -Pipe::IsValid() const +void +PipeWindows::Close() { - return ReadDescriptorIsValid() && WriteDescriptorIsValid(); + CloseReadEndpoint(); + CloseWriteEndpoint(); } bool -Pipe::CloseReadFileDescriptor() +PipeWindows::CanRead() const { - if (ReadDescriptorIsValid()) - { - int err; - err = _close(m_read_fd); - m_read_fd = -1; - m_read = INVALID_HANDLE_VALUE; - if (m_read_overlapped) - { - delete m_read_overlapped; - m_read_overlapped = nullptr; - } - return err == 0; - } - return true; + return (m_read != INVALID_HANDLE_VALUE); } bool -Pipe::CloseWriteFileDescriptor() +PipeWindows::CanWrite() const { - if (WriteDescriptorIsValid()) - { - int err; - err = _close(m_write_fd); - m_write_fd = -1; - m_write = INVALID_HANDLE_VALUE; - if (m_write_overlapped) - { - delete m_write_overlapped; - m_write_overlapped = nullptr; - } - return err == 0; - } - return true; + return (m_write != INVALID_HANDLE_VALUE); } HANDLE -Pipe::GetReadNativeHandle() +PipeWindows::GetReadNativeHandle() { return m_read; } HANDLE -Pipe::GetWriteNativeHandle() +PipeWindows::GetWriteNativeHandle() { return m_write; } -size_t -Pipe::Read(void *buf, size_t num_bytes) +Error +PipeWindows::Read(void *buf, size_t size, size_t &bytes_read) { - if (ReadDescriptorIsValid()) - { - DWORD bytes_read = 0; - ::ReadFile(m_read, buf, num_bytes, &bytes_read, m_read_overlapped); - if (m_read_overlapped) - GetOverlappedResult(m_read, m_read_overlapped, &bytes_read, TRUE); - return bytes_read; - } - return 0; // Return 0 since errno won't be set if we didn't call read + return ReadWithTimeout(buf, size, std::chrono::milliseconds::zero(), bytes_read); } -size_t -Pipe::Write(const void *buf, size_t num_bytes) +Error +PipeWindows::ReadWithTimeout(void *buf, size_t size, const std::chrono::milliseconds &duration, size_t &bytes_read) { - if (WriteDescriptorIsValid()) + if (!CanRead()) + return Error(ERROR_INVALID_HANDLE, eErrorTypeWin32); + + bytes_read = 0; + DWORD sys_bytes_read = size; + BOOL result = ::ReadFile(m_read, buf, sys_bytes_read, &sys_bytes_read, &m_read_overlapped); + if (!result && GetLastError() != ERROR_IO_PENDING) + return Error(::GetLastError(), eErrorTypeWin32); + + DWORD timeout = (duration == std::chrono::milliseconds::zero()) ? INFINITE : duration.count(); + DWORD wait_result = ::WaitForSingleObject(m_read_overlapped.hEvent, timeout); + if (wait_result != WAIT_OBJECT_0) { - DWORD bytes_written = 0; - ::WriteFile(m_write, buf, num_bytes, &bytes_written, m_read_overlapped); - if (m_write_overlapped) - GetOverlappedResult(m_write, m_write_overlapped, &bytes_written, TRUE); - return bytes_written; + // 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_read_overlapped); + if (!cancel_result && GetLastError() == ERROR_NOT_FOUND) + failed = false; + } + if (failed) + return Error(failure_error, eErrorTypeWin32); } - return 0; // Return 0 since errno won't be set if we didn't call write + + // Now we call GetOverlappedResult setting bWait to false, since we've already waited + // as long as we're willing to. + if (!GetOverlappedResult(m_read, &m_read_overlapped, &sys_bytes_read, FALSE)) + return Error(::GetLastError(), eErrorTypeWin32); + + bytes_read = sys_bytes_read; + return Error(); +} + +Error +PipeWindows::Write(const void *buf, size_t num_bytes, size_t &bytes_written) +{ + if (!CanWrite()) + return Error(ERROR_INVALID_HANDLE, eErrorTypeWin32); + + DWORD sys_bytes_written = 0; + BOOL write_result = ::WriteFile(m_write, buf, num_bytes, &sys_bytes_written, &m_write_overlapped); + if (!write_result && GetLastError() != ERROR_IO_PENDING) + return Error(::GetLastError(), eErrorTypeWin32); + + BOOL result = GetOverlappedResult(m_write, &m_write_overlapped, &sys_bytes_written, TRUE); + if (!result) + return Error(::GetLastError(), eErrorTypeWin32); + return Error(); } |