blob: cf08ac37ee5a0d5686fd64e0aaeee486c0c220ca [file] [log] [blame]
// 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<WatcherDispatcher>& watcher,
const scoped_refptr<Dispatcher>& 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<const MojoHandleSignalsState*>(&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