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 <functional>
16#include <thread>
17
18#include <cerrno>
19#include <climits>
20#include <fcntl.h>
21#include <sys/stat.h>
22#include <sys/types.h>
23#include <unistd.h>
24
25using namespace lldb;
26using namespace lldb_private;
27
29
30enum PIPES { READ, WRITE }; // Constants 0 and 1 for READ and WRITE
31
32// pipe2 is supported by a limited set of platforms
33// TODO: Add more platforms that support pipe2.
34#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || \
35 defined(__OpenBSD__)
36#define PIPE2_SUPPORTED 1
37#else
38#define PIPE2_SUPPORTED 0
39#endif
40
41static constexpr auto OPEN_WRITER_SLEEP_TIMEOUT_MSECS = 100;
42
43#if defined(FD_CLOEXEC) && !PIPE2_SUPPORTED
44static bool SetCloexecFlag(int fd) {
45 int flags = ::fcntl(fd, F_GETFD);
46 if (flags == -1)
47 return false;
48 return (::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == 0);
49}
50#endif
51
52static std::chrono::time_point<std::chrono::steady_clock> Now() {
53 return std::chrono::steady_clock::now();
54}
55
57 : m_fds{PipePosix::kInvalidDescriptor, PipePosix::kInvalidDescriptor} {}
58
60 : m_fds{read, write} {}
61
63 : PipeBase{std::move(pipe_posix)},
64 m_fds{pipe_posix.ReleaseReadFileDescriptor(),
65 pipe_posix.ReleaseWriteFileDescriptor()} {}
66
68 std::scoped_lock<std::mutex, std::mutex, std::mutex, std::mutex> guard(
69 m_read_mutex, m_write_mutex, pipe_posix.m_read_mutex,
70 pipe_posix.m_write_mutex);
71
72 PipeBase::operator=(std::move(pipe_posix));
73 m_fds[READ] = pipe_posix.ReleaseReadFileDescriptorUnlocked();
74 m_fds[WRITE] = pipe_posix.ReleaseWriteFileDescriptorUnlocked();
75 return *this;
76}
77
79
80Status PipePosix::CreateNew(bool child_processes_inherit) {
81 std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex);
83 return Status(EINVAL, eErrorTypePOSIX);
84
86#if PIPE2_SUPPORTED
87 if (::pipe2(m_fds, (child_processes_inherit) ? 0 : O_CLOEXEC) == 0)
88 return error;
89#else
90 if (::pipe(m_fds) == 0) {
91#ifdef FD_CLOEXEC
92 if (!child_processes_inherit) {
93 if (!SetCloexecFlag(m_fds[0]) || !SetCloexecFlag(m_fds[1])) {
94 error.SetErrorToErrno();
96 return error;
97 }
98 }
99#endif
100 return error;
101 }
102#endif
103
104 error.SetErrorToErrno();
107 return error;
108}
109
110Status PipePosix::CreateNew(llvm::StringRef name, bool child_process_inherit) {
111 std::scoped_lock<std::mutex, std::mutex> (m_read_mutex, m_write_mutex);
113 return Status("Pipe is already opened");
114
116 if (::mkfifo(name.str().c_str(), 0660) != 0)
117 error.SetErrorToErrno();
118
119 return error;
120}
121
123 bool child_process_inherit,
125 llvm::SmallString<128> named_pipe_path;
126 llvm::SmallString<128> pipe_spec((prefix + ".%%%%%%").str());
127 FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir();
128 if (!tmpdir_file_spec)
129 tmpdir_file_spec.AppendPathComponent("/tmp");
130 tmpdir_file_spec.AppendPathComponent(pipe_spec);
131
132 // It's possible that another process creates the target path after we've
133 // verified it's available but before we create it, in which case we should
134 // try again.
136 do {
137 llvm::sys::fs::createUniquePath(tmpdir_file_spec.GetPath(), named_pipe_path,
138 /*MakeAbsolute=*/false);
139 error = CreateNew(named_pipe_path, child_process_inherit);
140 } while (error.GetError() == EEXIST);
141
142 if (error.Success())
143 name = named_pipe_path;
144 return error;
145}
146
147Status PipePosix::OpenAsReader(llvm::StringRef name,
148 bool child_process_inherit) {
149 std::scoped_lock<std::mutex, std::mutex> (m_read_mutex, m_write_mutex);
150
152 return Status("Pipe is already opened");
153
154 int flags = O_RDONLY | O_NONBLOCK;
155 if (!child_process_inherit)
156 flags |= O_CLOEXEC;
157
159 int fd = FileSystem::Instance().Open(name.str().c_str(), flags);
160 if (fd != -1)
161 m_fds[READ] = fd;
162 else
163 error.SetErrorToErrno();
164
165 return error;
166}
167
168Status
170 bool child_process_inherit,
171 const std::chrono::microseconds &timeout) {
172 std::lock_guard<std::mutex> guard(m_write_mutex);
174 return Status("Pipe is already opened");
175
176 int flags = O_WRONLY | O_NONBLOCK;
177 if (!child_process_inherit)
178 flags |= O_CLOEXEC;
179
180 using namespace std::chrono;
181 const auto finish_time = Now() + timeout;
182
183 while (!CanWriteUnlocked()) {
184 if (timeout != microseconds::zero()) {
185 const auto dur = duration_cast<microseconds>(finish_time - Now()).count();
186 if (dur <= 0)
187 return Status("timeout exceeded - reader hasn't opened so far");
188 }
189
190 errno = 0;
191 int fd = ::open(name.str().c_str(), flags);
192 if (fd == -1) {
193 const auto errno_copy = errno;
194 // We may get ENXIO if a reader side of the pipe hasn't opened yet.
195 if (errno_copy != ENXIO && errno_copy != EINTR)
196 return Status(errno_copy, eErrorTypePOSIX);
197
198 std::this_thread::sleep_for(
199 milliseconds(OPEN_WRITER_SLEEP_TIMEOUT_MSECS));
200 } else {
201 m_fds[WRITE] = fd;
202 }
203 }
204
205 return Status();
206}
207
209 std::lock_guard<std::mutex> guard(m_read_mutex);
211}
212
214 return m_fds[READ];
215}
216
218 std::lock_guard<std::mutex> guard(m_write_mutex);
220}
221
223 return m_fds[WRITE];
224}
225
227 std::lock_guard<std::mutex> guard(m_read_mutex);
229}
230
232 const int fd = m_fds[READ];
234 return fd;
235}
236
238 std::lock_guard<std::mutex> guard(m_write_mutex);
240}
241
243 const int fd = m_fds[WRITE];
245 return fd;
246}
247
249 std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex);
251}
252
256}
257
258Status PipePosix::Delete(llvm::StringRef name) {
259 return llvm::sys::fs::remove(name);
260}
261
262bool PipePosix::CanRead() const {
263 std::lock_guard<std::mutex> guard(m_read_mutex);
264 return CanReadUnlocked();
265}
266
269}
270
272 std::lock_guard<std::mutex> guard(m_write_mutex);
273 return CanWriteUnlocked();
274}
275
278}
279
281 std::lock_guard<std::mutex> guard(m_read_mutex);
283}
285 if (CanReadUnlocked()) {
286 close(m_fds[READ]);
288 }
289}
290
292 std::lock_guard<std::mutex> guard(m_write_mutex);
294}
295
297 if (CanWriteUnlocked()) {
298 close(m_fds[WRITE]);
300 }
301}
302
303Status PipePosix::ReadWithTimeout(void *buf, size_t size,
304 const std::chrono::microseconds &timeout,
305 size_t &bytes_read) {
306 std::lock_guard<std::mutex> guard(m_read_mutex);
307 bytes_read = 0;
308 if (!CanReadUnlocked())
309 return Status(EINVAL, eErrorTypePOSIX);
310
311 const int fd = GetReadFileDescriptorUnlocked();
312
313 SelectHelper select_helper;
314 select_helper.SetTimeout(timeout);
315 select_helper.FDSetRead(fd);
316
318 while (error.Success()) {
319 error = select_helper.Select();
320 if (error.Success()) {
321 auto result =
322 ::read(fd, static_cast<char *>(buf) + bytes_read, size - bytes_read);
323 if (result != -1) {
324 bytes_read += result;
325 if (bytes_read == size || result == 0)
326 break;
327 } else if (errno == EINTR) {
328 continue;
329 } else {
330 error.SetErrorToErrno();
331 break;
332 }
333 }
334 }
335 return error;
336}
337
338Status PipePosix::Write(const void *buf, size_t size, size_t &bytes_written) {
339 std::lock_guard<std::mutex> guard(m_write_mutex);
340 bytes_written = 0;
341 if (!CanWriteUnlocked())
342 return Status(EINVAL, eErrorTypePOSIX);
343
344 const int fd = GetWriteFileDescriptorUnlocked();
345 SelectHelper select_helper;
346 select_helper.SetTimeout(std::chrono::seconds(0));
347 select_helper.FDSetWrite(fd);
348
350 while (error.Success()) {
351 error = select_helper.Select();
352 if (error.Success()) {
353 auto result = ::write(fd, static_cast<const char *>(buf) + bytes_written,
354 size - bytes_written);
355 if (result != -1) {
356 bytes_written += result;
357 if (bytes_written == size)
358 break;
359 } else if (errno == EINTR) {
360 continue;
361 } else {
362 error.SetErrorToErrno();
363 }
364 }
365 }
366 return error;
367}
static llvm::raw_ostream & error(Stream &strm)
static std::chrono::time_point< std::chrono::steady_clock > Now()
Definition: PipePosix.cpp:52
static constexpr auto OPEN_WRITER_SLEEP_TIMEOUT_MSECS
Definition: PipePosix.cpp:41
PIPES
Definition: PipePosix.cpp:30
@ WRITE
Definition: PipePosix.cpp:30
@ READ
Definition: PipePosix.cpp:30
static bool SetCloexecFlag(int fd)
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:56
void AppendPathComponent(llvm::StringRef component)
Definition: FileSpec.cpp:447
size_t GetPath(char *path, size_t max_path_length, bool denormalize=true) const
Extract the full path to the file.
Definition: FileSpec.cpp:367
int Open(const char *path, int flags, int mode=0600)
Wraps ::open in a platform-independent way.
static FileSystem & Instance()
A posix-based implementation of Pipe, a class that abtracts unix style pipes.
Definition: PipePosix.h:21
Status OpenAsReader(llvm::StringRef name, bool child_process_inherit) override
Definition: PipePosix.cpp:147
int ReleaseWriteFileDescriptorUnlocked()
Definition: PipePosix.cpp:242
int GetReadFileDescriptor() const override
Definition: PipePosix.cpp:208
Status OpenAsWriterWithTimeout(llvm::StringRef name, bool child_process_inherit, const std::chrono::microseconds &timeout) override
Definition: PipePosix.cpp:169
void CloseWriteFileDescriptor() override
Definition: PipePosix.cpp:291
bool CanWrite() const override
Definition: PipePosix.cpp:271
bool CanRead() const override
Definition: PipePosix.cpp:262
Status Write(const void *buf, size_t size, size_t &bytes_written) override
Definition: PipePosix.cpp:338
void Close() override
Definition: PipePosix.cpp:248
static int kInvalidDescriptor
Definition: PipePosix.h:23
Status ReadWithTimeout(void *buf, size_t size, const std::chrono::microseconds &timeout, size_t &bytes_read) override
Definition: PipePosix.cpp:303
std::mutex m_read_mutex
Mutexes for m_fds;.
Definition: PipePosix.h:87
int ReleaseReadFileDescriptor() override
Definition: PipePosix.cpp:226
Status Delete(llvm::StringRef name) override
Definition: PipePosix.cpp:258
int ReleaseReadFileDescriptorUnlocked()
Definition: PipePosix.cpp:231
void CloseWriteFileDescriptorUnlocked()
Definition: PipePosix.cpp:296
int GetReadFileDescriptorUnlocked() const
Definition: PipePosix.cpp:213
Status CreateWithUniqueName(llvm::StringRef prefix, bool child_process_inherit, llvm::SmallVectorImpl< char > &name) override
Definition: PipePosix.cpp:122
bool CanWriteUnlocked() const
Definition: PipePosix.cpp:276
PipePosix & operator=(const PipePosix &)=delete
int ReleaseWriteFileDescriptor() override
Definition: PipePosix.cpp:237
void CloseReadFileDescriptorUnlocked()
Definition: PipePosix.cpp:284
void CloseReadFileDescriptor() override
Definition: PipePosix.cpp:280
Status CreateNew(bool child_process_inherit) override
Definition: PipePosix.cpp:80
bool CanReadUnlocked() const
Definition: PipePosix.cpp:267
int GetWriteFileDescriptor() const override
Definition: PipePosix.cpp:217
int GetWriteFileDescriptorUnlocked() const
Definition: PipePosix.cpp:222
std::mutex m_write_mutex
Definition: PipePosix.h:88
An error handling class.
Definition: Status.h:44
A class that represents a running process on the host machine.
Definition: SBAttachInfo.h:14
Definition: SBAddress.h:15
int pipe_t
Definition: lldb-types.h:64
@ eErrorTypePOSIX
POSIX error codes.
#define O_NONBLOCK