]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/tools/lldb/source/Host/posix/PipePosix.cpp
Merge lld trunk r321017 to contrib/llvm/tools/lld.
[FreeBSD/FreeBSD.git] / contrib / llvm / tools / lldb / 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;
131   tmpdir_file_spec.Clear();
132   if (HostInfo::GetLLDBPath(ePathTypeLLDBTempSystemDir, tmpdir_file_spec)) {
133     tmpdir_file_spec.AppendPathComponent(pipe_spec.c_str());
134   } else {
135     tmpdir_file_spec.AppendPathComponent("/tmp");
136     tmpdir_file_spec.AppendPathComponent(pipe_spec.c_str());
137   }
138
139   // It's possible that another process creates the target path after we've
140   // verified it's available but before we create it, in which case we
141   // should try again.
142   Status error;
143   do {
144     llvm::sys::fs::createUniqueFile(tmpdir_file_spec.GetPath(),
145                                     named_pipe_path);
146     error = CreateNew(named_pipe_path, child_process_inherit);
147   } while (error.GetError() == EEXIST);
148
149   if (error.Success())
150     name = named_pipe_path;
151   return error;
152 }
153
154 Status PipePosix::OpenAsReader(llvm::StringRef name,
155                                bool child_process_inherit) {
156   if (CanRead() || CanWrite())
157     return Status("Pipe is already opened");
158
159   int flags = O_RDONLY | O_NONBLOCK;
160   if (!child_process_inherit)
161     flags |= O_CLOEXEC;
162
163   Status error;
164   int fd = ::open(name.data(), flags);
165   if (fd != -1)
166     m_fds[READ] = fd;
167   else
168     error.SetErrorToErrno();
169
170   return error;
171 }
172
173 Status
174 PipePosix::OpenAsWriterWithTimeout(llvm::StringRef name,
175                                    bool child_process_inherit,
176                                    const std::chrono::microseconds &timeout) {
177   if (CanRead() || CanWrite())
178     return Status("Pipe is already opened");
179
180   int flags = O_WRONLY | O_NONBLOCK;
181   if (!child_process_inherit)
182     flags |= O_CLOEXEC;
183
184   using namespace std::chrono;
185   const auto finish_time = Now() + timeout;
186
187   while (!CanWrite()) {
188     if (timeout != microseconds::zero()) {
189       const auto dur = duration_cast<microseconds>(finish_time - Now()).count();
190       if (dur <= 0)
191         return Status("timeout exceeded - reader hasn't opened so far");
192     }
193
194     errno = 0;
195     int fd = ::open(name.data(), flags);
196     if (fd == -1) {
197       const auto errno_copy = errno;
198       // We may get ENXIO if a reader side of the pipe hasn't opened yet.
199       if (errno_copy != ENXIO)
200         return Status(errno_copy, eErrorTypePOSIX);
201
202       std::this_thread::sleep_for(
203           milliseconds(OPEN_WRITER_SLEEP_TIMEOUT_MSECS));
204     } else {
205       m_fds[WRITE] = fd;
206     }
207   }
208
209   return Status();
210 }
211
212 int PipePosix::GetReadFileDescriptor() const { return m_fds[READ]; }
213
214 int PipePosix::GetWriteFileDescriptor() const { return m_fds[WRITE]; }
215
216 int PipePosix::ReleaseReadFileDescriptor() {
217   const int fd = m_fds[READ];
218   m_fds[READ] = PipePosix::kInvalidDescriptor;
219   return fd;
220 }
221
222 int PipePosix::ReleaseWriteFileDescriptor() {
223   const int fd = m_fds[WRITE];
224   m_fds[WRITE] = PipePosix::kInvalidDescriptor;
225   return fd;
226 }
227
228 void PipePosix::Close() {
229   CloseReadFileDescriptor();
230   CloseWriteFileDescriptor();
231 }
232
233 Status PipePosix::Delete(llvm::StringRef name) {
234   return llvm::sys::fs::remove(name);
235 }
236
237 bool PipePosix::CanRead() const {
238   return m_fds[READ] != PipePosix::kInvalidDescriptor;
239 }
240
241 bool PipePosix::CanWrite() const {
242   return m_fds[WRITE] != PipePosix::kInvalidDescriptor;
243 }
244
245 void PipePosix::CloseReadFileDescriptor() {
246   if (CanRead()) {
247     close(m_fds[READ]);
248     m_fds[READ] = PipePosix::kInvalidDescriptor;
249   }
250 }
251
252 void PipePosix::CloseWriteFileDescriptor() {
253   if (CanWrite()) {
254     close(m_fds[WRITE]);
255     m_fds[WRITE] = PipePosix::kInvalidDescriptor;
256   }
257 }
258
259 Status PipePosix::ReadWithTimeout(void *buf, size_t size,
260                                   const std::chrono::microseconds &timeout,
261                                   size_t &bytes_read) {
262   bytes_read = 0;
263   if (!CanRead())
264     return Status(EINVAL, eErrorTypePOSIX);
265
266   const int fd = GetReadFileDescriptor();
267
268   SelectHelper select_helper;
269   select_helper.SetTimeout(timeout);
270   select_helper.FDSetRead(fd);
271
272   Status error;
273   while (error.Success()) {
274     error = select_helper.Select();
275     if (error.Success()) {
276       auto result = ::read(fd, reinterpret_cast<char *>(buf) + bytes_read,
277                            size - bytes_read);
278       if (result != -1) {
279         bytes_read += result;
280         if (bytes_read == size || result == 0)
281           break;
282       } else {
283         error.SetErrorToErrno();
284         break;
285       }
286     }
287   }
288   return error;
289 }
290
291 Status PipePosix::Write(const void *buf, size_t size, size_t &bytes_written) {
292   bytes_written = 0;
293   if (!CanWrite())
294     return Status(EINVAL, eErrorTypePOSIX);
295
296   const int fd = GetWriteFileDescriptor();
297   SelectHelper select_helper;
298   select_helper.SetTimeout(std::chrono::seconds(0));
299   select_helper.FDSetWrite(fd);
300
301   Status error;
302   while (error.Success()) {
303     error = select_helper.Select();
304     if (error.Success()) {
305       auto result =
306           ::write(fd, reinterpret_cast<const char *>(buf) + bytes_written,
307                   size - bytes_written);
308       if (result != -1) {
309         bytes_written += result;
310         if (bytes_written == size)
311           break;
312       } else {
313         error.SetErrorToErrno();
314       }
315     }
316   }
317   return error;
318 }