diff options
| author | Ed Maste <emaste@FreeBSD.org> | 2015-02-06 21:38:51 +0000 | 
|---|---|---|
| committer | Ed Maste <emaste@FreeBSD.org> | 2015-02-06 21:38:51 +0000 | 
| commit | 205afe679855a4ce8149cdaa94d3f0868ce796dc (patch) | |
| tree | 09bc83f73246ee3c7a779605cd0122093d2a8a19 /source/Host/posix/PipePosix.cpp | |
| parent | 0cac4ca3916ac24ab6139d03cbfd18db9e715bfe (diff) | |
Notes
Diffstat (limited to 'source/Host/posix/PipePosix.cpp')
| -rw-r--r-- | source/Host/posix/PipePosix.cpp | 378 | 
1 files changed, 378 insertions, 0 deletions
| diff --git a/source/Host/posix/PipePosix.cpp b/source/Host/posix/PipePosix.cpp new file mode 100644 index 0000000000000..02838ec5124e7 --- /dev/null +++ b/source/Host/posix/PipePosix.cpp @@ -0,0 +1,378 @@ +//===-- PipePosix.cpp -------------------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/posix/PipePosix.h" +#include "lldb/Host/FileSystem.h" + +#include <functional> +#include <thread> + +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> + +using namespace lldb; +using namespace lldb_private; + +int PipePosix::kInvalidDescriptor = -1; + +enum PIPES { READ, WRITE }; // Constants 0 and 1 for READ and WRITE + +// pipe2 is supported by Linux, FreeBSD v10 and higher. +// TODO: Add more platforms that support pipe2. +#if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD__ >= 10) +#define PIPE2_SUPPORTED 1 +#else +#define PIPE2_SUPPORTED 0 +#endif + +namespace +{ + +constexpr auto OPEN_WRITER_SLEEP_TIMEOUT_MSECS = 100; + +#if defined(FD_CLOEXEC) && !PIPE2_SUPPORTED +bool SetCloexecFlag(int fd) +{ +    int flags = ::fcntl(fd, F_GETFD); +    if (flags == -1) +        return false; +    return (::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == 0); +} +#endif + +std::chrono::time_point<std::chrono::steady_clock> +Now() +{ +    return std::chrono::steady_clock::now(); +} + +Error +SelectIO(int handle, bool is_read, const std::function<Error(bool&)> &io_handler, const std::chrono::microseconds &timeout) +{ +    Error error; +    fd_set fds; +    bool done = false; + +    using namespace std::chrono; + +    const auto finish_time = Now() + timeout; + +    while (!done) +    { +        struct timeval tv = {0, 0}; +        if (timeout != microseconds::zero()) +        { +            const auto remaining_dur = duration_cast<microseconds>(finish_time - Now()); +            if (remaining_dur.count() <= 0) +            { +                error.SetErrorString("timeout exceeded"); +                break; +            } +            const auto dur_secs = duration_cast<seconds>(remaining_dur); +            const auto dur_usecs = remaining_dur % seconds(1); + +            tv.tv_sec = dur_secs.count(); +            tv.tv_usec = dur_usecs.count(); +        } +        else +            tv.tv_sec = 1; + +        FD_ZERO(&fds); +        FD_SET(handle, &fds); + +        const auto retval = ::select(handle + 1, +                                     (is_read) ? &fds : nullptr, +                                     (is_read) ? nullptr : &fds, +                                     nullptr, &tv); +        if (retval == -1) +        { +            if (errno == EINTR) +                continue; +            error.SetErrorToErrno(); +            break; +        } +        if (retval == 0) +        { +            error.SetErrorString("timeout exceeded"); +            break; +        } +        if (!FD_ISSET(handle, &fds)) +        { +            error.SetErrorString("invalid state"); +            break; +        } + +        error = io_handler(done); +        if (error.Fail()) +        { +          if (error.GetError() == EINTR) +              continue; +            break; +        } +    } +    return error; +} + +} + +PipePosix::PipePosix() +{ +    m_fds[READ] = PipePosix::kInvalidDescriptor; +    m_fds[WRITE] = PipePosix::kInvalidDescriptor; +} + +PipePosix::~PipePosix() +{ +    Close(); +} + +Error +PipePosix::CreateNew(bool child_processes_inherit) +{ +    if (CanRead() || CanWrite()) +        return Error(EINVAL, eErrorTypePOSIX); + +    Error error; +#if PIPE2_SUPPORTED +    if (::pipe2(m_fds, (child_processes_inherit) ? 0 : O_CLOEXEC) == 0) +        return error; +#else +    if (::pipe(m_fds) == 0) +    { +#ifdef FD_CLOEXEC +        if (!child_processes_inherit) +        { +            if (!SetCloexecFlag(m_fds[0]) || !SetCloexecFlag(m_fds[1])) +            { +                error.SetErrorToErrno(); +                Close(); +                return error; +            } +        } +#endif +        return error; +    } +#endif + +    error.SetErrorToErrno(); +    m_fds[READ] = PipePosix::kInvalidDescriptor; +    m_fds[WRITE] = PipePosix::kInvalidDescriptor; +    return error; +} + +Error +PipePosix::CreateNew(llvm::StringRef name, bool child_process_inherit) +{ +    if (CanRead() || CanWrite()) +        return Error("Pipe is already opened"); + +    Error error; +    if (::mkfifo(name.data(), 0660) != 0) +        error.SetErrorToErrno(); + +    return error; +} + +Error +PipePosix::OpenAsReader(llvm::StringRef name, bool child_process_inherit) +{ +    if (CanRead() || CanWrite()) +        return Error("Pipe is already opened"); + +    int flags = O_RDONLY | O_NONBLOCK; +    if (!child_process_inherit) +        flags |= O_CLOEXEC; + +    Error error; +    int fd = ::open(name.data(), flags); +    if (fd != -1) +        m_fds[READ] = fd; +    else +        error.SetErrorToErrno(); + +    return error; +} + +Error +PipePosix::OpenAsWriterWithTimeout(llvm::StringRef name, bool child_process_inherit, const std::chrono::microseconds &timeout) +{ +    if (CanRead() || CanWrite()) +        return Error("Pipe is already opened"); + +    int flags = O_WRONLY | O_NONBLOCK; +    if (!child_process_inherit) +        flags |= O_CLOEXEC; + +    using namespace std::chrono; +    const auto finish_time = Now() + timeout; + +    while (!CanWrite()) +    { +        if (timeout != microseconds::zero()) +        { +            const auto dur = duration_cast<microseconds>(finish_time - Now()).count(); +            if (dur <= 0) +                return Error("timeout exceeded - reader hasn't opened so far"); +        } + +        errno = 0; +        int fd = ::open(name.data(), flags); +        if (fd == -1) +        { +            const auto errno_copy = errno; +            // We may get ENXIO if a reader side of the pipe hasn't opened yet. +            if (errno_copy != ENXIO) +                return Error(errno_copy, eErrorTypePOSIX); + +            std::this_thread::sleep_for(milliseconds(OPEN_WRITER_SLEEP_TIMEOUT_MSECS)); +        } +        else +        { +            m_fds[WRITE] = fd; +        } +    } + +    return Error(); +} + +int +PipePosix::GetReadFileDescriptor() const +{ +    return m_fds[READ]; +} + +int +PipePosix::GetWriteFileDescriptor() const +{ +    return m_fds[WRITE]; +} + +int +PipePosix::ReleaseReadFileDescriptor() +{ +    const int fd = m_fds[READ]; +    m_fds[READ] = PipePosix::kInvalidDescriptor; +    return fd; +} + +int +PipePosix::ReleaseWriteFileDescriptor() +{ +    const int fd = m_fds[WRITE]; +    m_fds[WRITE] = PipePosix::kInvalidDescriptor; +    return fd; +} + +void +PipePosix::Close() +{ +    CloseReadFileDescriptor(); +    CloseWriteFileDescriptor(); +} + +Error +PipePosix::Delete(llvm::StringRef name) +{ +    return FileSystem::Unlink(name.data()); +} + +bool +PipePosix::CanRead() const +{ +    return m_fds[READ] != PipePosix::kInvalidDescriptor; +} + +bool +PipePosix::CanWrite() const +{ +    return m_fds[WRITE] != PipePosix::kInvalidDescriptor; +} + +void +PipePosix::CloseReadFileDescriptor() +{ +    if (CanRead()) +    { +        close(m_fds[READ]); +        m_fds[READ] = PipePosix::kInvalidDescriptor; +    } +} + +void +PipePosix::CloseWriteFileDescriptor() +{ +    if (CanWrite()) +    { +        close(m_fds[WRITE]); +        m_fds[WRITE] = PipePosix::kInvalidDescriptor; +    } +} + +Error +PipePosix::ReadWithTimeout(void *buf, size_t size, const std::chrono::microseconds &timeout, size_t &bytes_read) +{ +    bytes_read = 0; +    if (!CanRead()) +        return Error(EINVAL, eErrorTypePOSIX); + +    auto handle = GetReadFileDescriptor(); +    return SelectIO(handle, +                    true, +                    [=, &bytes_read](bool &done) +                    { +                      Error error; +                      auto result = ::read(handle, +                                           reinterpret_cast<char*>(buf) + bytes_read, +                                           size - bytes_read); +                      if (result != -1) +                      { +                          bytes_read += result; +                          if (bytes_read == size || result == 0) +                              done = true; +                      } +                      else +                          error.SetErrorToErrno(); + +                      return error; +                  }, +                  timeout); +} + +Error +PipePosix::Write(const void *buf, size_t size, size_t &bytes_written) +{ +    bytes_written = 0; +    if (!CanWrite()) +        return Error(EINVAL, eErrorTypePOSIX); + +    auto handle = GetWriteFileDescriptor(); +    return SelectIO(handle, +                    false, +                    [=, &bytes_written](bool &done) +                    { +                        Error error; +                        auto result = ::write(handle, +                                              reinterpret_cast<const char*>(buf) + bytes_written, +                                              size - bytes_written); +                        if (result != -1) +                        { +                            bytes_written += result; +                            if (bytes_written == size) +                                done = true; +                        } +                        else +                            error.SetErrorToErrno(); + +                        return error; +                    }, +                    std::chrono::microseconds::zero()); +} | 
