1 //===-- PipePosix.cpp -----------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
22 #include <sys/types.h>
26 using namespace lldb_private
;
28 int PipePosix::kInvalidDescriptor
= -1;
30 enum PIPES
{ READ
, WRITE
}; // Constants 0 and 1 for READ and WRITE
32 // pipe2 is supported by a limited set of platforms
33 // TODO: Add more platforms that support pipe2.
34 #if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD__ >= 10) || \
36 #define PIPE2_SUPPORTED 1
38 #define PIPE2_SUPPORTED 0
41 static constexpr auto OPEN_WRITER_SLEEP_TIMEOUT_MSECS
= 100;
43 #if defined(FD_CLOEXEC) && !PIPE2_SUPPORTED
44 static bool SetCloexecFlag(int fd
) {
45 int flags
= ::fcntl(fd
, F_GETFD
);
48 return (::fcntl(fd
, F_SETFD
, flags
| FD_CLOEXEC
) == 0);
52 static std::chrono::time_point
<std::chrono::steady_clock
> Now() {
53 return std::chrono::steady_clock::now();
56 PipePosix::PipePosix()
57 : m_fds
{PipePosix::kInvalidDescriptor
, PipePosix::kInvalidDescriptor
} {}
59 PipePosix::PipePosix(lldb::pipe_t read
, lldb::pipe_t write
)
60 : m_fds
{read
, write
} {}
62 PipePosix::PipePosix(PipePosix
&&pipe_posix
)
63 : PipeBase
{std::move(pipe_posix
)},
64 m_fds
{pipe_posix
.ReleaseReadFileDescriptor(),
65 pipe_posix
.ReleaseWriteFileDescriptor()} {}
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
);
72 PipeBase::operator=(std::move(pipe_posix
));
73 m_fds
[READ
] = pipe_posix
.ReleaseReadFileDescriptorUnlocked();
74 m_fds
[WRITE
] = pipe_posix
.ReleaseWriteFileDescriptorUnlocked();
78 PipePosix::~PipePosix() { Close(); }
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
);
87 if (::pipe2(m_fds
, (child_processes_inherit
) ? 0 : O_CLOEXEC
) == 0)
90 if (::pipe(m_fds
) == 0) {
92 if (!child_processes_inherit
) {
93 if (!SetCloexecFlag(m_fds
[0]) || !SetCloexecFlag(m_fds
[1])) {
94 error
.SetErrorToErrno();
104 error
.SetErrorToErrno();
105 m_fds
[READ
] = PipePosix::kInvalidDescriptor
;
106 m_fds
[WRITE
] = PipePosix::kInvalidDescriptor
;
110 Status
PipePosix::CreateNew(llvm::StringRef name
, bool child_process_inherit
) {
111 std::scoped_lock
<std::mutex
, std::mutex
> (m_read_mutex
, m_write_mutex
);
112 if (CanReadUnlocked() || CanWriteUnlocked())
113 return Status("Pipe is already opened");
116 if (::mkfifo(name
.str().c_str(), 0660) != 0)
117 error
.SetErrorToErrno();
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
);
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
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
);
143 name
= named_pipe_path
;
147 Status
PipePosix::OpenAsReader(llvm::StringRef name
,
148 bool child_process_inherit
) {
149 std::scoped_lock
<std::mutex
, std::mutex
> (m_read_mutex
, m_write_mutex
);
151 if (CanReadUnlocked() || CanWriteUnlocked())
152 return Status("Pipe is already opened");
154 int flags
= O_RDONLY
| O_NONBLOCK
;
155 if (!child_process_inherit
)
159 int fd
= FileSystem::Instance().Open(name
.str().c_str(), flags
);
163 error
.SetErrorToErrno();
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");
176 int flags
= O_WRONLY
| O_NONBLOCK
;
177 if (!child_process_inherit
)
180 using namespace std::chrono
;
181 const auto finish_time
= Now() + timeout
;
183 while (!CanWriteUnlocked()) {
184 if (timeout
!= microseconds::zero()) {
185 const auto dur
= duration_cast
<microseconds
>(finish_time
- Now()).count();
187 return Status("timeout exceeded - reader hasn't opened so far");
191 int fd
= ::open(name
.str().c_str(), flags
);
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
);
198 std::this_thread::sleep_for(
199 milliseconds(OPEN_WRITER_SLEEP_TIMEOUT_MSECS
));
208 int PipePosix::GetReadFileDescriptor() const {
209 std::lock_guard
<std::mutex
> guard(m_read_mutex
);
210 return GetReadFileDescriptorUnlocked();
213 int PipePosix::GetReadFileDescriptorUnlocked() const {
217 int PipePosix::GetWriteFileDescriptor() const {
218 std::lock_guard
<std::mutex
> guard(m_write_mutex
);
219 return GetWriteFileDescriptorUnlocked();
222 int PipePosix::GetWriteFileDescriptorUnlocked() const {
226 int PipePosix::ReleaseReadFileDescriptor() {
227 std::lock_guard
<std::mutex
> guard(m_read_mutex
);
228 return ReleaseReadFileDescriptorUnlocked();
231 int PipePosix::ReleaseReadFileDescriptorUnlocked() {
232 const int fd
= m_fds
[READ
];
233 m_fds
[READ
] = PipePosix::kInvalidDescriptor
;
237 int PipePosix::ReleaseWriteFileDescriptor() {
238 std::lock_guard
<std::mutex
> guard(m_write_mutex
);
239 return ReleaseWriteFileDescriptorUnlocked();
242 int PipePosix::ReleaseWriteFileDescriptorUnlocked() {
243 const int fd
= m_fds
[WRITE
];
244 m_fds
[WRITE
] = PipePosix::kInvalidDescriptor
;
248 void PipePosix::Close() {
249 std::scoped_lock
<std::mutex
, std::mutex
> guard(m_read_mutex
, m_write_mutex
);
253 void PipePosix::CloseUnlocked() {
254 CloseReadFileDescriptorUnlocked();
255 CloseWriteFileDescriptorUnlocked();
258 Status
PipePosix::Delete(llvm::StringRef name
) {
259 return llvm::sys::fs::remove(name
);
262 bool PipePosix::CanRead() const {
263 std::lock_guard
<std::mutex
> guard(m_read_mutex
);
264 return CanReadUnlocked();
267 bool PipePosix::CanReadUnlocked() const {
268 return m_fds
[READ
] != PipePosix::kInvalidDescriptor
;
271 bool PipePosix::CanWrite() const {
272 std::lock_guard
<std::mutex
> guard(m_write_mutex
);
273 return CanWriteUnlocked();
276 bool PipePosix::CanWriteUnlocked() const {
277 return m_fds
[WRITE
] != PipePosix::kInvalidDescriptor
;
280 void PipePosix::CloseReadFileDescriptor() {
281 std::lock_guard
<std::mutex
> guard(m_read_mutex
);
282 CloseReadFileDescriptorUnlocked();
284 void PipePosix::CloseReadFileDescriptorUnlocked() {
285 if (CanReadUnlocked()) {
287 m_fds
[READ
] = PipePosix::kInvalidDescriptor
;
291 void PipePosix::CloseWriteFileDescriptor() {
292 std::lock_guard
<std::mutex
> guard(m_write_mutex
);
293 CloseWriteFileDescriptorUnlocked();
296 void PipePosix::CloseWriteFileDescriptorUnlocked() {
297 if (CanWriteUnlocked()) {
299 m_fds
[WRITE
] = PipePosix::kInvalidDescriptor
;
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
);
308 if (!CanReadUnlocked())
309 return Status(EINVAL
, eErrorTypePOSIX
);
311 const int fd
= GetReadFileDescriptorUnlocked();
313 SelectHelper select_helper
;
314 select_helper
.SetTimeout(timeout
);
315 select_helper
.FDSetRead(fd
);
318 while (error
.Success()) {
319 error
= select_helper
.Select();
320 if (error
.Success()) {
322 ::read(fd
, static_cast<char *>(buf
) + bytes_read
, size
- bytes_read
);
324 bytes_read
+= result
;
325 if (bytes_read
== size
|| result
== 0)
327 } else if (errno
== EINTR
) {
330 error
.SetErrorToErrno();
338 Status
PipePosix::Write(const void *buf
, size_t size
, size_t &bytes_written
) {
339 std::lock_guard
<std::mutex
> guard(m_write_mutex
);
341 if (!CanWriteUnlocked())
342 return Status(EINVAL
, eErrorTypePOSIX
);
344 const int fd
= GetWriteFileDescriptorUnlocked();
345 SelectHelper select_helper
;
346 select_helper
.SetTimeout(std::chrono::seconds(0));
347 select_helper
.FDSetWrite(fd
);
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
);
356 bytes_written
+= result
;
357 if (bytes_written
== size
)
359 } else if (errno
== EINTR
) {
362 error
.SetErrorToErrno();