// 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 "mojo/edk/system/watch.h" #include "mojo/edk/system/request_context.h" #include "mojo/edk/system/watcher_dispatcher.h" namespace mojo { namespace edk { Watch::Watch(const scoped_refptr& watcher, const scoped_refptr& dispatcher, uintptr_t context, MojoHandleSignals signals) : watcher_(watcher), dispatcher_(dispatcher), context_(context), signals_(signals) {} bool Watch::NotifyState(const HandleSignalsState& state, bool allowed_to_call_callback) { AssertWatcherLockAcquired(); // NOTE: This method must NEVER call into |dispatcher_| directly, because it // may be called while |dispatcher_| holds a lock. MojoResult rv = MOJO_RESULT_SHOULD_WAIT; RequestContext* const request_context = RequestContext::current(); if (state.satisfies(signals_)) { rv = MOJO_RESULT_OK; if (allowed_to_call_callback && rv != last_known_result_) { request_context->AddWatchNotifyFinalizer(this, MOJO_RESULT_OK, state); } } else if (!state.can_satisfy(signals_)) { rv = MOJO_RESULT_FAILED_PRECONDITION; if (allowed_to_call_callback && rv != last_known_result_) { request_context->AddWatchNotifyFinalizer( this, MOJO_RESULT_FAILED_PRECONDITION, state); } } last_known_signals_state_ = *static_cast(&state); last_known_result_ = rv; return ready(); } void Watch::Cancel() { RequestContext::current()->AddWatchCancelFinalizer(this); } void Watch::InvokeCallback(MojoResult result, const HandleSignalsState& state, MojoWatcherNotificationFlags flags) { // We hold the lock through invocation to ensure that only one notification // callback runs for this context at any given time. base::AutoLock lock(notification_lock_); if (result == MOJO_RESULT_CANCELLED) { // Make sure cancellation is the last notification we dispatch. DCHECK(!is_cancelled_); is_cancelled_ = true; } else if (is_cancelled_) { return; } // NOTE: This will acquire |watcher_|'s internal lock. It's safe because a // thread can only enter InvokeCallback() from within a RequestContext // destructor where no dispatcher locks are held. watcher_->InvokeWatchCallback(context_, result, state, flags); } Watch::~Watch() {} #if DCHECK_IS_ON() void Watch::AssertWatcherLockAcquired() const { watcher_->lock_.AssertAcquired(); } #endif } // namespace edk } // namespace mojo