|  | // Copyright 2017 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "base/message_loop/message_pump_fuchsia.h" | 
|  |  | 
|  | #include <fdio/io.h> | 
|  | #include <fdio/private.h> | 
|  | #include <zircon/status.h> | 
|  | #include <zircon/syscalls.h> | 
|  |  | 
|  | #include "base/auto_reset.h" | 
|  | #include "base/fuchsia/fuchsia_logging.h" | 
|  | #include "base/logging.h" | 
|  |  | 
|  | namespace base { | 
|  |  | 
|  | MessagePumpFuchsia::ZxHandleWatchController::ZxHandleWatchController( | 
|  | const Location& from_here) | 
|  | : async_wait_t({}), created_from_location_(from_here) {} | 
|  |  | 
|  | MessagePumpFuchsia::ZxHandleWatchController::~ZxHandleWatchController() { | 
|  | if (!StopWatchingZxHandle()) | 
|  | NOTREACHED(); | 
|  | } | 
|  |  | 
|  | bool MessagePumpFuchsia::ZxHandleWatchController::WaitBegin() { | 
|  | DCHECK(!handler); | 
|  | async_wait_t::handler = &HandleSignal; | 
|  |  | 
|  | zx_status_t status = async_begin_wait(&weak_pump_->async_dispatcher_, this); | 
|  | if (status != ZX_OK) { | 
|  | ZX_DLOG(ERROR, status) << "async_begin_wait() failed"; | 
|  | async_wait_t::handler = nullptr; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool MessagePumpFuchsia::ZxHandleWatchController::StopWatchingZxHandle() { | 
|  | if (was_stopped_) { | 
|  | DCHECK(!*was_stopped_); | 
|  | *was_stopped_ = true; | 
|  |  | 
|  | // |was_stopped_| points at a value stored on the stack, which will go out | 
|  | // of scope. MessagePumpFuchsia::Run() will reset it only if the value is | 
|  | // false. So we need to reset this pointer here as well, to make sure it's | 
|  | // not used again. | 
|  | was_stopped_ = nullptr; | 
|  | } | 
|  |  | 
|  | // If the pump is gone then there is nothing to cancel. | 
|  | if (!weak_pump_) | 
|  | return true; | 
|  |  | 
|  | // |handler| is set when waiting for a signal. | 
|  | if (!handler) | 
|  | return true; | 
|  |  | 
|  | async_wait_t::handler = nullptr; | 
|  |  | 
|  | zx_status_t result = async_cancel_wait(&weak_pump_->async_dispatcher_, this); | 
|  | ZX_DLOG_IF(ERROR, result != ZX_OK, result) << "async_cancel_wait failed"; | 
|  | return result == ZX_OK; | 
|  | } | 
|  |  | 
|  | // static | 
|  | void MessagePumpFuchsia::ZxHandleWatchController::HandleSignal( | 
|  | async_t* async, | 
|  | async_wait_t* wait, | 
|  | zx_status_t status, | 
|  | const zx_packet_signal_t* signal) { | 
|  | if (status != ZX_OK) { | 
|  | ZX_LOG(WARNING, status) << "async wait failed"; | 
|  | return; | 
|  | } | 
|  |  | 
|  | ZxHandleWatchController* controller = | 
|  | static_cast<ZxHandleWatchController*>(wait); | 
|  | DCHECK_EQ(controller->handler, &HandleSignal); | 
|  | controller->handler = nullptr; | 
|  |  | 
|  | // |signal| can include other spurious things, in particular, that an fd | 
|  | // is writable, when we only asked to know when it was readable. In that | 
|  | // case, we don't want to call both the CanWrite and CanRead callback, | 
|  | // when the caller asked for only, for example, readable callbacks. So, | 
|  | // mask with the events that we actually wanted to know about. | 
|  | zx_signals_t signals = signal->trigger & signal->observed; | 
|  | DCHECK_NE(0u, signals); | 
|  |  | 
|  | // In the case of a persistent Watch, the Watch may be stopped and | 
|  | // potentially deleted by the caller within the callback, in which case | 
|  | // |controller| should not be accessed again, and we mustn't continue the | 
|  | // watch. We check for this with a bool on the stack, which the Watch | 
|  | // receives a pointer to. | 
|  | bool was_stopped = false; | 
|  | controller->was_stopped_ = &was_stopped; | 
|  |  | 
|  | controller->watcher_->OnZxHandleSignalled(wait->object, signals); | 
|  |  | 
|  | if (was_stopped) | 
|  | return; | 
|  |  | 
|  | controller->was_stopped_ = nullptr; | 
|  |  | 
|  | if (controller->persistent_) | 
|  | controller->WaitBegin(); | 
|  | } | 
|  |  | 
|  | void MessagePumpFuchsia::FdWatchController::OnZxHandleSignalled( | 
|  | zx_handle_t handle, | 
|  | zx_signals_t signals) { | 
|  | uint32_t events; | 
|  | __fdio_wait_end(io_, signals, &events); | 
|  |  | 
|  | // Each |watcher_| callback we invoke may stop or delete |this|. The pump has | 
|  | // set |was_stopped_| to point to a safe location on the calling stack, so we | 
|  | // can use that to detect being stopped mid-callback and avoid doing further | 
|  | // work that would touch |this|. | 
|  | bool* was_stopped = was_stopped_; | 
|  | if (events & FDIO_EVT_WRITABLE) | 
|  | watcher_->OnFileCanWriteWithoutBlocking(fd_); | 
|  | if (!*was_stopped && (events & FDIO_EVT_READABLE)) | 
|  | watcher_->OnFileCanReadWithoutBlocking(fd_); | 
|  |  | 
|  | // Don't add additional work here without checking |*was_stopped_| again. | 
|  | } | 
|  |  | 
|  | MessagePumpFuchsia::FdWatchController::FdWatchController( | 
|  | const Location& from_here) | 
|  | : FdWatchControllerInterface(from_here), | 
|  | ZxHandleWatchController(from_here) {} | 
|  |  | 
|  | MessagePumpFuchsia::FdWatchController::~FdWatchController() { | 
|  | if (!StopWatchingFileDescriptor()) | 
|  | NOTREACHED(); | 
|  | } | 
|  |  | 
|  | bool MessagePumpFuchsia::FdWatchController::WaitBegin() { | 
|  | // Refresh the |handle_| and |desired_signals_| from the mxio for the fd. | 
|  | // Some types of fdio map read/write events to different signals depending on | 
|  | // their current state, so we must do this every time we begin to wait. | 
|  | __fdio_wait_begin(io_, desired_events_, &object, &trigger); | 
|  | if (async_wait_t::object == ZX_HANDLE_INVALID) { | 
|  | DLOG(ERROR) << "fdio_wait_begin failed"; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return MessagePumpFuchsia::ZxHandleWatchController::WaitBegin(); | 
|  | } | 
|  |  | 
|  | bool MessagePumpFuchsia::FdWatchController::StopWatchingFileDescriptor() { | 
|  | bool success = StopWatchingZxHandle(); | 
|  | if (io_) { | 
|  | __fdio_release(io_); | 
|  | io_ = nullptr; | 
|  | } | 
|  | return success; | 
|  | } | 
|  |  | 
|  | MessagePumpFuchsia::MessagePumpFuchsia() : weak_factory_(this) {} | 
|  |  | 
|  | MessagePumpFuchsia::~MessagePumpFuchsia() = default; | 
|  |  | 
|  | bool MessagePumpFuchsia::WatchFileDescriptor(int fd, | 
|  | bool persistent, | 
|  | int mode, | 
|  | FdWatchController* controller, | 
|  | FdWatcher* delegate) { | 
|  | DCHECK_GE(fd, 0); | 
|  | DCHECK(controller); | 
|  | DCHECK(delegate); | 
|  |  | 
|  | if (!controller->StopWatchingFileDescriptor()) | 
|  | NOTREACHED(); | 
|  |  | 
|  | controller->fd_ = fd; | 
|  | controller->watcher_ = delegate; | 
|  |  | 
|  | DCHECK(!controller->io_); | 
|  | controller->io_ = __fdio_fd_to_io(fd); | 
|  | if (!controller->io_) { | 
|  | DLOG(ERROR) << "Failed to get IO for FD"; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | switch (mode) { | 
|  | case WATCH_READ: | 
|  | controller->desired_events_ = FDIO_EVT_READABLE; | 
|  | break; | 
|  | case WATCH_WRITE: | 
|  | controller->desired_events_ = FDIO_EVT_WRITABLE; | 
|  | break; | 
|  | case WATCH_READ_WRITE: | 
|  | controller->desired_events_ = FDIO_EVT_READABLE | FDIO_EVT_WRITABLE; | 
|  | break; | 
|  | default: | 
|  | NOTREACHED() << "unexpected mode: " << mode; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Pass dummy |handle| and |signals| values to WatchZxHandle(). The real | 
|  | // values will be populated by FdWatchController::WaitBegin(), before actually | 
|  | // starting the wait operation. | 
|  | return WatchZxHandle(ZX_HANDLE_INVALID, persistent, 1, controller, | 
|  | controller); | 
|  | } | 
|  |  | 
|  | bool MessagePumpFuchsia::WatchZxHandle(zx_handle_t handle, | 
|  | bool persistent, | 
|  | zx_signals_t signals, | 
|  | ZxHandleWatchController* controller, | 
|  | ZxHandleWatcher* delegate) { | 
|  | DCHECK_NE(0u, signals); | 
|  | DCHECK(controller); | 
|  | DCHECK(delegate); | 
|  | DCHECK(handle == ZX_HANDLE_INVALID || | 
|  | controller->async_wait_t::object == ZX_HANDLE_INVALID || | 
|  | handle == controller->async_wait_t::object); | 
|  |  | 
|  | if (!controller->StopWatchingZxHandle()) | 
|  | NOTREACHED(); | 
|  |  | 
|  | controller->async_wait_t::object = handle; | 
|  | controller->persistent_ = persistent; | 
|  | controller->async_wait_t::trigger = signals; | 
|  | controller->watcher_ = delegate; | 
|  |  | 
|  | controller->weak_pump_ = weak_factory_.GetWeakPtr(); | 
|  |  | 
|  | return controller->WaitBegin(); | 
|  | } | 
|  |  | 
|  | bool MessagePumpFuchsia::HandleEvents(zx_time_t deadline) { | 
|  | zx_status_t status = async_dispatcher_.DispatchOrWaitUntil(deadline); | 
|  | switch (status) { | 
|  | // Return true if some tasks or events were dispatched or if the dispatcher | 
|  | // was stopped by ScheduleWork(). | 
|  | case ZX_OK: | 
|  | case ZX_ERR_CANCELED: | 
|  | return true; | 
|  |  | 
|  | case ZX_ERR_TIMED_OUT: | 
|  | return false; | 
|  |  | 
|  | default: | 
|  | ZX_DLOG(DCHECK, status) << "unexpected wait status"; | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | void MessagePumpFuchsia::Run(Delegate* delegate) { | 
|  | AutoReset<bool> auto_reset_keep_running(&keep_running_, true); | 
|  |  | 
|  | for (;;) { | 
|  | bool did_work = delegate->DoWork(); | 
|  | if (!keep_running_) | 
|  | break; | 
|  |  | 
|  | did_work |= delegate->DoDelayedWork(&delayed_work_time_); | 
|  | if (!keep_running_) | 
|  | break; | 
|  |  | 
|  | did_work |= HandleEvents(/*deadline=*/0); | 
|  | if (!keep_running_) | 
|  | break; | 
|  |  | 
|  | if (did_work) | 
|  | continue; | 
|  |  | 
|  | did_work = delegate->DoIdleWork(); | 
|  | if (!keep_running_) | 
|  | break; | 
|  |  | 
|  | if (did_work) | 
|  | continue; | 
|  |  | 
|  | zx_time_t deadline = delayed_work_time_.is_null() | 
|  | ? ZX_TIME_INFINITE | 
|  | : delayed_work_time_.ToZxTime(); | 
|  | HandleEvents(deadline); | 
|  | } | 
|  | } | 
|  |  | 
|  | void MessagePumpFuchsia::Quit() { | 
|  | keep_running_ = false; | 
|  | } | 
|  |  | 
|  | void MessagePumpFuchsia::ScheduleWork() { | 
|  | // Stop AsyncDispatcher to let MessagePumpFuchsia::Run() handle message loop | 
|  | // tasks. | 
|  | async_dispatcher_.Stop(); | 
|  | } | 
|  |  | 
|  | void MessagePumpFuchsia::ScheduleDelayedWork( | 
|  | const TimeTicks& delayed_work_time) { | 
|  | // We know that we can't be blocked right now since this method can only be | 
|  | // called on the same thread as Run, so we only need to update our record of | 
|  | // how long to sleep when we do sleep. | 
|  | delayed_work_time_ = delayed_work_time; | 
|  | } | 
|  |  | 
|  | }  // namespace base |