Revert "[lldb][test] Remove compiler version check and use regex" (#124101)
[llvm-project.git] / lldb / source / Host / posix / PipePosix.cpp
blob24c563d8c24bd2abbcf8321231c3cac0c4c28f10
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 //===----------------------------------------------------------------------===//
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>
18 #include <cerrno>
19 #include <climits>
20 #include <fcntl.h>
21 #include <sys/stat.h>
22 #include <sys/types.h>
23 #include <unistd.h>
25 using namespace lldb;
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__) || defined(__NetBSD__) || \
35 defined(__OpenBSD__)
36 #define PIPE2_SUPPORTED 1
37 #else
38 #define PIPE2_SUPPORTED 0
39 #endif
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);
46 if (flags == -1)
47 return false;
48 return (::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == 0);
50 #endif
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();
75 return *this;
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);
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 = Status::FromErrno();
95 CloseUnlocked();
96 return error;
99 #endif
100 return error;
102 #endif
104 error = Status::FromErrno();
105 m_fds[READ] = PipePosix::kInvalidDescriptor;
106 m_fds[WRITE] = PipePosix::kInvalidDescriptor;
107 return error;
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::FromErrorString("Pipe is already opened");
115 Status error;
116 if (::mkfifo(name.str().c_str(), 0660) != 0)
117 error = Status::FromErrno();
118 return error;
121 Status PipePosix::CreateWithUniqueName(llvm::StringRef prefix,
122 bool child_process_inherit,
123 llvm::SmallVectorImpl<char> &name) {
124 llvm::SmallString<128> named_pipe_path;
125 llvm::SmallString<128> pipe_spec((prefix + ".%%%%%%").str());
126 FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir();
127 if (!tmpdir_file_spec)
128 tmpdir_file_spec.AppendPathComponent("/tmp");
129 tmpdir_file_spec.AppendPathComponent(pipe_spec);
131 // It's possible that another process creates the target path after we've
132 // verified it's available but before we create it, in which case we should
133 // try again.
134 Status error;
135 do {
136 llvm::sys::fs::createUniquePath(tmpdir_file_spec.GetPath(), named_pipe_path,
137 /*MakeAbsolute=*/false);
138 error = CreateNew(named_pipe_path, child_process_inherit);
139 } while (error.GetError() == EEXIST);
141 if (error.Success())
142 name = named_pipe_path;
143 return error;
146 Status PipePosix::OpenAsReader(llvm::StringRef name,
147 bool child_process_inherit) {
148 std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex);
150 if (CanReadUnlocked() || CanWriteUnlocked())
151 return Status::FromErrorString("Pipe is already opened");
153 int flags = O_RDONLY | O_NONBLOCK;
154 if (!child_process_inherit)
155 flags |= O_CLOEXEC;
157 Status error;
158 int fd = FileSystem::Instance().Open(name.str().c_str(), flags);
159 if (fd != -1)
160 m_fds[READ] = fd;
161 else
162 error = Status::FromErrno();
164 return error;
167 Status
168 PipePosix::OpenAsWriterWithTimeout(llvm::StringRef name,
169 bool child_process_inherit,
170 const std::chrono::microseconds &timeout) {
171 std::lock_guard<std::mutex> guard(m_write_mutex);
172 if (CanReadUnlocked() || CanWriteUnlocked())
173 return Status::FromErrorString("Pipe is already opened");
175 int flags = O_WRONLY | O_NONBLOCK;
176 if (!child_process_inherit)
177 flags |= O_CLOEXEC;
179 using namespace std::chrono;
180 const auto finish_time = Now() + timeout;
182 while (!CanWriteUnlocked()) {
183 if (timeout != microseconds::zero()) {
184 const auto dur = duration_cast<microseconds>(finish_time - Now()).count();
185 if (dur <= 0)
186 return Status::FromErrorString(
187 "timeout exceeded - reader hasn't opened so far");
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);
198 std::this_thread::sleep_for(
199 milliseconds(OPEN_WRITER_SLEEP_TIMEOUT_MSECS));
200 } else {
201 m_fds[WRITE] = fd;
205 return Status();
208 int PipePosix::GetReadFileDescriptor() const {
209 std::lock_guard<std::mutex> guard(m_read_mutex);
210 return GetReadFileDescriptorUnlocked();
213 int PipePosix::GetReadFileDescriptorUnlocked() const {
214 return m_fds[READ];
217 int PipePosix::GetWriteFileDescriptor() const {
218 std::lock_guard<std::mutex> guard(m_write_mutex);
219 return GetWriteFileDescriptorUnlocked();
222 int PipePosix::GetWriteFileDescriptorUnlocked() const {
223 return m_fds[WRITE];
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;
234 return fd;
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;
245 return fd;
248 void PipePosix::Close() {
249 std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex);
250 CloseUnlocked();
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()) {
286 close(m_fds[READ]);
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()) {
298 close(m_fds[WRITE]);
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);
307 bytes_read = 0;
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);
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 = Status::FromErrno();
331 break;
335 return error;
338 Status PipePosix::WriteWithTimeout(const void *buf, size_t size,
339 const std::chrono::microseconds &timeout,
340 size_t &bytes_written) {
341 std::lock_guard<std::mutex> guard(m_write_mutex);
342 bytes_written = 0;
343 if (!CanWriteUnlocked())
344 return Status(EINVAL, eErrorTypePOSIX);
346 const int fd = GetWriteFileDescriptorUnlocked();
347 SelectHelper select_helper;
348 select_helper.SetTimeout(timeout);
349 select_helper.FDSetWrite(fd);
351 Status error;
352 while (error.Success()) {
353 error = select_helper.Select();
354 if (error.Success()) {
355 auto result = ::write(fd, static_cast<const char *>(buf) + bytes_written,
356 size - bytes_written);
357 if (result != -1) {
358 bytes_written += result;
359 if (bytes_written == size)
360 break;
361 } else if (errno == EINTR) {
362 continue;
363 } else {
364 error = Status::FromErrno();
368 return error;