// 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/core/watch.h"
#include "mojo/core/request_context.h"
#include "mojo/core/watcher_dispatcher.h"
namespace mojo {
namespace core {
Watch::Watch(const scoped_refptr<WatcherDispatcher>& watcher,
const scoped_refptr<Dispatcher>& dispatcher,
uintptr_t context,
MojoHandleSignals signals,
MojoTriggerCondition condition)
: watcher_(watcher),
dispatcher_(dispatcher),
context_(context),
signals_(signals),
condition_(condition) {}
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();
const bool notify_success =
(state.satisfies_any(signals_) &&
condition_ == MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED) ||
(!state.satisfies_all(signals_) &&
condition_ == MOJO_TRIGGER_CONDITION_SIGNALS_UNSATISFIED);
if (notify_success) {
rv = MOJO_RESULT_OK;
if (allowed_to_call_callback && rv != last_known_result_) {
request_context->AddWatchNotifyFinalizer(this, MOJO_RESULT_OK, state);
}
} else if (condition_ == MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED &&
!state.can_satisfy_any(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<const MojoHandleSignalsState*>(&state);
last_known_result_ = rv;
return ready();
}
void Watch::Cancel() {
RequestContext::current()->AddWatchCancelFinalizer(this);
}
void Watch::InvokeCallback(MojoResult result,
const HandleSignalsState& state,
MojoTrapEventFlags 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_);
// Ensure that no notifications are dispatched beyond cancellation.
if (is_cancelled_)
return;
if (result == MOJO_RESULT_CANCELLED)
is_cancelled_ = true;
// 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 core
} // namespace mojo