Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/lldb/source/Host/posix/PipePosix.cpp
39608 views
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
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 <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
25
using namespace lldb;
26
using namespace lldb_private;
27
28
int PipePosix::kInvalidDescriptor = -1;
29
30
enum 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
41
static constexpr auto OPEN_WRITER_SLEEP_TIMEOUT_MSECS = 100;
42
43
#if defined(FD_CLOEXEC) && !PIPE2_SUPPORTED
44
static 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
52
static std::chrono::time_point<std::chrono::steady_clock> Now() {
53
return std::chrono::steady_clock::now();
54
}
55
56
PipePosix::PipePosix()
57
: m_fds{PipePosix::kInvalidDescriptor, PipePosix::kInvalidDescriptor} {}
58
59
PipePosix::PipePosix(lldb::pipe_t read, lldb::pipe_t write)
60
: m_fds{read, write} {}
61
62
PipePosix::PipePosix(PipePosix &&pipe_posix)
63
: PipeBase{std::move(pipe_posix)},
64
m_fds{pipe_posix.ReleaseReadFileDescriptor(),
65
pipe_posix.ReleaseWriteFileDescriptor()} {}
66
67
PipePosix &PipePosix::operator=(PipePosix &&pipe_posix) {
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
78
PipePosix::~PipePosix() { Close(); }
79
80
Status PipePosix::CreateNew(bool child_processes_inherit) {
81
std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex);
82
if (CanReadUnlocked() || CanWriteUnlocked())
83
return Status(EINVAL, eErrorTypePOSIX);
84
85
Status error;
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();
95
CloseUnlocked();
96
return error;
97
}
98
}
99
#endif
100
return error;
101
}
102
#endif
103
104
error.SetErrorToErrno();
105
m_fds[READ] = PipePosix::kInvalidDescriptor;
106
m_fds[WRITE] = PipePosix::kInvalidDescriptor;
107
return error;
108
}
109
110
Status PipePosix::CreateNew(llvm::StringRef name, bool child_process_inherit) {
111
std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex);
112
if (CanReadUnlocked() || CanWriteUnlocked())
113
return Status("Pipe is already opened");
114
115
Status error;
116
if (::mkfifo(name.str().c_str(), 0660) != 0)
117
error.SetErrorToErrno();
118
119
return error;
120
}
121
122
Status PipePosix::CreateWithUniqueName(llvm::StringRef prefix,
123
bool child_process_inherit,
124
llvm::SmallVectorImpl<char> &name) {
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.
135
Status error;
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
147
Status PipePosix::OpenAsReader(llvm::StringRef name,
148
bool child_process_inherit) {
149
std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex);
150
151
if (CanReadUnlocked() || CanWriteUnlocked())
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
158
Status error;
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
168
Status
169
PipePosix::OpenAsWriterWithTimeout(llvm::StringRef name,
170
bool child_process_inherit,
171
const std::chrono::microseconds &timeout) {
172
std::lock_guard<std::mutex> guard(m_write_mutex);
173
if (CanReadUnlocked() || CanWriteUnlocked())
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
208
int PipePosix::GetReadFileDescriptor() const {
209
std::lock_guard<std::mutex> guard(m_read_mutex);
210
return GetReadFileDescriptorUnlocked();
211
}
212
213
int PipePosix::GetReadFileDescriptorUnlocked() const {
214
return m_fds[READ];
215
}
216
217
int PipePosix::GetWriteFileDescriptor() const {
218
std::lock_guard<std::mutex> guard(m_write_mutex);
219
return GetWriteFileDescriptorUnlocked();
220
}
221
222
int PipePosix::GetWriteFileDescriptorUnlocked() const {
223
return m_fds[WRITE];
224
}
225
226
int PipePosix::ReleaseReadFileDescriptor() {
227
std::lock_guard<std::mutex> guard(m_read_mutex);
228
return ReleaseReadFileDescriptorUnlocked();
229
}
230
231
int PipePosix::ReleaseReadFileDescriptorUnlocked() {
232
const int fd = m_fds[READ];
233
m_fds[READ] = PipePosix::kInvalidDescriptor;
234
return fd;
235
}
236
237
int PipePosix::ReleaseWriteFileDescriptor() {
238
std::lock_guard<std::mutex> guard(m_write_mutex);
239
return ReleaseWriteFileDescriptorUnlocked();
240
}
241
242
int PipePosix::ReleaseWriteFileDescriptorUnlocked() {
243
const int fd = m_fds[WRITE];
244
m_fds[WRITE] = PipePosix::kInvalidDescriptor;
245
return fd;
246
}
247
248
void PipePosix::Close() {
249
std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex);
250
CloseUnlocked();
251
}
252
253
void PipePosix::CloseUnlocked() {
254
CloseReadFileDescriptorUnlocked();
255
CloseWriteFileDescriptorUnlocked();
256
}
257
258
Status PipePosix::Delete(llvm::StringRef name) {
259
return llvm::sys::fs::remove(name);
260
}
261
262
bool PipePosix::CanRead() const {
263
std::lock_guard<std::mutex> guard(m_read_mutex);
264
return CanReadUnlocked();
265
}
266
267
bool PipePosix::CanReadUnlocked() const {
268
return m_fds[READ] != PipePosix::kInvalidDescriptor;
269
}
270
271
bool PipePosix::CanWrite() const {
272
std::lock_guard<std::mutex> guard(m_write_mutex);
273
return CanWriteUnlocked();
274
}
275
276
bool PipePosix::CanWriteUnlocked() const {
277
return m_fds[WRITE] != PipePosix::kInvalidDescriptor;
278
}
279
280
void PipePosix::CloseReadFileDescriptor() {
281
std::lock_guard<std::mutex> guard(m_read_mutex);
282
CloseReadFileDescriptorUnlocked();
283
}
284
void PipePosix::CloseReadFileDescriptorUnlocked() {
285
if (CanReadUnlocked()) {
286
close(m_fds[READ]);
287
m_fds[READ] = PipePosix::kInvalidDescriptor;
288
}
289
}
290
291
void PipePosix::CloseWriteFileDescriptor() {
292
std::lock_guard<std::mutex> guard(m_write_mutex);
293
CloseWriteFileDescriptorUnlocked();
294
}
295
296
void PipePosix::CloseWriteFileDescriptorUnlocked() {
297
if (CanWriteUnlocked()) {
298
close(m_fds[WRITE]);
299
m_fds[WRITE] = PipePosix::kInvalidDescriptor;
300
}
301
}
302
303
Status 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
317
Status error;
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
338
Status 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
349
Status error;
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
}
368
369