]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - source/Host/posix/PipePosix.cpp
Vendor import of lldb trunk r338150:
[FreeBSD/FreeBSD.git] / source / Host / posix / PipePosix.cpp
1 //===-- PipePosix.cpp -------------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #include "lldb/Host/posix/PipePosix.h"
11 #include "lldb/Host/HostInfo.h"
12 #include "lldb/Utility/SelectHelper.h"
13 #include "llvm/ADT/SmallString.h"
14 #include "llvm/Support/FileSystem.h"
15
16 #if defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8))
17 #ifndef _GLIBCXX_USE_NANOSLEEP
18 #define _GLIBCXX_USE_NANOSLEEP
19 #endif
20 #endif
21
22 #include <functional>
23 #include <thread>
24
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <limits.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <unistd.h>
31
32 using namespace lldb;
33 using namespace lldb_private;
34
35 int PipePosix::kInvalidDescriptor = -1;
36
37 enum PIPES { READ, WRITE }; // Constants 0 and 1 for READ and WRITE
38
39 // pipe2 is supported by a limited set of platforms
40 // TODO: Add more platforms that support pipe2.
41 #if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD__ >= 10) ||       \
42     defined(__NetBSD__)
43 #define PIPE2_SUPPORTED 1
44 #else
45 #define PIPE2_SUPPORTED 0
46 #endif
47
48 namespace {
49
50 constexpr auto OPEN_WRITER_SLEEP_TIMEOUT_MSECS = 100;
51
52 #if defined(FD_CLOEXEC) && !PIPE2_SUPPORTED
53 bool SetCloexecFlag(int fd) {
54   int flags = ::fcntl(fd, F_GETFD);
55   if (flags == -1)
56     return false;
57   return (::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == 0);
58 }
59 #endif
60
61 std::chrono::time_point<std::chrono::steady_clock> Now() {
62   return std::chrono::steady_clock::now();
63 }
64 }
65
66 PipePosix::PipePosix()
67     : m_fds{PipePosix::kInvalidDescriptor, PipePosix::kInvalidDescriptor} {}
68
69 PipePosix::PipePosix(int read_fd, int write_fd) : m_fds{read_fd, write_fd} {}
70
71 PipePosix::PipePosix(PipePosix &&pipe_posix)
72     : PipeBase{std::move(pipe_posix)},
73       m_fds{pipe_posix.ReleaseReadFileDescriptor(),
74             pipe_posix.ReleaseWriteFileDescriptor()} {}
75
76 PipePosix &PipePosix::operator=(PipePosix &&pipe_posix) {
77   PipeBase::operator=(std::move(pipe_posix));
78   m_fds[READ] = pipe_posix.ReleaseReadFileDescriptor();
79   m_fds[WRITE] = pipe_posix.ReleaseWriteFileDescriptor();
80   return *this;
81 }
82
83 PipePosix::~PipePosix() { Close(); }
84
85 Status PipePosix::CreateNew(bool child_processes_inherit) {
86   if (CanRead() || CanWrite())
87     return Status(EINVAL, eErrorTypePOSIX);
88
89   Status error;
90 #if PIPE2_SUPPORTED
91   if (::pipe2(m_fds, (child_processes_inherit) ? 0 : O_CLOEXEC) == 0)
92     return error;
93 #else
94   if (::pipe(m_fds) == 0) {
95 #ifdef FD_CLOEXEC
96     if (!child_processes_inherit) {
97       if (!SetCloexecFlag(m_fds[0]) || !SetCloexecFlag(m_fds[1])) {
98         error.SetErrorToErrno();
99         Close();
100         return error;
101       }
102     }
103 #endif
104     return error;
105   }
106 #endif
107
108   error.SetErrorToErrno();
109   m_fds[READ] = PipePosix::kInvalidDescriptor;
110   m_fds[WRITE] = PipePosix::kInvalidDescriptor;
111   return error;
112 }
113
114 Status PipePosix::CreateNew(llvm::StringRef name, bool child_process_inherit) {
115   if (CanRead() || CanWrite())
116     return Status("Pipe is already opened");
117
118   Status error;
119   if (::mkfifo(name.data(), 0660) != 0)
120     error.SetErrorToErrno();
121
122   return error;
123 }
124
125 Status PipePosix::CreateWithUniqueName(llvm::StringRef prefix,
126                                        bool child_process_inherit,
127                                        llvm::SmallVectorImpl<char> &name) {
128   llvm::SmallString<PATH_MAX> named_pipe_path;
129   llvm::SmallString<PATH_MAX> pipe_spec((prefix + ".%%%%%%").str());
130   FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir();
131   if (!tmpdir_file_spec)
132     tmpdir_file_spec.AppendPathComponent("/tmp");
133   tmpdir_file_spec.AppendPathComponent(pipe_spec);
134
135   // It's possible that another process creates the target path after we've
136   // verified it's available but before we create it, in which case we should
137   // try again.
138   Status error;
139   do {
140     llvm::sys::fs::createUniqueFile(tmpdir_file_spec.GetPath(),
141                                     named_pipe_path);
142     error = CreateNew(named_pipe_path, child_process_inherit);
143   } while (error.GetError() == EEXIST);
144
145   if (error.Success())
146     name = named_pipe_path;
147   return error;
148 }
149
150 Status PipePosix::OpenAsReader(llvm::StringRef name,
151                                bool child_process_inherit) {
152   if (CanRead() || CanWrite())
153     return Status("Pipe is already opened");
154
155   int flags = O_RDONLY | O_NONBLOCK;
156   if (!child_process_inherit)
157     flags |= O_CLOEXEC;
158
159   Status error;
160   int fd = ::open(name.data(), flags);
161   if (fd != -1)
162     m_fds[READ] = fd;
163   else
164     error.SetErrorToErrno();
165
166   return error;
167 }
168
169 Status
170 PipePosix::OpenAsWriterWithTimeout(llvm::StringRef name,
171                                    bool child_process_inherit,
172                                    const std::chrono::microseconds &timeout) {
173   if (CanRead() || CanWrite())
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 (!CanWrite()) {
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.data(), 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)
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 { return m_fds[READ]; }
209
210 int PipePosix::GetWriteFileDescriptor() const { return m_fds[WRITE]; }
211
212 int PipePosix::ReleaseReadFileDescriptor() {
213   const int fd = m_fds[READ];
214   m_fds[READ] = PipePosix::kInvalidDescriptor;
215   return fd;
216 }
217
218 int PipePosix::ReleaseWriteFileDescriptor() {
219   const int fd = m_fds[WRITE];
220   m_fds[WRITE] = PipePosix::kInvalidDescriptor;
221   return fd;
222 }
223
224 void PipePosix::Close() {
225   CloseReadFileDescriptor();
226   CloseWriteFileDescriptor();
227 }
228
229 Status PipePosix::Delete(llvm::StringRef name) {
230   return llvm::sys::fs::remove(name);
231 }
232
233 bool PipePosix::CanRead() const {
234   return m_fds[READ] != PipePosix::kInvalidDescriptor;
235 }
236
237 bool PipePosix::CanWrite() const {
238   return m_fds[WRITE] != PipePosix::kInvalidDescriptor;
239 }
240
241 void PipePosix::CloseReadFileDescriptor() {
242   if (CanRead()) {
243     close(m_fds[READ]);
244     m_fds[READ] = PipePosix::kInvalidDescriptor;
245   }
246 }
247
248 void PipePosix::CloseWriteFileDescriptor() {
249   if (CanWrite()) {
250     close(m_fds[WRITE]);
251     m_fds[WRITE] = PipePosix::kInvalidDescriptor;
252   }
253 }
254
255 Status PipePosix::ReadWithTimeout(void *buf, size_t size,
256                                   const std::chrono::microseconds &timeout,
257                                   size_t &bytes_read) {
258   bytes_read = 0;
259   if (!CanRead())
260     return Status(EINVAL, eErrorTypePOSIX);
261
262   const int fd = GetReadFileDescriptor();
263
264   SelectHelper select_helper;
265   select_helper.SetTimeout(timeout);
266   select_helper.FDSetRead(fd);
267
268   Status error;
269   while (error.Success()) {
270     error = select_helper.Select();
271     if (error.Success()) {
272       auto result = ::read(fd, reinterpret_cast<char *>(buf) + bytes_read,
273                            size - bytes_read);
274       if (result != -1) {
275         bytes_read += result;
276         if (bytes_read == size || result == 0)
277           break;
278       } else {
279         error.SetErrorToErrno();
280         break;
281       }
282     }
283   }
284   return error;
285 }
286
287 Status PipePosix::Write(const void *buf, size_t size, size_t &bytes_written) {
288   bytes_written = 0;
289   if (!CanWrite())
290     return Status(EINVAL, eErrorTypePOSIX);
291
292   const int fd = GetWriteFileDescriptor();
293   SelectHelper select_helper;
294   select_helper.SetTimeout(std::chrono::seconds(0));
295   select_helper.FDSetWrite(fd);
296
297   Status error;
298   while (error.Success()) {
299     error = select_helper.Select();
300     if (error.Success()) {
301       auto result =
302           ::write(fd, reinterpret_cast<const char *>(buf) + bytes_written,
303                   size - bytes_written);
304       if (result != -1) {
305         bytes_written += result;
306         if (bytes_written == size)
307           break;
308       } else {
309         error.SetErrorToErrno();
310       }
311     }
312   }
313   return error;
314 }