blob: 5e758b22530ca0c5370e6de5e1d125ab33c29e83 [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2017 The Chromium Authors
Scott Grahamf5902f842017-05-26 02:02:102// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/message_loop/message_pump_fuchsia.h"
6
Sergey Ulanovb229d18d2018-10-26 00:02:347#include <lib/async-loop/cpp/loop.h>
Wez6d0d6ec2019-08-29 01:59:558#include <lib/async-loop/default.h>
Wez5c3c6f152018-06-09 18:24:029#include <lib/fdio/io.h>
Wez65ed7a82018-10-17 21:20:3810#include <lib/fdio/unsafe.h>
Sergey Ulanovb229d18d2018-10-26 00:02:3411#include <lib/zx/time.h>
Tamir Dubersteinb8f8c6242021-04-12 18:10:3612#include <zircon/errors.h>
Scott Graham6f68b982017-06-07 23:02:3213
Sergey Ulanovf1343702017-08-19 01:02:0814#include "base/auto_reset.h"
Adam Riced47f4982024-09-03 16:18:2315#include "base/check.h"
Sergey Ulanov66f866c2018-04-17 18:18:2516#include "base/fuchsia/fuchsia_logging.h"
Scott Grahamf5902f842017-05-26 02:02:1017#include "base/logging.h"
Etienne Pierre-dorayfc7952f02025-06-06 00:04:3318#include "base/trace_event/trace_event.h"
Scott Grahamf5902f842017-05-26 02:02:1019
20namespace base {
21
Scott Grahamfe0e9f462017-09-18 21:25:0422MessagePumpFuchsia::ZxHandleWatchController::ZxHandleWatchController(
Brett Wilson8e88b312017-09-12 05:22:1623 const Location& from_here)
Sergey Ulanov66f866c2018-04-17 18:18:2524 : async_wait_t({}), created_from_location_(from_here) {}
Scott Grahamf5902f842017-05-26 02:02:1025
Scott Grahamfe0e9f462017-09-18 21:25:0426MessagePumpFuchsia::ZxHandleWatchController::~ZxHandleWatchController() {
Adam Riced47f4982024-09-03 16:18:2327 const bool success = StopWatchingZxHandle();
28 CHECK(success);
Scott Graham6f68b982017-06-07 23:02:3229}
Scott Grahamf5902f842017-05-26 02:02:1030
Sergey Ulanov66f866c2018-04-17 18:18:2531bool MessagePumpFuchsia::ZxHandleWatchController::WaitBegin() {
32 DCHECK(!handler);
33 async_wait_t::handler = &HandleSignal;
34
Sergey Ulanovb229d18d2018-10-26 00:02:3435 zx_status_t status =
36 async_begin_wait(weak_pump_->async_loop_->dispatcher(), this);
Sergey Ulanov66f866c2018-04-17 18:18:2537 if (status != ZX_OK) {
Wez7415ecf52019-02-27 23:20:5538 ZX_DLOG(ERROR, status) << "async_begin_wait():"
39 << created_from_location_.ToString();
Sergey Ulanov66f866c2018-04-17 18:18:2540 async_wait_t::handler = nullptr;
41 return false;
42 }
43
44 return true;
45}
46
Scott Grahamfe0e9f462017-09-18 21:25:0447bool MessagePumpFuchsia::ZxHandleWatchController::StopWatchingZxHandle() {
Sergey Ulanov2e69d6b22017-08-12 17:22:5748 if (was_stopped_) {
49 DCHECK(!*was_stopped_);
50 *was_stopped_ = true;
51
52 // |was_stopped_| points at a value stored on the stack, which will go out
53 // of scope. MessagePumpFuchsia::Run() will reset it only if the value is
54 // false. So we need to reset this pointer here as well, to make sure it's
55 // not used again.
56 was_stopped_ = nullptr;
57 }
Wez9a7c7272017-08-10 06:29:2758
Sergey Ulanov0357c5b2017-08-15 22:52:4459 // If the pump is gone then there is nothing to cancel.
Peter Kasting134ef9af2024-12-28 02:30:0960 if (!weak_pump_) {
Wez14e88d12017-07-12 01:51:0061 return true;
Peter Kasting134ef9af2024-12-28 02:30:0962 }
Wez5289ff32017-07-18 00:17:1263
Peter Kasting134ef9af2024-12-28 02:30:0964 if (!is_active()) {
Sergey Ulanov66f866c2018-04-17 18:18:2565 return true;
Peter Kasting134ef9af2024-12-28 02:30:0966 }
Wez9a7c7272017-08-10 06:29:2767
Sergey Ulanov66f866c2018-04-17 18:18:2568 async_wait_t::handler = nullptr;
69
Sergey Ulanovb229d18d2018-10-26 00:02:3470 zx_status_t result =
71 async_cancel_wait(weak_pump_->async_loop_->dispatcher(), this);
Wez7415ecf52019-02-27 23:20:5572 ZX_DLOG_IF(ERROR, result != ZX_OK, result)
73 << "async_cancel_wait(): " << created_from_location_.ToString();
Scott Grahamfe0e9f462017-09-18 21:25:0474 return result == ZX_OK;
Scott Grahamf5902f842017-05-26 02:02:1075}
76
Sergey Ulanov66f866c2018-04-17 18:18:2577// static
78void MessagePumpFuchsia::ZxHandleWatchController::HandleSignal(
Wez7f5e4842018-07-22 12:09:3279 async_dispatcher_t* async,
Sergey Ulanov66f866c2018-04-17 18:18:2580 async_wait_t* wait,
81 zx_status_t status,
82 const zx_packet_signal_t* signal) {
Sergey Ulanov66f866c2018-04-17 18:18:2583 ZxHandleWatchController* controller =
84 static_cast<ZxHandleWatchController*>(wait);
85 DCHECK_EQ(controller->handler, &HandleSignal);
Wez7415ecf52019-02-27 23:20:5586
Gabriel Charette37b6f6b22023-07-12 14:54:1187 // Inform ThreadController of this native work item for tracking purposes.
88 // This call must precede the "ZxHandleSignal" trace event below as
89 // BeginWorkItem() might generate a top-level trace event for the thread if
90 // this is the first work item post-wakeup.
91 Delegate::ScopedDoWorkItem scoped_do_work_item;
92 if (controller->weak_pump_ && controller->weak_pump_->run_state_) {
93 scoped_do_work_item =
94 controller->weak_pump_->run_state_->delegate->BeginWorkItem();
95 }
96
97 TRACE_EVENT0("toplevel", "ZxHandleSignal");
98
Wez7415ecf52019-02-27 23:20:5599 if (status != ZX_OK) {
100 ZX_DLOG(WARNING, status) << "async wait failed: "
101 << controller->created_from_location_.ToString();
102 return;
103 }
104
Sergey Ulanov66f866c2018-04-17 18:18:25105 controller->handler = nullptr;
106
Sergey Ulanov66f866c2018-04-17 18:18:25107 // In the case of a persistent Watch, the Watch may be stopped and
108 // potentially deleted by the caller within the callback, in which case
109 // |controller| should not be accessed again, and we mustn't continue the
110 // watch. We check for this with a bool on the stack, which the Watch
111 // receives a pointer to.
112 bool was_stopped = false;
113 controller->was_stopped_ = &was_stopped;
114
Tamir Duberstein2aa51e52019-07-11 22:11:00115 controller->watcher_->OnZxHandleSignalled(wait->object, signal->observed);
Sergey Ulanov66f866c2018-04-17 18:18:25116
Peter Kasting134ef9af2024-12-28 02:30:09117 if (was_stopped) {
Sergey Ulanov66f866c2018-04-17 18:18:25118 return;
Peter Kasting134ef9af2024-12-28 02:30:09119 }
Sergey Ulanov66f866c2018-04-17 18:18:25120
121 controller->was_stopped_ = nullptr;
122
Peter Kasting134ef9af2024-12-28 02:30:09123 if (controller->persistent_) {
Sergey Ulanov66f866c2018-04-17 18:18:25124 controller->WaitBegin();
Peter Kasting134ef9af2024-12-28 02:30:09125 }
Sergey Ulanov66f866c2018-04-17 18:18:25126}
127
Scott Grahamfe0e9f462017-09-18 21:25:04128void MessagePumpFuchsia::FdWatchController::OnZxHandleSignalled(
129 zx_handle_t handle,
130 zx_signals_t signals) {
Wez9a7c7272017-08-10 06:29:27131 uint32_t events;
Wez65ed7a82018-10-17 21:20:38132 fdio_unsafe_wait_end(io_, signals, &events);
Wez9a7c7272017-08-10 06:29:27133
Tamir Duberstein2aa51e52019-07-11 22:11:00134 // |events| can include other spurious things, in particular, that an fd
135 // is writable, when we only asked to know when it was readable. In that
136 // case, we don't want to call both the CanWrite and CanRead callback,
137 // when the caller asked for only, for example, readable callbacks. So,
138 // mask with the events that we actually wanted to know about.
Tamir Duberstein7358b28c2021-04-12 23:06:54139 //
140 // Note that errors are always included.
141 const uint32_t desired_events = desired_events_ | FDIO_EVT_ERROR;
142 const uint32_t filtered_events = events & desired_events;
143 DCHECK_NE(filtered_events, 0u) << events << " & " << desired_events;
Tamir Duberstein2aa51e52019-07-11 22:11:00144
Sergey Ulanov2e69d6b22017-08-12 17:22:57145 // Each |watcher_| callback we invoke may stop or delete |this|. The pump has
146 // set |was_stopped_| to point to a safe location on the calling stack, so we
147 // can use that to detect being stopped mid-callback and avoid doing further
148 // work that would touch |this|.
149 bool* was_stopped = was_stopped_;
Peter Kasting134ef9af2024-12-28 02:30:09150 if (filtered_events & FDIO_EVT_WRITABLE) {
Wez9a7c7272017-08-10 06:29:27151 watcher_->OnFileCanWriteWithoutBlocking(fd_);
Peter Kasting134ef9af2024-12-28 02:30:09152 }
153 if (!*was_stopped && (filtered_events & FDIO_EVT_READABLE)) {
Wez9a7c7272017-08-10 06:29:27154 watcher_->OnFileCanReadWithoutBlocking(fd_);
Peter Kasting134ef9af2024-12-28 02:30:09155 }
Wez9a7c7272017-08-10 06:29:27156
Sergey Ulanov2e69d6b22017-08-12 17:22:57157 // Don't add additional work here without checking |*was_stopped_| again.
Wez9a7c7272017-08-10 06:29:27158}
159
160MessagePumpFuchsia::FdWatchController::FdWatchController(
Brett Wilson8e88b312017-09-12 05:22:16161 const Location& from_here)
Gabriel Charette60a2b0082018-04-09 23:47:02162 : FdWatchControllerInterface(from_here),
163 ZxHandleWatchController(from_here) {}
Wez9a7c7272017-08-10 06:29:27164
165MessagePumpFuchsia::FdWatchController::~FdWatchController() {
Adam Riced47f4982024-09-03 16:18:23166 const bool success = StopWatchingFileDescriptor();
167 CHECK(success);
Wez9a7c7272017-08-10 06:29:27168}
169
Sergey Ulanov66f866c2018-04-17 18:18:25170bool MessagePumpFuchsia::FdWatchController::WaitBegin() {
Sergey Ulanov6e54689e2020-05-05 04:00:04171 // Refresh the |handle_| and |desired_signals_| from the fdio for the fd.
Sergey Ulanov66f866c2018-04-17 18:18:25172 // Some types of fdio map read/write events to different signals depending on
173 // their current state, so we must do this every time we begin to wait.
Wez65ed7a82018-10-17 21:20:38174 fdio_unsafe_wait_begin(io_, desired_events_, &object, &trigger);
Sergey Ulanov66f866c2018-04-17 18:18:25175 if (async_wait_t::object == ZX_HANDLE_INVALID) {
Wez7415ecf52019-02-27 23:20:55176 DLOG(ERROR) << "fdio_wait_begin failed: "
177 << ZxHandleWatchController::created_from_location_.ToString();
Sergey Ulanov66f866c2018-04-17 18:18:25178 return false;
179 }
180
181 return MessagePumpFuchsia::ZxHandleWatchController::WaitBegin();
182}
183
Wez9a7c7272017-08-10 06:29:27184bool MessagePumpFuchsia::FdWatchController::StopWatchingFileDescriptor() {
Scott Grahamfe0e9f462017-09-18 21:25:04185 bool success = StopWatchingZxHandle();
Wez9a7c7272017-08-10 06:29:27186 if (io_) {
Wez65ed7a82018-10-17 21:20:38187 fdio_unsafe_release(io_);
Wez9a7c7272017-08-10 06:29:27188 io_ = nullptr;
189 }
190 return success;
191}
192
Sergey Ulanovb229d18d2018-10-26 00:02:34193MessagePumpFuchsia::MessagePumpFuchsia()
Wez6d0d6ec2019-08-29 01:59:55194 : async_loop_(new async::Loop(&kAsyncLoopConfigAttachToCurrentThread)),
Sergey Ulanovb229d18d2018-10-26 00:02:34195 weak_factory_(this) {}
Sergey Ulanov66f866c2018-04-17 18:18:25196MessagePumpFuchsia::~MessagePumpFuchsia() = default;
Scott Graham80380842017-10-25 04:01:02197
Scott Grahamf5902f842017-05-26 02:02:10198bool MessagePumpFuchsia::WatchFileDescriptor(int fd,
199 bool persistent,
200 int mode,
Wez9a7c7272017-08-10 06:29:27201 FdWatchController* controller,
202 FdWatcher* delegate) {
Scott Graham6f68b982017-06-07 23:02:32203 DCHECK_GE(fd, 0);
204 DCHECK(controller);
205 DCHECK(delegate);
Wez5289ff32017-07-18 00:17:12206
Adam Riced47f4982024-09-03 16:18:23207 const bool success = controller->StopWatchingFileDescriptor();
208 CHECK(success);
Wez9a7c7272017-08-10 06:29:27209
Wez5289ff32017-07-18 00:17:12210 controller->fd_ = fd;
James Robinsonf53b4e72017-06-29 20:59:55211 controller->watcher_ = delegate;
James Robinsonf53b4e72017-06-29 20:59:55212
Wez9a7c7272017-08-10 06:29:27213 DCHECK(!controller->io_);
Wez65ed7a82018-10-17 21:20:38214 controller->io_ = fdio_unsafe_fd_to_io(fd);
Wez14e88d12017-07-12 01:51:00215 if (!controller->io_) {
216 DLOG(ERROR) << "Failed to get IO for FD";
Scott Graham0607b792017-06-14 22:24:46217 return false;
Wez14e88d12017-07-12 01:51:00218 }
Scott Graham0607b792017-06-14 22:24:46219
Wez9a7c7272017-08-10 06:29:27220 switch (mode) {
221 case WATCH_READ:
Wez088e95612018-11-03 06:08:28222 controller->desired_events_ = FDIO_EVT_READABLE;
Wez9a7c7272017-08-10 06:29:27223 break;
224 case WATCH_WRITE:
Scott Grahamfe0e9f462017-09-18 21:25:04225 controller->desired_events_ = FDIO_EVT_WRITABLE;
Wez9a7c7272017-08-10 06:29:27226 break;
227 case WATCH_READ_WRITE:
Wez088e95612018-11-03 06:08:28228 controller->desired_events_ = FDIO_EVT_READABLE | FDIO_EVT_WRITABLE;
Wez9a7c7272017-08-10 06:29:27229 break;
230 default:
Peter Boströmde573332024-08-26 20:42:45231 NOTREACHED() << "unexpected mode: " << mode;
Wez9a7c7272017-08-10 06:29:27232 }
Scott Graham6f68b982017-06-07 23:02:32233
Scott Grahamfe0e9f462017-09-18 21:25:04234 // Pass dummy |handle| and |signals| values to WatchZxHandle(). The real
Wez9a7c7272017-08-10 06:29:27235 // values will be populated by FdWatchController::WaitBegin(), before actually
236 // starting the wait operation.
Scott Grahamfe0e9f462017-09-18 21:25:04237 return WatchZxHandle(ZX_HANDLE_INVALID, persistent, 1, controller,
Wez9a7c7272017-08-10 06:29:27238 controller);
James Robinsonf53b4e72017-06-29 20:59:55239}
240
Scott Grahamfe0e9f462017-09-18 21:25:04241bool MessagePumpFuchsia::WatchZxHandle(zx_handle_t handle,
Wez9a7c7272017-08-10 06:29:27242 bool persistent,
Scott Grahamfe0e9f462017-09-18 21:25:04243 zx_signals_t signals,
244 ZxHandleWatchController* controller,
245 ZxHandleWatcher* delegate) {
Wez9a7c7272017-08-10 06:29:27246 DCHECK_NE(0u, signals);
247 DCHECK(controller);
248 DCHECK(delegate);
Sergey Ulanov6e54689e2020-05-05 04:00:04249
250 // If the watch controller is active then WatchZxHandle() can be called only
251 // for the same handle.
252 DCHECK(handle == ZX_HANDLE_INVALID || !controller->is_active() ||
Sergey Ulanov66f866c2018-04-17 18:18:25253 handle == controller->async_wait_t::object);
Wez9a7c7272017-08-10 06:29:27254
Adam Riced47f4982024-09-03 16:18:23255 const bool success = controller->StopWatchingZxHandle();
256 CHECK(success);
Wez9a7c7272017-08-10 06:29:27257
Sergey Ulanov66f866c2018-04-17 18:18:25258 controller->async_wait_t::object = handle;
Wez9a7c7272017-08-10 06:29:27259 controller->persistent_ = persistent;
Sergey Ulanov66f866c2018-04-17 18:18:25260 controller->async_wait_t::trigger = signals;
Wez9a7c7272017-08-10 06:29:27261 controller->watcher_ = delegate;
262
263 controller->weak_pump_ = weak_factory_.GetWeakPtr();
264
265 return controller->WaitBegin();
266}
267
Francois Dorayc1c66c62020-03-18 15:27:49268bool MessagePumpFuchsia::HandleIoEventsUntil(zx_time_t deadline) {
Sergey Ulanovb229d18d2018-10-26 00:02:34269 zx_status_t status = async_loop_->Run(zx::time(deadline), /*once=*/true);
Sergey Ulanov66f866c2018-04-17 18:18:25270 switch (status) {
271 // Return true if some tasks or events were dispatched or if the dispatcher
272 // was stopped by ScheduleWork().
273 case ZX_OK:
Sergey Ulanovb229d18d2018-10-26 00:02:34274 return true;
275
Sergey Ulanov66f866c2018-04-17 18:18:25276 case ZX_ERR_CANCELED:
Sergey Ulanovb229d18d2018-10-26 00:02:34277 async_loop_->ResetQuit();
Sergey Ulanov66f866c2018-04-17 18:18:25278 return true;
Sergey Ulanovb2fd4ab52017-09-09 00:32:17279
Sergey Ulanov66f866c2018-04-17 18:18:25280 case ZX_ERR_TIMED_OUT:
281 return false;
Sergey Ulanovb2fd4ab52017-09-09 00:32:17282
Sergey Ulanov66f866c2018-04-17 18:18:25283 default:
Peter Boström29c761792024-01-18 23:05:29284 ZX_DLOG(FATAL, status) << "unexpected wait status";
Sergey Ulanov66f866c2018-04-17 18:18:25285 return false;
Sergey Ulanovb2fd4ab52017-09-09 00:32:17286 }
Sergey Ulanovb2fd4ab52017-09-09 00:32:17287}
288
Scott Grahamf5902f842017-05-26 02:02:10289void MessagePumpFuchsia::Run(Delegate* delegate) {
Gabriel Charette37b6f6b22023-07-12 14:54:11290 RunState run_state(delegate);
291 AutoReset<RunState*> auto_reset_run_state(&run_state_, &run_state);
Scott Grahamf2402372017-05-29 23:47:07292
293 for (;;) {
Etienne Pierre-doray2163f3012020-04-02 21:37:14294 const Delegate::NextWorkInfo next_work_info = delegate->DoWork();
Gabriel Charette37b6f6b22023-07-12 14:54:11295 if (run_state.should_quit) {
Scott Grahamf2402372017-05-29 23:47:07296 break;
Gabriel Charette37b6f6b22023-07-12 14:54:11297 }
Scott Grahamf2402372017-05-29 23:47:07298
Francois Dorayc1c66c62020-03-18 15:27:49299 const bool did_handle_io_event = HandleIoEventsUntil(/*deadline=*/0);
Gabriel Charette37b6f6b22023-07-12 14:54:11300 if (run_state.should_quit) {
Scott Grahamf2402372017-05-29 23:47:07301 break;
Gabriel Charette37b6f6b22023-07-12 14:54:11302 }
Scott Grahamf2402372017-05-29 23:47:07303
Francois Dorayc1c66c62020-03-18 15:27:49304 bool attempt_more_work =
305 next_work_info.is_immediate() || did_handle_io_event;
Peter Kasting134ef9af2024-12-28 02:30:09306 if (attempt_more_work) {
Scott Grahamf2402372017-05-29 23:47:07307 continue;
Peter Kasting134ef9af2024-12-28 02:30:09308 }
Scott Grahamf2402372017-05-29 23:47:07309
Olivier Lic01b21342024-05-27 16:19:35310 delegate->DoIdleWork();
Gabriel Charette37b6f6b22023-07-12 14:54:11311 if (run_state.should_quit) {
Scott Grahamf2402372017-05-29 23:47:07312 break;
Gabriel Charette37b6f6b22023-07-12 14:54:11313 }
Scott Grahamf2402372017-05-29 23:47:07314
Gabriel Charette37b6f6b22023-07-12 14:54:11315 delegate->BeforeWait();
316
Francois Dorayc1c66c62020-03-18 15:27:49317 zx_time_t deadline = next_work_info.delayed_run_time.is_max()
Scott Grahamfe0e9f462017-09-18 21:25:04318 ? ZX_TIME_INFINITE
Francois Dorayc1c66c62020-03-18 15:27:49319 : next_work_info.delayed_run_time.ToZxTime();
Sergey Ulanovb229d18d2018-10-26 00:02:34320
Francois Dorayc1c66c62020-03-18 15:27:49321 HandleIoEventsUntil(deadline);
Scott Grahamf2402372017-05-29 23:47:07322 }
Scott Grahamf5902f842017-05-26 02:02:10323}
324
325void MessagePumpFuchsia::Quit() {
Gabriel Charette37b6f6b22023-07-12 14:54:11326 CHECK(run_state_);
327 run_state_->should_quit = true;
Scott Grahamf5902f842017-05-26 02:02:10328}
329
330void MessagePumpFuchsia::ScheduleWork() {
Sergey Ulanovb229d18d2018-10-26 00:02:34331 // Stop async_loop to let MessagePumpFuchsia::Run() handle message loop tasks.
332 async_loop_->Quit();
Scott Grahamf5902f842017-05-26 02:02:10333}
334
335void MessagePumpFuchsia::ScheduleDelayedWork(
Etienne Pierre-dorayf2f8e13b2022-03-10 12:42:33336 const Delegate::NextWorkInfo& next_work_info) {
Francois Dorayc1c66c62020-03-18 15:27:49337 // Since this is always called from the same thread as Run(), there is nothing
338 // to do as the loop is already running. It will wait in Run() with the
339 // correct timeout when it's out of immediate tasks.
Alison Galed965ba02024-04-26 21:50:54340 // TODO(crbug.com/40594269): Consider removing ScheduleDelayedWork()
Francois Dorayc1c66c62020-03-18 15:27:49341 // when all pumps function this way (bit.ly/merge-message-pump-do-work).
Scott Grahamf5902f842017-05-26 02:02:10342}
343
344} // namespace base