// 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/trace_event/heap_profiler.h" #include "mojo/public/c/system/functions.h" namespace mojo { Watcher::Watcher(const tracked_objects::Location& from_here, scoped_refptr<base::SingleThreadTaskRunner> runner) : task_runner_(std::move(runner)), is_default_task_runner_(task_runner_ == base::ThreadTaskRunnerHandle::Get()), heap_profiler_tag_(from_here.file_name()), 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()); 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(); 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)); // |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) { handle_.set_value(kInvalidHandleValue); callback_.Reset(); } // NOTE: It's legal for |callback| to delete |this|. if (!callback.is_null()) { TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION event(heap_profiler_tag_); 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