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/synchronization/lock.h"
12 #include "base/threading/thread_task_runner_handle.h"
13 #include "base/trace_event/heap_profiler.h"
14 #include "base/trace_event/trace_event.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       context->cancelled_ = true;
46 
47       // Balanced by the AddRef() above since MojoAddTrigger failed.
48       context->Release();
49       return nullptr;
50     }
51 
52     return context;
53   }
54 
CallNotify(const MojoTrapEvent * event)55   static void CallNotify(const MojoTrapEvent* event) {
56     auto* context = reinterpret_cast<Context*>(event->trigger_context);
57     context->Notify(event->result, event->signals_state, event->flags);
58 
59     // The trigger was removed. We can release the ref it owned, which in turn
60     // may delete the Context.
61     if (event->result == MOJO_RESULT_CANCELLED)
62       context->Release();
63   }
64 
value() const65   uintptr_t value() const { return reinterpret_cast<uintptr_t>(this); }
66 
DisableCancellationNotifications()67   void DisableCancellationNotifications() {
68     base::AutoLock lock(lock_);
69     enable_cancellation_notifications_ = false;
70   }
71 
72  private:
73   friend class base::RefCountedThreadSafe<Context>;
74 
Context(base::WeakPtr<SimpleWatcher> weak_watcher,scoped_refptr<base::SequencedTaskRunner> task_runner,int watch_id)75   Context(base::WeakPtr<SimpleWatcher> weak_watcher,
76           scoped_refptr<base::SequencedTaskRunner> task_runner,
77           int watch_id)
78       : weak_watcher_(weak_watcher),
79         task_runner_(task_runner),
80         watch_id_(watch_id) {}
81 
~Context()82   ~Context() {
83     // TODO(https://crbug.com/896419): Remove this once it's been live for a
84     // while. This is intended to catch possible double-frees of SimpleWatchers,
85     // due to, e.g., invalid cross-thread usage of bindings endpoints. If this
86     // CHECK fails, then the Context is being destroyed before a cancellation
87     // notification fired. In that case we know a Context ref has been
88     // double-released and we can catch its stack.
89     base::AutoLock lock(lock_);
90     CHECK(cancelled_);
91   }
92 
Notify(MojoResult result,MojoHandleSignalsState signals_state,MojoTrapEventFlags flags)93   void Notify(MojoResult result,
94               MojoHandleSignalsState signals_state,
95               MojoTrapEventFlags flags) {
96     if (result == MOJO_RESULT_CANCELLED) {
97       // The SimpleWatcher may have explicitly removed this trigger, so we don't
98       // bother dispatching the notification - it would be ignored anyway.
99       //
100       // TODO(rockot): This shouldn't really be necessary, but there are already
101       // instances today where bindings object may be bound and subsequently
102       // closed due to pipe error, all before the thread's TaskRunner has been
103       // properly initialized.
104       base::AutoLock lock(lock_);
105       cancelled_ = true;
106       if (!enable_cancellation_notifications_)
107         return;
108     }
109 
110     HandleSignalsState state(signals_state.satisfied_signals,
111                              signals_state.satisfiable_signals);
112     if (!(flags & MOJO_TRAP_EVENT_FLAG_WITHIN_API_CALL) &&
113         task_runner_->RunsTasksInCurrentSequence() && weak_watcher_ &&
114         weak_watcher_->is_default_task_runner_) {
115       // System notifications will trigger from the task runner passed to
116       // mojo::core::ScopedIPCSupport. In Chrome this happens to always be
117       // the default task runner for the IO thread.
118       weak_watcher_->OnHandleReady(watch_id_, result, state);
119     } else {
120       task_runner_->PostTask(
121           FROM_HERE, base::BindOnce(&SimpleWatcher::OnHandleReady,
122                                     weak_watcher_, watch_id_, result, state));
123     }
124   }
125 
126   const base::WeakPtr<SimpleWatcher> weak_watcher_;
127   const scoped_refptr<base::SequencedTaskRunner> task_runner_;
128   const int watch_id_;
129 
130   base::Lock lock_;
131   bool cancelled_ = false;
132   bool enable_cancellation_notifications_ = true;
133 
134   DISALLOW_COPY_AND_ASSIGN(Context);
135 };
136 
SimpleWatcher(const base::Location & from_here,ArmingPolicy arming_policy,scoped_refptr<base::SequencedTaskRunner> runner)137 SimpleWatcher::SimpleWatcher(const base::Location& from_here,
138                              ArmingPolicy arming_policy,
139                              scoped_refptr<base::SequencedTaskRunner> runner)
140     : arming_policy_(arming_policy),
141       task_runner_(std::move(runner)),
142       is_default_task_runner_(base::ThreadTaskRunnerHandle::IsSet() &&
143                               task_runner_ ==
144                                   base::ThreadTaskRunnerHandle::Get()),
145       heap_profiler_tag_(from_here.file_name()) {
146   MojoResult rv = CreateTrap(&Context::CallNotify, &trap_handle_);
147   DCHECK_EQ(MOJO_RESULT_OK, rv);
148   DCHECK(task_runner_->RunsTasksInCurrentSequence());
149 }
150 
~SimpleWatcher()151 SimpleWatcher::~SimpleWatcher() {
152   if (IsWatching())
153     Cancel();
154 }
155 
IsWatching() const156 bool SimpleWatcher::IsWatching() const {
157   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
158   return context_ != nullptr;
159 }
160 
Watch(Handle handle,MojoHandleSignals signals,MojoTriggerCondition condition,ReadyCallbackWithState callback)161 MojoResult SimpleWatcher::Watch(Handle handle,
162                                 MojoHandleSignals signals,
163                                 MojoTriggerCondition condition,
164                                 ReadyCallbackWithState callback) {
165   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
166   DCHECK(!IsWatching());
167   DCHECK(!callback.is_null());
168 
169   callback_ = std::move(callback);
170   handle_ = handle;
171   watch_id_ += 1;
172 
173   MojoResult result = MOJO_RESULT_UNKNOWN;
174   context_ = Context::Create(weak_factory_.GetWeakPtr(), task_runner_,
175                              trap_handle_.get(), handle_, signals, condition,
176                              watch_id_, &result);
177   if (!context_) {
178     handle_.set_value(kInvalidHandleValue);
179     callback_.Reset();
180     DCHECK_EQ(MOJO_RESULT_INVALID_ARGUMENT, result);
181     return result;
182   }
183 
184   if (arming_policy_ == ArmingPolicy::AUTOMATIC)
185     ArmOrNotify();
186 
187   return MOJO_RESULT_OK;
188 }
189 
Cancel()190 void SimpleWatcher::Cancel() {
191   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
192 
193   // The watcher may have already been cancelled if the handle was closed.
194   if (!context_)
195     return;
196 
197   // Prevent the cancellation notification from being dispatched to
198   // OnHandleReady() when cancellation is explicit. See the note in the
199   // implementation of DisableCancellationNotifications() above.
200   context_->DisableCancellationNotifications();
201 
202   handle_.set_value(kInvalidHandleValue);
203   callback_.Reset();
204 
205   // Ensure |context_| is unset by the time we call MojoRemoveTrigger, as it may
206   // re-enter the notification callback and we want to ensure |context_| is
207   // unset by then.
208   scoped_refptr<Context> context;
209   std::swap(context, context_);
210   MojoResult rv =
211       MojoRemoveTrigger(trap_handle_.get().value(), context->value(), nullptr);
212 
213   // It's possible this cancellation could race with a handle closure
214   // notification, in which case the watch may have already been implicitly
215   // cancelled.
216   DCHECK(rv == MOJO_RESULT_OK || rv == MOJO_RESULT_NOT_FOUND);
217 
218   weak_factory_.InvalidateWeakPtrs();
219 }
220 
Arm(MojoResult * ready_result,HandleSignalsState * ready_state)221 MojoResult SimpleWatcher::Arm(MojoResult* ready_result,
222                               HandleSignalsState* ready_state) {
223   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
224   uint32_t num_blocking_events = 1;
225   MojoTrapEvent blocking_event = {sizeof(blocking_event)};
226   MojoResult rv = MojoArmTrap(trap_handle_.get().value(), nullptr,
227                               &num_blocking_events, &blocking_event);
228   if (rv == MOJO_RESULT_FAILED_PRECONDITION) {
229     DCHECK(context_);
230     DCHECK_EQ(1u, num_blocking_events);
231     DCHECK_EQ(context_->value(), blocking_event.trigger_context);
232     if (ready_result)
233       *ready_result = blocking_event.result;
234     if (ready_state) {
235       *ready_state =
236           HandleSignalsState(blocking_event.signals_state.satisfied_signals,
237                              blocking_event.signals_state.satisfiable_signals);
238     }
239   }
240 
241   return rv;
242 }
243 
ArmOrNotify()244 void SimpleWatcher::ArmOrNotify() {
245   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
246 
247   // Already cancelled, nothing to do.
248   if (!IsWatching())
249     return;
250 
251   MojoResult ready_result;
252   HandleSignalsState ready_state;
253   MojoResult rv = Arm(&ready_result, &ready_state);
254   if (rv == MOJO_RESULT_OK)
255     return;
256 
257   DCHECK_EQ(MOJO_RESULT_FAILED_PRECONDITION, rv);
258   task_runner_->PostTask(
259       FROM_HERE,
260       base::BindOnce(&SimpleWatcher::OnHandleReady, weak_factory_.GetWeakPtr(),
261                      watch_id_, ready_result, ready_state));
262 }
263 
OnHandleReady(int watch_id,MojoResult result,const HandleSignalsState & state)264 void SimpleWatcher::OnHandleReady(int watch_id,
265                                   MojoResult result,
266                                   const HandleSignalsState& state) {
267   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
268 
269   // This notification may be for a previously watched context, in which case
270   // we just ignore it.
271   if (watch_id != watch_id_)
272     return;
273 
274   ReadyCallbackWithState callback = callback_;
275   if (result == MOJO_RESULT_CANCELLED) {
276     // Implicit cancellation due to someone closing the watched handle. We clear
277     // the SimppleWatcher's state before dispatching this.
278     context_ = nullptr;
279     handle_.set_value(kInvalidHandleValue);
280     callback_.Reset();
281   }
282 
283   // NOTE: It's legal for |callback| to delete |this|.
284   if (!callback.is_null()) {
285     TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION event(heap_profiler_tag_);
286     // Lot of janks caused are grouped to OnHandleReady tasks. This trace event helps identify the
287     // cause of janks. It is ok to pass |heap_profiler_tag_| here since it is a string literal.
288     // TODO(927206): Consider renaming |heap_profiler_tag_|.
289     TRACE_EVENT0("toplevel", heap_profiler_tag_);
290 
291     base::WeakPtr<SimpleWatcher> weak_self = weak_factory_.GetWeakPtr();
292     callback.Run(result, state);
293     if (!weak_self)
294       return;
295 
296     // Prevent |MOJO_RESULT_FAILED_PRECONDITION| task spam by only notifying
297     // at most once in AUTOMATIC arming mode.
298     if (result == MOJO_RESULT_FAILED_PRECONDITION)
299       return;
300 
301     if (arming_policy_ == ArmingPolicy::AUTOMATIC && IsWatching())
302       ArmOrNotify();
303   }
304 }
305 
306 }  // namespace mojo
307