brintos

brintos / llvm-project-archived public Read only

0
0
Text · 9.8 KiB · 68b02a9 Raw
346 lines · cpp
1//===-- PipePosix.cpp -----------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "lldb/Host/posix/PipePosix.h"10#include "lldb/Host/FileSystem.h"11#include "lldb/Host/HostInfo.h"12#include "lldb/Utility/SelectHelper.h"13#include "llvm/ADT/SmallString.h"14#include "llvm/Support/Errno.h"15#include "llvm/Support/Error.h"16#include <functional>17#include <system_error>18#include <thread>19 20#include <cerrno>21#include <climits>22#include <fcntl.h>23#include <sys/stat.h>24#include <sys/types.h>25#include <unistd.h>26 27using namespace lldb;28using namespace lldb_private;29 30int PipePosix::kInvalidDescriptor = -1;31 32enum PIPES { READ, WRITE }; // Constants 0 and 1 for READ and WRITE33 34// pipe2 is supported by a limited set of platforms35// TODO: Add more platforms that support pipe2.36#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) ||       \37    defined(__OpenBSD__)38#define PIPE2_SUPPORTED 139#else40#define PIPE2_SUPPORTED 041#endif42 43static constexpr auto OPEN_WRITER_SLEEP_TIMEOUT_MSECS = 100;44 45#if defined(FD_CLOEXEC) && !PIPE2_SUPPORTED46static bool SetCloexecFlag(int fd) {47  int flags = ::fcntl(fd, F_GETFD);48  if (flags == -1)49    return false;50  return (::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == 0);51}52#endif53 54static std::chrono::time_point<std::chrono::steady_clock> Now() {55  return std::chrono::steady_clock::now();56}57 58PipePosix::PipePosix()59    : m_fds{PipePosix::kInvalidDescriptor, PipePosix::kInvalidDescriptor} {}60 61PipePosix::PipePosix(lldb::pipe_t read, lldb::pipe_t write)62    : m_fds{read, write} {}63 64PipePosix::PipePosix(PipePosix &&pipe_posix)65    : PipeBase{std::move(pipe_posix)},66      m_fds{pipe_posix.ReleaseReadFileDescriptor(),67            pipe_posix.ReleaseWriteFileDescriptor()} {}68 69PipePosix &PipePosix::operator=(PipePosix &&pipe_posix) {70  std::scoped_lock<std::mutex, std::mutex, std::mutex, std::mutex> guard(71      m_read_mutex, m_write_mutex, pipe_posix.m_read_mutex,72      pipe_posix.m_write_mutex);73 74  PipeBase::operator=(std::move(pipe_posix));75  m_fds[READ] = pipe_posix.ReleaseReadFileDescriptorUnlocked();76  m_fds[WRITE] = pipe_posix.ReleaseWriteFileDescriptorUnlocked();77  return *this;78}79 80PipePosix::~PipePosix() { Close(); }81 82Status PipePosix::CreateNew() {83  std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex);84  if (CanReadUnlocked() || CanWriteUnlocked())85    return Status(EINVAL, eErrorTypePOSIX);86 87  Status error;88#if PIPE2_SUPPORTED89  if (::pipe2(m_fds, O_CLOEXEC) == 0)90    return error;91#else92  if (::pipe(m_fds) == 0) {93#ifdef FD_CLOEXEC94    if (!SetCloexecFlag(m_fds[0]) || !SetCloexecFlag(m_fds[1])) {95      error = Status::FromErrno();96      CloseUnlocked();97      return error;98    }99#endif100    return error;101  }102#endif103 104  error = Status::FromErrno();105  m_fds[READ] = PipePosix::kInvalidDescriptor;106  m_fds[WRITE] = PipePosix::kInvalidDescriptor;107  return error;108}109 110Status PipePosix::CreateNew(llvm::StringRef name) {111  std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex);112  if (CanReadUnlocked() || CanWriteUnlocked())113    return Status::FromErrorString("Pipe is already opened");114 115  Status error;116  if (::mkfifo(name.str().c_str(), 0660) != 0)117    error = Status::FromErrno();118  return error;119}120 121Status PipePosix::CreateWithUniqueName(llvm::StringRef prefix,122                                       llvm::SmallVectorImpl<char> &name) {123  llvm::SmallString<128> named_pipe_path;124  llvm::SmallString<128> pipe_spec((prefix + ".%%%%%%").str());125  FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir();126  if (!tmpdir_file_spec)127    tmpdir_file_spec.AppendPathComponent("/tmp");128  tmpdir_file_spec.AppendPathComponent(pipe_spec);129 130  // It's possible that another process creates the target path after we've131  // verified it's available but before we create it, in which case we should132  // try again.133  Status error;134  do {135    llvm::sys::fs::createUniquePath(tmpdir_file_spec.GetPath(), named_pipe_path,136                                    /*MakeAbsolute=*/false);137    error = CreateNew(named_pipe_path);138  } while (error.GetError() == EEXIST);139 140  if (error.Success())141    name = named_pipe_path;142  return error;143}144 145Status PipePosix::OpenAsReader(llvm::StringRef name) {146  std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex);147 148  if (CanReadUnlocked() || CanWriteUnlocked())149    return Status::FromErrorString("Pipe is already opened");150 151  int flags = O_RDONLY | O_NONBLOCK | O_CLOEXEC;152 153  Status error;154  int fd = FileSystem::Instance().Open(name.str().c_str(), flags);155  if (fd != -1)156    m_fds[READ] = fd;157  else158    error = Status::FromErrno();159 160  return error;161}162 163llvm::Error PipePosix::OpenAsWriter(llvm::StringRef name,164                                    const Timeout<std::micro> &timeout) {165  std::lock_guard<std::mutex> guard(m_write_mutex);166  if (CanReadUnlocked() || CanWriteUnlocked())167    return llvm::createStringError("Pipe is already opened");168 169  int flags = O_WRONLY | O_NONBLOCK | O_CLOEXEC;170 171  using namespace std::chrono;172  std::optional<time_point<steady_clock>> finish_time;173  if (timeout)174    finish_time = Now() + *timeout;175 176  while (!CanWriteUnlocked()) {177    if (timeout) {178      if (Now() > finish_time)179        return llvm::createStringError(180            std::make_error_code(std::errc::timed_out),181            "timeout exceeded - reader hasn't opened so far");182    }183 184    errno = 0;185    int fd = ::open(name.str().c_str(), flags);186    if (fd == -1) {187      const auto errno_copy = errno;188      // We may get ENXIO if a reader side of the pipe hasn't opened yet.189      if (errno_copy != ENXIO && errno_copy != EINTR)190        return llvm::errorCodeToError(191            std::error_code(errno_copy, std::generic_category()));192 193      std::this_thread::sleep_for(194          milliseconds(OPEN_WRITER_SLEEP_TIMEOUT_MSECS));195    } else {196      m_fds[WRITE] = fd;197    }198  }199 200  return llvm::Error::success();201}202 203int PipePosix::GetReadFileDescriptor() const {204  std::lock_guard<std::mutex> guard(m_read_mutex);205  return GetReadFileDescriptorUnlocked();206}207 208int PipePosix::GetReadFileDescriptorUnlocked() const {209  return m_fds[READ];210}211 212int PipePosix::GetWriteFileDescriptor() const {213  std::lock_guard<std::mutex> guard(m_write_mutex);214  return GetWriteFileDescriptorUnlocked();215}216 217int PipePosix::GetWriteFileDescriptorUnlocked() const {218  return m_fds[WRITE];219}220 221int PipePosix::ReleaseReadFileDescriptor() {222  std::lock_guard<std::mutex> guard(m_read_mutex);223  return ReleaseReadFileDescriptorUnlocked();224}225 226int PipePosix::ReleaseReadFileDescriptorUnlocked() {227  const int fd = m_fds[READ];228  m_fds[READ] = PipePosix::kInvalidDescriptor;229  return fd;230}231 232int PipePosix::ReleaseWriteFileDescriptor() {233  std::lock_guard<std::mutex> guard(m_write_mutex);234  return ReleaseWriteFileDescriptorUnlocked();235}236 237int PipePosix::ReleaseWriteFileDescriptorUnlocked() {238  const int fd = m_fds[WRITE];239  m_fds[WRITE] = PipePosix::kInvalidDescriptor;240  return fd;241}242 243void PipePosix::Close() {244  std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex);245  CloseUnlocked();246}247 248void PipePosix::CloseUnlocked() {249  CloseReadFileDescriptorUnlocked();250  CloseWriteFileDescriptorUnlocked();251}252 253Status PipePosix::Delete(llvm::StringRef name) {254  return llvm::sys::fs::remove(name);255}256 257bool PipePosix::CanRead() const {258  std::lock_guard<std::mutex> guard(m_read_mutex);259  return CanReadUnlocked();260}261 262bool PipePosix::CanReadUnlocked() const {263  return m_fds[READ] != PipePosix::kInvalidDescriptor;264}265 266bool PipePosix::CanWrite() const {267  std::lock_guard<std::mutex> guard(m_write_mutex);268  return CanWriteUnlocked();269}270 271bool PipePosix::CanWriteUnlocked() const {272  return m_fds[WRITE] != PipePosix::kInvalidDescriptor;273}274 275void PipePosix::CloseReadFileDescriptor() {276  std::lock_guard<std::mutex> guard(m_read_mutex);277  CloseReadFileDescriptorUnlocked();278}279void PipePosix::CloseReadFileDescriptorUnlocked() {280  if (CanReadUnlocked()) {281    close(m_fds[READ]);282    m_fds[READ] = PipePosix::kInvalidDescriptor;283  }284}285 286void PipePosix::CloseWriteFileDescriptor() {287  std::lock_guard<std::mutex> guard(m_write_mutex);288  CloseWriteFileDescriptorUnlocked();289}290 291void PipePosix::CloseWriteFileDescriptorUnlocked() {292  if (CanWriteUnlocked()) {293    close(m_fds[WRITE]);294    m_fds[WRITE] = PipePosix::kInvalidDescriptor;295  }296}297 298llvm::Expected<size_t> PipePosix::Read(void *buf, size_t size,299                                       const Timeout<std::micro> &timeout) {300  std::lock_guard<std::mutex> guard(m_read_mutex);301  if (!CanReadUnlocked())302    return llvm::errorCodeToError(303        std::make_error_code(std::errc::invalid_argument));304 305  const int fd = GetReadFileDescriptorUnlocked();306 307  SelectHelper select_helper;308  if (timeout)309    select_helper.SetTimeout(*timeout);310  select_helper.FDSetRead(fd);311 312  if (llvm::Error error = select_helper.Select().takeError())313    return error;314 315  ssize_t result = ::read(fd, buf, size);316  if (result == -1)317    return llvm::errorCodeToError(318        std::error_code(errno, std::generic_category()));319 320  return result;321}322 323llvm::Expected<size_t> PipePosix::Write(const void *buf, size_t size,324                                        const Timeout<std::micro> &timeout) {325  std::lock_guard<std::mutex> guard(m_write_mutex);326  if (!CanWriteUnlocked())327    return llvm::errorCodeToError(328        std::make_error_code(std::errc::invalid_argument));329 330  const int fd = GetWriteFileDescriptorUnlocked();331  SelectHelper select_helper;332  if (timeout)333    select_helper.SetTimeout(*timeout);334  select_helper.FDSetWrite(fd);335 336  if (llvm::Error error = select_helper.Select().takeError())337    return error;338 339  ssize_t result = ::write(fd, buf, size);340  if (result == -1)341    return llvm::errorCodeToError(342        std::error_code(errno, std::generic_category()));343 344  return result;345}346