LLDB mainline
PipePosix.cpp
Go to the documentation of this file.
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-exception
6//
7//===----------------------------------------------------------------------===//
8
11#include "lldb/Host/HostInfo.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
31
32enum PIPES { READ, WRITE }; // Constants 0 and 1 for READ and WRITE
33
34// pipe2 is supported by a limited set of platforms
35// TODO: Add more platforms that support pipe2.
36#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || \
37 defined(__OpenBSD__)
38#define PIPE2_SUPPORTED 1
39#else
40#define PIPE2_SUPPORTED 0
41#endif
42
43static constexpr auto OPEN_WRITER_SLEEP_TIMEOUT_MSECS = 100;
44
45#if defined(FD_CLOEXEC) && !PIPE2_SUPPORTED
46static 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#endif
53
54static std::chrono::time_point<std::chrono::steady_clock> Now() {
55 return std::chrono::steady_clock::now();
56}
57
60
62 : m_fds{read, write} {}
63
65 : PipeBase{std::move(pipe_posix)},
67 pipe_posix.ReleaseWriteFileDescriptor()} {}
68
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
81
83 std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex);
85 return Status(EINVAL, eErrorTypePOSIX);
86
88#if PIPE2_SUPPORTED
89 if (::pipe2(m_fds, O_CLOEXEC) == 0)
90 return error;
91#else
92 if (::pipe(m_fds) == 0) {
93#ifdef FD_CLOEXEC
94 if (!SetCloexecFlag(m_fds[0]) || !SetCloexecFlag(m_fds[1])) {
97 return error;
98 }
99#endif
100 return error;
101 }
102#endif
103
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);
113 return Status::FromErrorString("Pipe is already opened");
114
116 if (::mkfifo(name.str().c_str(), 0660) != 0)
118 return error;
119}
120
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've
131 // verified it's available but before we create it, in which case we should
132 // try again.
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
149 return Status::FromErrorString("Pipe is already opened");
150
151 int flags = O_RDONLY | O_NONBLOCK | O_CLOEXEC;
152
154 int fd = FileSystem::Instance().Open(name.str().c_str(), flags);
155 if (fd != -1)
156 m_fds[READ] = fd;
157 else
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);
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
204 std::lock_guard<std::mutex> guard(m_read_mutex);
206}
207
211
213 std::lock_guard<std::mutex> guard(m_write_mutex);
215}
216
220
222 std::lock_guard<std::mutex> guard(m_read_mutex);
224}
225
231
233 std::lock_guard<std::mutex> guard(m_write_mutex);
235}
236
242
244 std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex);
246}
247
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
265
267 std::lock_guard<std::mutex> guard(m_write_mutex);
268 return CanWriteUnlocked();
269}
270
274
276 std::lock_guard<std::mutex> guard(m_read_mutex);
278}
285
287 std::lock_guard<std::mutex> guard(m_write_mutex);
289}
290
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}
static llvm::raw_ostream & error(Stream &strm)
static std::chrono::time_point< std::chrono::steady_clock > Now()
Definition PipePosix.cpp:54
static constexpr auto OPEN_WRITER_SLEEP_TIMEOUT_MSECS
Definition PipePosix.cpp:43
PIPES
Definition PipePosix.cpp:32
@ WRITE
Definition PipePosix.cpp:32
@ READ
Definition PipePosix.cpp:32
lldb_private::Status Select()
void FDSetRead(lldb::socket_t fd)
void SetTimeout(const std::chrono::microseconds &timeout)
void FDSetWrite(lldb::socket_t fd)
A file utility class.
Definition FileSpec.h:57
void AppendPathComponent(llvm::StringRef component)
Definition FileSpec.cpp:454
size_t GetPath(char *path, size_t max_path_length, bool denormalize=true) const
Extract the full path to the file.
Definition FileSpec.cpp:374
int Open(const char *path, int flags, int mode=0600)
Wraps open in a platform-independent way.
static FileSystem & Instance()
llvm::Error OpenAsWriter(llvm::StringRef name, const Timeout< std::micro > &timeout) override
int ReleaseWriteFileDescriptorUnlocked()
int GetReadFileDescriptor() const override
void CloseWriteFileDescriptor() override
Status CreateNew() override
Definition PipePosix.cpp:82
bool CanWrite() const override
bool CanRead() const override
void Close() override
static int kInvalidDescriptor
Definition PipePosix.h:24
std::mutex m_read_mutex
Mutexes for m_fds;.
Definition PipePosix.h:88
int ReleaseReadFileDescriptor() override
llvm::Expected< size_t > Read(void *buf, size_t size, const Timeout< std::micro > &timeout=std::nullopt) override
Status Delete(llvm::StringRef name) override
int ReleaseReadFileDescriptorUnlocked()
void CloseWriteFileDescriptorUnlocked()
int GetReadFileDescriptorUnlocked() const
Status OpenAsReader(llvm::StringRef name) override
bool CanWriteUnlocked() const
PipePosix & operator=(const PipePosix &)=delete
llvm::Expected< size_t > Write(const void *buf, size_t size, const Timeout< std::micro > &timeout=std::nullopt) override
int ReleaseWriteFileDescriptor() override
void CloseReadFileDescriptorUnlocked()
void CloseReadFileDescriptor() override
bool CanReadUnlocked() const
int GetWriteFileDescriptor() const override
int GetWriteFileDescriptorUnlocked() const
Status CreateWithUniqueName(llvm::StringRef prefix, llvm::SmallVectorImpl< char > &name) override
std::mutex m_write_mutex
Definition PipePosix.h:89
An error handling class.
Definition Status.h:118
static Status FromErrno()
Set the current error to errno.
Definition Status.cpp:300
llvm::Error takeError()
Definition Status.h:170
static Status FromErrorString(const char *str)
Definition Status.h:141
A class that represents a running process on the host machine.
int pipe_t
Definition lldb-types.h:64
@ eErrorTypePOSIX
POSIX error codes.
#define O_NONBLOCK