• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/public/cpp/system/simple_watcher.h"
6 
7 #include "base/bind.h"
8 #include "base/macros.h"
9 #include "base/memory/ptr_util.h"
10 #include "base/sequenced_task_runner.h"
11 #include "base/single_thread_task_runner.h"
12 #include "base/synchronization/lock.h"
13 #include "base/threading/thread_task_runner_handle.h"
14 #include "base/trace_event/heap_profiler.h"
15 #include "mojo/public/c/system/trap.h"
16 
17 namespace mojo {
18 
19 // Thread-safe Context object used to schedule trap events from arbitrary
20 // threads.
21 class SimpleWatcher::Context : public base::RefCountedThreadSafe<Context> {
22  public:
23   // Creates a |Context| instance for a new watch on |watcher|, to observe
24   // |signals| on |handle|.
Create(base::WeakPtr<SimpleWatcher> watcher,scoped_refptr<base::SequencedTaskRunner> task_runner,TrapHandle trap_handle,Handle handle,MojoHandleSignals signals,MojoTriggerCondition condition,int watch_id,MojoResult * result)25   static scoped_refptr<Context> Create(
26       base::WeakPtr<SimpleWatcher> watcher,
27       scoped_refptr<base::SequencedTaskRunner> task_runner,
28       TrapHandle trap_handle,
29       Handle handle,
30       MojoHandleSignals signals,
31       MojoTriggerCondition condition,
32       int watch_id,
33       MojoResult* result) {
34     scoped_refptr<Context> context =
35         new Context(watcher, task_runner, watch_id);
36 
37     // If MojoAddTrigger succeeds, it effectively assumes ownership of a
38     // reference to |context|. In that case, this reference is balanced in
39     // CallNotify() when |result| is |MOJO_RESULT_CANCELLED|.
40     context->AddRef();
41 
42     *result = MojoAddTrigger(trap_handle.value(), handle.value(), signals,
43                              condition, context->value(), nullptr);
44     if (*result != MOJO_RESULT_OK) {
45       // Balanced by the AddRef() above since MojoAddTrigger failed.
46       context->Release();
47       return nullptr;
48     }
49 
50     return context;
51   }
52 
CallNotify(const MojoTrapEvent * event)53   static void CallNotify(const MojoTrapEvent* event) {
54     auto* context = reinterpret_cast<Context*>(event->trigger_context);
55     context->Notify(event->result, event->signals_state, event->flags);
56 
57     // The trigger was removed. We can release the ref it owned, which in turn
58     // may delete the Context.
59     if (event->result == MOJO_RESULT_CANCELLED)
60       context->Release();
61   }
62 
value() const63   uintptr_t value() const { return reinterpret_cast<uintptr_t>(this); }
64 
DisableCancellationNotifications()65   void DisableCancellationNotifications() {
66     base::AutoLock lock(lock_);
67     enable_cancellation_notifications_ = false;
68   }
69 
70  private:
71   friend class base::RefCountedThreadSafe<Context>;
72 
Context(base::WeakPtr<SimpleWatcher> weak_watcher,scoped_refptr<base::SequencedTaskRunner> task_runner,int watch_id)73   Context(base::WeakPtr<SimpleWatcher> weak_watcher,
74           scoped_refptr<base::SequencedTaskRunner> task_runner,
75           int watch_id)
76       : weak_watcher_(weak_watcher),
77         task_runner_(task_runner),
78         watch_id_(watch_id) {}
~Context()79   ~Context() {}
80 
Notify(MojoResult result,MojoHandleSignalsState signals_state,MojoTrapEventFlags flags)81   void Notify(MojoResult result,
82               MojoHandleSignalsState signals_state,
83               MojoTrapEventFlags flags) {
84     if (result == MOJO_RESULT_CANCELLED) {
85       // The SimpleWatcher may have explicitly removed this trigger, so we don't
86       // bother dispatching the notification - it would be ignored anyway.
87       //
88       // TODO(rockot): This shouldn't really be necessary, but there are already
89       // instances today where bindings object may be bound and subsequently
90       // closed due to pipe error, all before the thread's TaskRunner has been
91       // properly initialized.
92       base::AutoLock lock(lock_);
93       if (!enable_cancellation_notifications_)
94         return;
95     }
96 
97     HandleSignalsState state(signals_state.satisfied_signals,
98                              signals_state.satisfiable_signals);
99     if (!(flags & MOJO_TRAP_EVENT_FLAG_WITHIN_API_CALL) &&
100         task_runner_->RunsTasksInCurrentSequence() && weak_watcher_ &&
101         weak_watcher_->is_default_task_runner_) {
102       // System notifications will trigger from the task runner passed to
103       // mojo::core::ScopedIPCSupport. In Chrome this happens to always be
104       // the default task runner for the IO thread.
105       weak_watcher_->OnHandleReady(watch_id_, result, state);
106     } else {
107       task_runner_->PostTask(
108           FROM_HERE, base::Bind(&SimpleWatcher::OnHandleReady, weak_watcher_,
109                                 watch_id_, result, state));
110     }
111   }
112 
113   const base::WeakPtr<SimpleWatcher> weak_watcher_;
114   const scoped_refptr<base::SequencedTaskRunner> task_runner_;
115   const int watch_id_;
116 
117   base::Lock lock_;
118   bool enable_cancellation_notifications_ = true;
119 
120   DISALLOW_COPY_AND_ASSIGN(Context);
121 };
122 
SimpleWatcher(const base::Location & from_here,ArmingPolicy arming_policy,scoped_refptr<base::SequencedTaskRunner> runner)123 SimpleWatcher::SimpleWatcher(const base::Location& from_here,
124                              ArmingPolicy arming_policy,
125                              scoped_refptr<base::SequencedTaskRunner> runner)
126     : arming_policy_(arming_policy),
127       task_runner_(std::move(runner)),
128       is_default_task_runner_(base::ThreadTaskRunnerHandle::IsSet() &&
129                               task_runner_ ==
130                                   base::ThreadTaskRunnerHandle::Get()),
131       heap_profiler_tag_(from_here.file_name()),
132       weak_factory_(this) {
133   MojoResult rv = CreateTrap(&Context::CallNotify, &trap_handle_);
134   DCHECK_EQ(MOJO_RESULT_OK, rv);
135   DCHECK(task_runner_->RunsTasksInCurrentSequence());
136 }
137 
~SimpleWatcher()138 SimpleWatcher::~SimpleWatcher() {
139   if (IsWatching())
140     Cancel();
141 }
142 
IsWatching() const143 bool SimpleWatcher::IsWatching() const {
144   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
145   return context_ != nullptr;
146 }
147 
Watch(Handle handle,MojoHandleSignals signals,MojoTriggerCondition condition,const ReadyCallbackWithState & callback)148 MojoResult SimpleWatcher::Watch(Handle handle,
149                                 MojoHandleSignals signals,
150                                 MojoTriggerCondition condition,
151                                 const ReadyCallbackWithState& callback) {
152   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
153   DCHECK(!IsWatching());
154   DCHECK(!callback.is_null());
155 
156   callback_ = callback;
157   handle_ = handle;
158   watch_id_ += 1;
159 
160   MojoResult result = MOJO_RESULT_UNKNOWN;
161   context_ = Context::Create(weak_factory_.GetWeakPtr(), task_runner_,
162                              trap_handle_.get(), handle_, signals, condition,
163                              watch_id_, &result);
164   if (!context_) {
165     handle_.set_value(kInvalidHandleValue);
166     callback_.Reset();
167     DCHECK_EQ(MOJO_RESULT_INVALID_ARGUMENT, result);
168     return result;
169   }
170 
171   if (arming_policy_ == ArmingPolicy::AUTOMATIC)
172     ArmOrNotify();
173 
174   return MOJO_RESULT_OK;
175 }
176 
Cancel()177 void SimpleWatcher::Cancel() {
178   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
179 
180   // The watcher may have already been cancelled if the handle was closed.
181   if (!context_)
182     return;
183 
184   // Prevent the cancellation notification from being dispatched to
185   // OnHandleReady() when cancellation is explicit. See the note in the
186   // implementation of DisableCancellationNotifications() above.
187   context_->DisableCancellationNotifications();
188 
189   handle_.set_value(kInvalidHandleValue);
190   callback_.Reset();
191 
192   // Ensure |context_| is unset by the time we call MojoRemoveTrigger, as it may
193   // re-enter the notification callback and we want to ensure |context_| is
194   // unset by then.
195   scoped_refptr<Context> context;
196   std::swap(context, context_);
197   MojoResult rv =
198       MojoRemoveTrigger(trap_handle_.get().value(), context->value(), nullptr);
199 
200   // It's possible this cancellation could race with a handle closure
201   // notification, in which case the watch may have already been implicitly
202   // cancelled.
203   DCHECK(rv == MOJO_RESULT_OK || rv == MOJO_RESULT_NOT_FOUND);
204 }
205 
Arm(MojoResult * ready_result,HandleSignalsState * ready_state)206 MojoResult SimpleWatcher::Arm(MojoResult* ready_result,
207                               HandleSignalsState* ready_state) {
208   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
209   uint32_t num_blocking_events = 1;
210   MojoTrapEvent blocking_event = {sizeof(blocking_event)};
211   MojoResult rv = MojoArmTrap(trap_handle_.get().value(), nullptr,
212                               &num_blocking_events, &blocking_event);
213   if (rv == MOJO_RESULT_FAILED_PRECONDITION) {
214     DCHECK(context_);
215     DCHECK_EQ(1u, num_blocking_events);
216     DCHECK_EQ(context_->value(), blocking_event.trigger_context);
217     if (ready_result)
218       *ready_result = blocking_event.result;
219     if (ready_state) {
220       *ready_state =
221           HandleSignalsState(blocking_event.signals_state.satisfied_signals,
222                              blocking_event.signals_state.satisfiable_signals);
223     }
224   }
225 
226   return rv;
227 }
228 
ArmOrNotify()229 void SimpleWatcher::ArmOrNotify() {
230   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
231 
232   // Already cancelled, nothing to do.
233   if (!IsWatching())
234     return;
235 
236   MojoResult ready_result;
237   HandleSignalsState ready_state;
238   MojoResult rv = Arm(&ready_result, &ready_state);
239   if (rv == MOJO_RESULT_OK)
240     return;
241 
242   DCHECK_EQ(MOJO_RESULT_FAILED_PRECONDITION, rv);
243   task_runner_->PostTask(
244       FROM_HERE,
245       base::Bind(&SimpleWatcher::OnHandleReady, weak_factory_.GetWeakPtr(),
246                  watch_id_, ready_result, ready_state));
247 }
248 
OnHandleReady(int watch_id,MojoResult result,const HandleSignalsState & state)249 void SimpleWatcher::OnHandleReady(int watch_id,
250                                   MojoResult result,
251                                   const HandleSignalsState& state) {
252   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
253 
254   // This notification may be for a previously watched context, in which case
255   // we just ignore it.
256   if (watch_id != watch_id_)
257     return;
258 
259   ReadyCallbackWithState callback = callback_;
260   if (result == MOJO_RESULT_CANCELLED) {
261     // Implicit cancellation due to someone closing the watched handle. We clear
262     // the SimppleWatcher's state before dispatching this.
263     context_ = nullptr;
264     handle_.set_value(kInvalidHandleValue);
265     callback_.Reset();
266   }
267 
268   // NOTE: It's legal for |callback| to delete |this|.
269   if (!callback.is_null()) {
270     TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION event(heap_profiler_tag_);
271 
272     base::WeakPtr<SimpleWatcher> weak_self = weak_factory_.GetWeakPtr();
273     callback.Run(result, state);
274     if (!weak_self)
275       return;
276 
277     // Prevent |MOJO_RESULT_FAILED_PRECONDITION| task spam by only notifying
278     // at most once in AUTOMATIC arming mode.
279     if (result == MOJO_RESULT_FAILED_PRECONDITION)
280       return;
281 
282     if (arming_policy_ == ArmingPolicy::AUTOMATIC && IsWatching())
283       ArmOrNotify();
284   }
285 }
286 
287 }  // namespace mojo
288