1 | //===-- MainLoopPosix.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/MainLoopPosix.h" |
10 | #include "lldb/Host/Config.h" |
11 | #include "lldb/Host/PosixApi.h" |
12 | #include "lldb/Utility/Status.h" |
13 | #include "llvm/Config/llvm-config.h" |
14 | #include "llvm/Support/Errno.h" |
15 | #include <algorithm> |
16 | #include <cassert> |
17 | #include <cerrno> |
18 | #include <chrono> |
19 | #include <csignal> |
20 | #include <ctime> |
21 | #include <fcntl.h> |
22 | #include <vector> |
23 | |
24 | // Multiplexing is implemented using kqueue on systems that support it (BSD |
25 | // variants including OSX). On linux we use ppoll. |
26 | |
27 | #if HAVE_SYS_EVENT_H |
28 | #include <sys/event.h> |
29 | #else |
30 | #include <poll.h> |
31 | #endif |
32 | |
33 | using namespace lldb; |
34 | using namespace lldb_private; |
35 | |
36 | namespace { |
37 | struct GlobalSignalInfo { |
38 | sig_atomic_t pipe_fd = -1; |
39 | static_assert(sizeof(sig_atomic_t) >= sizeof(int), |
40 | "Type too small for a file descriptor" ); |
41 | sig_atomic_t flag = 0; |
42 | }; |
43 | } // namespace |
44 | static GlobalSignalInfo g_signal_info[NSIG]; |
45 | |
46 | static void SignalHandler(int signo, siginfo_t *info, void *) { |
47 | assert(signo < NSIG); |
48 | |
49 | // Set the flag before writing to the pipe! |
50 | g_signal_info[signo].flag = 1; |
51 | |
52 | int fd = g_signal_info[signo].pipe_fd; |
53 | if (fd < 0) { |
54 | // This can happen with the following (unlikely) sequence of events: |
55 | // 1. Thread 1 gets a signal, starts running the signal handler |
56 | // 2. Thread 2 unregisters the signal handler, setting pipe_fd to -1 |
57 | // 3. Signal handler on thread 1 reads -1 out of pipe_fd |
58 | // In this case, we can just ignore the signal because we're no longer |
59 | // interested in it. |
60 | return; |
61 | } |
62 | |
63 | // Write a(ny) character to the pipe to wake up from the poll syscall. |
64 | char c = '.'; |
65 | ssize_t bytes_written = llvm::sys::RetryAfterSignal(Fail: -1, F&: ::write, As: fd, As: &c, As: 1); |
66 | // We can safely ignore EAGAIN (pipe full), as that means poll will definitely |
67 | // return. |
68 | assert(bytes_written == 1 || (bytes_written == -1 && errno == EAGAIN)); |
69 | (void)bytes_written; |
70 | } |
71 | |
72 | class ToTimeSpec { |
73 | public: |
74 | explicit ToTimeSpec(std::optional<MainLoopPosix::TimePoint> point) { |
75 | using namespace std::chrono; |
76 | |
77 | if (!point) { |
78 | m_ts_ptr = nullptr; |
79 | return; |
80 | } |
81 | nanoseconds dur = std::max(a: *point - steady_clock::now(), b: nanoseconds(0)); |
82 | m_ts_ptr = &m_ts; |
83 | m_ts.tv_sec = duration_cast<seconds>(d: dur).count(); |
84 | m_ts.tv_nsec = (dur % seconds(1)).count(); |
85 | } |
86 | ToTimeSpec(const ToTimeSpec &) = delete; |
87 | ToTimeSpec &operator=(const ToTimeSpec &) = delete; |
88 | |
89 | operator struct timespec *() { return m_ts_ptr; } |
90 | |
91 | private: |
92 | struct timespec m_ts; |
93 | struct timespec *m_ts_ptr; |
94 | }; |
95 | |
96 | class MainLoopPosix::RunImpl { |
97 | public: |
98 | RunImpl(MainLoopPosix &loop); |
99 | ~RunImpl() = default; |
100 | |
101 | Status Poll(); |
102 | |
103 | void ProcessReadEvents(); |
104 | |
105 | private: |
106 | MainLoopPosix &loop; |
107 | |
108 | #if HAVE_SYS_EVENT_H |
109 | std::vector<struct kevent> in_events; |
110 | struct kevent out_events[4]; |
111 | int num_events = -1; |
112 | |
113 | #else |
114 | std::vector<struct pollfd> read_fds; |
115 | #endif |
116 | }; |
117 | |
118 | #if HAVE_SYS_EVENT_H |
119 | MainLoopPosix::RunImpl::RunImpl(MainLoopPosix &loop) : loop(loop) { |
120 | in_events.reserve(loop.m_read_fds.size()); |
121 | } |
122 | |
123 | Status MainLoopPosix::RunImpl::Poll() { |
124 | in_events.resize(loop.m_read_fds.size()); |
125 | unsigned i = 0; |
126 | for (auto &fd : loop.m_read_fds) |
127 | EV_SET(&in_events[i++], fd.first, EVFILT_READ, EV_ADD, 0, 0, 0); |
128 | |
129 | num_events = |
130 | kevent(loop.m_kqueue, in_events.data(), in_events.size(), out_events, |
131 | std::size(out_events), ToTimeSpec(loop.GetNextWakeupTime())); |
132 | |
133 | if (num_events < 0) { |
134 | if (errno == EINTR) { |
135 | // in case of EINTR, let the main loop run one iteration |
136 | // we need to zero num_events to avoid assertions failing |
137 | num_events = 0; |
138 | } else |
139 | return Status(errno, eErrorTypePOSIX); |
140 | } |
141 | return Status(); |
142 | } |
143 | |
144 | void MainLoopPosix::RunImpl::ProcessReadEvents() { |
145 | assert(num_events >= 0); |
146 | for (int i = 0; i < num_events; ++i) { |
147 | if (loop.m_terminate_request) |
148 | return; |
149 | switch (out_events[i].filter) { |
150 | case EVFILT_READ: |
151 | loop.ProcessReadObject(out_events[i].ident); |
152 | break; |
153 | default: |
154 | llvm_unreachable("Unknown event" ); |
155 | } |
156 | } |
157 | } |
158 | #else |
159 | MainLoopPosix::RunImpl::RunImpl(MainLoopPosix &loop) : loop(loop) { |
160 | read_fds.reserve(n: loop.m_read_fds.size()); |
161 | } |
162 | |
163 | static int StartPoll(llvm::MutableArrayRef<struct pollfd> fds, |
164 | std::optional<MainLoopPosix::TimePoint> point) { |
165 | #if HAVE_PPOLL |
166 | return ppoll(fds: fds.data(), nfds: fds.size(), timeout: ToTimeSpec(point), |
167 | /*sigmask=*/ss: nullptr); |
168 | #else |
169 | using namespace std::chrono; |
170 | int timeout = -1; |
171 | if (point) { |
172 | nanoseconds dur = std::max(*point - steady_clock::now(), nanoseconds(0)); |
173 | timeout = ceil<milliseconds>(dur).count(); |
174 | } |
175 | return poll(fds.data(), fds.size(), timeout); |
176 | #endif |
177 | } |
178 | |
179 | Status MainLoopPosix::RunImpl::Poll() { |
180 | read_fds.clear(); |
181 | |
182 | for (const auto &fd : loop.m_read_fds) { |
183 | struct pollfd pfd; |
184 | pfd.fd = fd.first; |
185 | pfd.events = POLLIN; |
186 | pfd.revents = 0; |
187 | read_fds.push_back(x: pfd); |
188 | } |
189 | int ready = StartPoll(fds: read_fds, point: loop.GetNextWakeupTime()); |
190 | |
191 | if (ready == -1 && errno != EINTR) |
192 | return Status(errno, eErrorTypePOSIX); |
193 | |
194 | return Status(); |
195 | } |
196 | |
197 | void MainLoopPosix::RunImpl::ProcessReadEvents() { |
198 | for (const auto &fd : read_fds) { |
199 | if ((fd.revents & (POLLIN | POLLHUP)) == 0) |
200 | continue; |
201 | IOObject::WaitableHandle handle = fd.fd; |
202 | if (loop.m_terminate_request) |
203 | return; |
204 | |
205 | loop.ProcessReadObject(handle); |
206 | } |
207 | } |
208 | #endif |
209 | |
210 | MainLoopPosix::MainLoopPosix() { |
211 | Status error = m_interrupt_pipe.CreateNew(/*child_process_inherit=*/false); |
212 | assert(error.Success()); |
213 | |
214 | // Make the write end of the pipe non-blocking. |
215 | int result = fcntl(fd: m_interrupt_pipe.GetWriteFileDescriptor(), F_SETFL, |
216 | fcntl(fd: m_interrupt_pipe.GetWriteFileDescriptor(), F_GETFL) | |
217 | O_NONBLOCK); |
218 | assert(result == 0); |
219 | UNUSED_IF_ASSERT_DISABLED(result); |
220 | |
221 | const int interrupt_pipe_fd = m_interrupt_pipe.GetReadFileDescriptor(); |
222 | m_read_fds.insert( |
223 | KV: {interrupt_pipe_fd, [interrupt_pipe_fd](MainLoopBase &loop) { |
224 | char c; |
225 | ssize_t bytes_read = |
226 | llvm::sys::RetryAfterSignal(Fail: -1, F&: ::read, As: interrupt_pipe_fd, As: &c, As: 1); |
227 | assert(bytes_read == 1); |
228 | UNUSED_IF_ASSERT_DISABLED(bytes_read); |
229 | // NB: This implicitly causes another loop iteration |
230 | // and therefore the execution of pending callbacks. |
231 | }}); |
232 | #if HAVE_SYS_EVENT_H |
233 | m_kqueue = kqueue(); |
234 | assert(m_kqueue >= 0); |
235 | #endif |
236 | } |
237 | |
238 | MainLoopPosix::~MainLoopPosix() { |
239 | #if HAVE_SYS_EVENT_H |
240 | close(m_kqueue); |
241 | #endif |
242 | m_read_fds.erase(Val: m_interrupt_pipe.GetReadFileDescriptor()); |
243 | m_interrupt_pipe.Close(); |
244 | assert(m_read_fds.size() == 0); |
245 | assert(m_signals.size() == 0); |
246 | } |
247 | |
248 | MainLoopPosix::ReadHandleUP |
249 | MainLoopPosix::RegisterReadObject(const IOObjectSP &object_sp, |
250 | const Callback &callback, Status &error) { |
251 | if (!object_sp || !object_sp->IsValid()) { |
252 | error = Status::FromErrorString(str: "IO object is not valid." ); |
253 | return nullptr; |
254 | } |
255 | |
256 | const bool inserted = |
257 | m_read_fds.insert(KV: {object_sp->GetWaitableHandle(), callback}).second; |
258 | if (!inserted) { |
259 | error = Status::FromErrorStringWithFormat( |
260 | format: "File descriptor %d already monitored." , |
261 | object_sp->GetWaitableHandle()); |
262 | return nullptr; |
263 | } |
264 | |
265 | return CreateReadHandle(object_sp); |
266 | } |
267 | |
268 | // We shall block the signal, then install the signal handler. The signal will |
269 | // be unblocked in the Run() function to check for signal delivery. |
270 | MainLoopPosix::SignalHandleUP |
271 | MainLoopPosix::RegisterSignal(int signo, const Callback &callback, |
272 | Status &error) { |
273 | auto signal_it = m_signals.find(Val: signo); |
274 | if (signal_it != m_signals.end()) { |
275 | auto callback_it = signal_it->second.callbacks.insert( |
276 | position: signal_it->second.callbacks.end(), x: callback); |
277 | return SignalHandleUP(new SignalHandle(*this, signo, callback_it)); |
278 | } |
279 | |
280 | SignalInfo info; |
281 | info.callbacks.push_back(x: callback); |
282 | struct sigaction new_action; |
283 | new_action.sa_sigaction = &SignalHandler; |
284 | new_action.sa_flags = SA_SIGINFO; |
285 | sigemptyset(set: &new_action.sa_mask); |
286 | sigaddset(set: &new_action.sa_mask, signo: signo); |
287 | sigset_t old_set; |
288 | |
289 | // Set signal info before installing the signal handler! |
290 | g_signal_info[signo].pipe_fd = m_interrupt_pipe.GetWriteFileDescriptor(); |
291 | g_signal_info[signo].flag = 0; |
292 | |
293 | int ret = sigaction(sig: signo, act: &new_action, oact: &info.old_action); |
294 | UNUSED_IF_ASSERT_DISABLED(ret); |
295 | assert(ret == 0 && "sigaction failed" ); |
296 | |
297 | ret = pthread_sigmask(SIG_UNBLOCK, newmask: &new_action.sa_mask, oldmask: &old_set); |
298 | assert(ret == 0 && "pthread_sigmask failed" ); |
299 | info.was_blocked = sigismember(set: &old_set, signo: signo); |
300 | auto insert_ret = m_signals.insert(KV: {signo, info}); |
301 | |
302 | return SignalHandleUP(new SignalHandle( |
303 | *this, signo, insert_ret.first->second.callbacks.begin())); |
304 | } |
305 | |
306 | void MainLoopPosix::UnregisterReadObject(IOObject::WaitableHandle handle) { |
307 | bool erased = m_read_fds.erase(Val: handle); |
308 | UNUSED_IF_ASSERT_DISABLED(erased); |
309 | assert(erased); |
310 | } |
311 | |
312 | void MainLoopPosix::UnregisterSignal( |
313 | int signo, std::list<Callback>::iterator callback_it) { |
314 | auto it = m_signals.find(Val: signo); |
315 | assert(it != m_signals.end()); |
316 | |
317 | it->second.callbacks.erase(position: callback_it); |
318 | // Do not remove the signal handler unless all callbacks have been erased. |
319 | if (!it->second.callbacks.empty()) |
320 | return; |
321 | |
322 | sigaction(sig: signo, act: &it->second.old_action, oact: nullptr); |
323 | |
324 | sigset_t set; |
325 | sigemptyset(set: &set); |
326 | sigaddset(set: &set, signo: signo); |
327 | int ret = pthread_sigmask(how: it->second.was_blocked ? SIG_BLOCK : SIG_UNBLOCK, |
328 | newmask: &set, oldmask: nullptr); |
329 | assert(ret == 0); |
330 | UNUSED_IF_ASSERT_DISABLED(ret); |
331 | |
332 | m_signals.erase(I: it); |
333 | g_signal_info[signo] = {}; |
334 | } |
335 | |
336 | Status MainLoopPosix::Run() { |
337 | m_terminate_request = false; |
338 | |
339 | Status error; |
340 | RunImpl impl(*this); |
341 | |
342 | while (!m_terminate_request) { |
343 | error = impl.Poll(); |
344 | if (error.Fail()) |
345 | return error; |
346 | |
347 | impl.ProcessReadEvents(); |
348 | |
349 | ProcessSignals(); |
350 | |
351 | m_interrupting = false; |
352 | ProcessCallbacks(); |
353 | } |
354 | return Status(); |
355 | } |
356 | |
357 | void MainLoopPosix::ProcessReadObject(IOObject::WaitableHandle handle) { |
358 | auto it = m_read_fds.find(Val: handle); |
359 | if (it != m_read_fds.end()) |
360 | it->second(*this); // Do the work |
361 | } |
362 | |
363 | void MainLoopPosix::ProcessSignals() { |
364 | std::vector<int> signals; |
365 | for (const auto &entry : m_signals) |
366 | if (g_signal_info[entry.first].flag != 0) |
367 | signals.push_back(x: entry.first); |
368 | |
369 | for (const auto &signal : signals) { |
370 | if (m_terminate_request) |
371 | return; |
372 | |
373 | g_signal_info[signal].flag = 0; |
374 | ProcessSignal(signo: signal); |
375 | } |
376 | } |
377 | |
378 | void MainLoopPosix::ProcessSignal(int signo) { |
379 | auto it = m_signals.find(Val: signo); |
380 | if (it != m_signals.end()) { |
381 | // The callback may actually register/unregister signal handlers, |
382 | // so we need to create a copy first. |
383 | llvm::SmallVector<Callback, 4> callbacks_to_run{ |
384 | it->second.callbacks.begin(), it->second.callbacks.end()}; |
385 | for (auto &x : callbacks_to_run) |
386 | x(*this); // Do the work |
387 | } |
388 | } |
389 | |
390 | void MainLoopPosix::Interrupt() { |
391 | if (m_interrupting.exchange(i: true)) |
392 | return; |
393 | |
394 | char c = '.'; |
395 | cantFail(ValOrErr: m_interrupt_pipe.Write(buf: &c, size: 1)); |
396 | } |
397 | |