1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "mojo/core/watch.h"
6
7 #include "mojo/core/request_context.h"
8 #include "mojo/core/watcher_dispatcher.h"
9
10 namespace mojo {
11 namespace core {
12
Watch(const scoped_refptr<WatcherDispatcher> & watcher,const scoped_refptr<Dispatcher> & dispatcher,uintptr_t context,MojoHandleSignals signals,MojoTriggerCondition condition)13 Watch::Watch(const scoped_refptr<WatcherDispatcher>& watcher,
14 const scoped_refptr<Dispatcher>& dispatcher,
15 uintptr_t context,
16 MojoHandleSignals signals,
17 MojoTriggerCondition condition)
18 : watcher_(watcher),
19 dispatcher_(dispatcher),
20 context_(context),
21 signals_(signals),
22 condition_(condition) {}
23
NotifyState(const HandleSignalsState & state,bool allowed_to_call_callback)24 bool Watch::NotifyState(const HandleSignalsState& state,
25 bool allowed_to_call_callback) {
26 AssertWatcherLockAcquired();
27
28 // NOTE: This method must NEVER call into |dispatcher_| directly, because it
29 // may be called while |dispatcher_| holds a lock.
30 MojoResult rv = MOJO_RESULT_SHOULD_WAIT;
31 RequestContext* const request_context = RequestContext::current();
32 const bool notify_success =
33 (state.satisfies_any(signals_) &&
34 condition_ == MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED) ||
35 (!state.satisfies_all(signals_) &&
36 condition_ == MOJO_TRIGGER_CONDITION_SIGNALS_UNSATISFIED);
37 if (notify_success) {
38 rv = MOJO_RESULT_OK;
39 if (allowed_to_call_callback && rv != last_known_result_) {
40 request_context->AddWatchNotifyFinalizer(this, MOJO_RESULT_OK, state);
41 }
42 } else if (condition_ == MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED &&
43 !state.can_satisfy_any(signals_)) {
44 rv = MOJO_RESULT_FAILED_PRECONDITION;
45 if (allowed_to_call_callback && rv != last_known_result_) {
46 request_context->AddWatchNotifyFinalizer(
47 this, MOJO_RESULT_FAILED_PRECONDITION, state);
48 }
49 }
50
51 last_known_signals_state_ =
52 *static_cast<const MojoHandleSignalsState*>(&state);
53 last_known_result_ = rv;
54 return ready();
55 }
56
Cancel()57 void Watch::Cancel() {
58 RequestContext::current()->AddWatchCancelFinalizer(this);
59 }
60
InvokeCallback(MojoResult result,const HandleSignalsState & state,MojoTrapEventFlags flags)61 void Watch::InvokeCallback(MojoResult result,
62 const HandleSignalsState& state,
63 MojoTrapEventFlags flags) {
64 // We hold the lock through invocation to ensure that only one notification
65 // callback runs for this context at any given time.
66 base::AutoLock lock(notification_lock_);
67
68 // Ensure that no notifications are dispatched beyond cancellation.
69 if (is_cancelled_)
70 return;
71
72 if (result == MOJO_RESULT_CANCELLED)
73 is_cancelled_ = true;
74
75 // NOTE: This will acquire |watcher_|'s internal lock. It's safe because a
76 // thread can only enter InvokeCallback() from within a RequestContext
77 // destructor where no dispatcher locks are held.
78 watcher_->InvokeWatchCallback(context_, result, state, flags);
79 }
80
~Watch()81 Watch::~Watch() {}
82
83 #if DCHECK_IS_ON()
AssertWatcherLockAcquired() const84 void Watch::AssertWatcherLockAcquired() const {
85 watcher_->lock_.AssertAcquired();
86 }
87 #endif
88
89 } // namespace core
90 } // namespace mojo
91