// Copyright 2016 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 "mojo/public/cpp/system/watcher.h" #include "base/bind.h" #include "base/location.h" #include "base/macros.h" #include "base/message_loop/message_loop.h" #include "mojo/public/c/system/functions.h" namespace mojo { class Watcher::MessageLoopObserver : public base::MessageLoop::DestructionObserver { public: explicit MessageLoopObserver(Watcher* watcher) : watcher_(watcher) { base::MessageLoop::current()->AddDestructionObserver(this); } ~MessageLoopObserver() override { StopObservingIfNecessary(); } private: // base::MessageLoop::DestructionObserver: void WillDestroyCurrentMessageLoop() override { StopObservingIfNecessary(); if (watcher_->IsWatching()) { // TODO(yzshen): Remove this notification. crbug.com/604762 watcher_->OnHandleReady(MOJO_RESULT_ABORTED); } } void StopObservingIfNecessary() { if (is_observing_) { is_observing_ = false; base::MessageLoop::current()->RemoveDestructionObserver(this); } } bool is_observing_ = true; Watcher* watcher_; DISALLOW_COPY_AND_ASSIGN(MessageLoopObserver); }; Watcher::Watcher(scoped_refptr<base::SingleThreadTaskRunner> runner) : task_runner_(std::move(runner)), is_default_task_runner_(task_runner_ == base::ThreadTaskRunnerHandle::Get()), weak_factory_(this) { DCHECK(task_runner_->BelongsToCurrentThread()); weak_self_ = weak_factory_.GetWeakPtr(); } Watcher::~Watcher() { if(IsWatching()) Cancel(); } bool Watcher::IsWatching() const { DCHECK(thread_checker_.CalledOnValidThread()); return handle_.is_valid(); } MojoResult Watcher::Start(Handle handle, MojoHandleSignals signals, const ReadyCallback& callback) { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(!IsWatching()); DCHECK(!callback.is_null()); message_loop_observer_.reset(new MessageLoopObserver(this)); callback_ = callback; handle_ = handle; MojoResult result = MojoWatch(handle_.value(), signals, &Watcher::CallOnHandleReady, reinterpret_cast<uintptr_t>(this)); if (result != MOJO_RESULT_OK) { handle_.set_value(kInvalidHandleValue); callback_.Reset(); message_loop_observer_.reset(); DCHECK(result == MOJO_RESULT_FAILED_PRECONDITION || result == MOJO_RESULT_INVALID_ARGUMENT); return result; } return MOJO_RESULT_OK; } void Watcher::Cancel() { DCHECK(thread_checker_.CalledOnValidThread()); // The watch may have already been cancelled if the handle was closed. if (!handle_.is_valid()) return; MojoResult result = MojoCancelWatch(handle_.value(), reinterpret_cast<uintptr_t>(this)); message_loop_observer_.reset(); // |result| may be MOJO_RESULT_INVALID_ARGUMENT if |handle_| has closed, but // OnHandleReady has not yet been called. DCHECK(result == MOJO_RESULT_INVALID_ARGUMENT || result == MOJO_RESULT_OK); handle_.set_value(kInvalidHandleValue); callback_.Reset(); } void Watcher::OnHandleReady(MojoResult result) { DCHECK(thread_checker_.CalledOnValidThread()); ReadyCallback callback = callback_; if (result == MOJO_RESULT_CANCELLED) { message_loop_observer_.reset(); handle_.set_value(kInvalidHandleValue); callback_.Reset(); } // NOTE: It's legal for |callback| to delete |this|. if (!callback.is_null()) callback.Run(result); } // static void Watcher::CallOnHandleReady(uintptr_t context, MojoResult result, MojoHandleSignalsState signals_state, MojoWatchNotificationFlags flags) { // NOTE: It is safe to assume the Watcher still exists because this callback // will never be run after the Watcher's destructor. // // TODO: Maybe we should also expose |signals_state| through the Watcher API. // Current HandleWatcher users have no need for it, so it's omitted here. Watcher* watcher = reinterpret_cast<Watcher*>(context); if ((flags & MOJO_WATCH_NOTIFICATION_FLAG_FROM_SYSTEM) && watcher->task_runner_->RunsTasksOnCurrentThread() && watcher->is_default_task_runner_) { // System notifications will trigger from the task runner passed to // mojo::edk::InitIPCSupport(). In Chrome this happens to always be the // default task runner for the IO thread. watcher->OnHandleReady(result); } else { watcher->task_runner_->PostTask( FROM_HERE, base::Bind(&Watcher::OnHandleReady, watcher->weak_self_, result)); } } } // namespace mojo