Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(194)

Side by Side Diff: mojo/public/cpp/system/simple_watcher.cc

Issue 2725133002: Mojo: Armed Watchers (Closed)
Patch Set: rebase Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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/single_thread_task_runner.h"
11 #include "base/synchronization/lock.h"
12 #include "base/trace_event/heap_profiler.h"
13 #include "mojo/public/c/system/watcher.h"
14
15 namespace mojo {
16
17 // Thread-safe Context object used to dispatch watch notifications from a
18 // arbitrary threads.
19 class SimpleWatcher::Context : public base::RefCountedThreadSafe<Context> {
20 public:
21 // Creates a |Context| instance for a new watch on |watcher|, to watch
22 // |handle| for |signals|.
23 static scoped_refptr<Context> Create(
24 base::WeakPtr<SimpleWatcher> watcher,
25 scoped_refptr<base::SingleThreadTaskRunner> task_runner,
26 WatcherHandle watcher_handle,
27 Handle handle,
28 MojoHandleSignals signals,
29 MojoResult* watch_result) {
30 // We use a scoped_refptr<Context> instance as watch context value. This
31 // instance (and thus the Context ref it holds) is effectively owned by the
32 // registered watch. We delete it on cancellation, which is a guaranteed
33 // event.
34 auto context_ref = base::MakeUnique<scoped_refptr<Context>>();
35 *context_ref = new Context(watcher, task_runner,
36 reinterpret_cast<uintptr_t>(context_ref.get()));
37
38 *watch_result = MojoWatch(watcher_handle.value(), handle.value(), signals,
39 (*context_ref)->value());
40 if (*watch_result != MOJO_RESULT_OK)
41 return nullptr;
42
43 // Ownership of the ref has been transferred into the successfully
44 // registered watch.
45 return *context_ref.release();
46 }
47
48 static void CallNotify(uintptr_t context_value,
49 MojoResult result,
50 MojoHandleSignalsState signals_state,
51 MojoWatcherNotificationFlags flags) {
52 auto* context_ref =
53 reinterpret_cast<scoped_refptr<Context>*>(context_value);
54 (*context_ref)->Notify(result, signals_state, flags);
55
56 // That was the last notification for the context. We can delete the ref
57 // owned by the watch.
58 if (result == MOJO_RESULT_CANCELLED)
59 delete context_ref;
yzshen1 2017/03/13 20:21:18 Does it make sense to use Context* as the |context
Ken Rockot(use gerrit already) 2017/03/13 22:00:10 Sure, that's effectively equivalent to this, but I
60 }
61
62 uintptr_t value() const { return context_value_; }
63
64 void DisableCancellationNotifications() {
65 base::AutoLock lock(lock_);
66 enable_cancellation_notifications_ = false;
67 }
68
69 private:
70 friend class base::RefCountedThreadSafe<Context>;
71
72 Context(base::WeakPtr<SimpleWatcher> weak_watcher,
73 scoped_refptr<base::SingleThreadTaskRunner> task_runner,
74 uintptr_t context_value)
75 : weak_watcher_(weak_watcher),
76 task_runner_(task_runner),
77 context_value_(context_value) {}
78 ~Context() {}
79
80 void Notify(MojoResult result,
81 MojoHandleSignalsState signals_state,
82 MojoWatcherNotificationFlags flags) {
83 if (result == MOJO_RESULT_CANCELLED) {
84 // The SimpleWatcher may have explicitly cancelled this watch, so we don't
85 // bother dispatching the notification - it would be ignored anyway.
86 //
87 // TODO(rockot): This shouldn't really be necessary, but there are already
88 // instances today where bindings object may be bound and subsequently
89 // closed due to pipe error, all before the thread's TaskRunner has been
90 // properly initialized.
91 base::AutoLock lock(lock_);
92 if (!enable_cancellation_notifications_)
93 return;
94 }
95
96 if ((flags & MOJO_WATCHER_NOTIFICATION_FLAG_FROM_SYSTEM) &&
97 task_runner_->RunsTasksOnCurrentThread() && weak_watcher_ &&
98 weak_watcher_->is_default_task_runner_) {
99 // System notifications will trigger from the task runner passed to
100 // mojo::edk::InitIPCSupport(). In Chrome this happens to always be the
101 // default task runner for the IO thread.
102 weak_watcher_->OnHandleReady(make_scoped_refptr(this), result);
103 } else {
104 task_runner_->PostTask(
105 FROM_HERE, base::Bind(&SimpleWatcher::OnHandleReady, weak_watcher_,
106 make_scoped_refptr(this), result));
107 }
108 }
109
110 const base::WeakPtr<SimpleWatcher> weak_watcher_;
111 const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
112 const uintptr_t context_value_;
113
114 base::Lock lock_;
115 bool enable_cancellation_notifications_ = true;
116
117 DISALLOW_COPY_AND_ASSIGN(Context);
118 };
119
120 SimpleWatcher::SimpleWatcher(const tracked_objects::Location& from_here,
121 ArmingPolicy arming_policy,
122 scoped_refptr<base::SingleThreadTaskRunner> runner)
123 : arming_policy_(arming_policy),
124 task_runner_(std::move(runner)),
125 is_default_task_runner_(task_runner_ ==
126 base::ThreadTaskRunnerHandle::Get()),
127 heap_profiler_tag_(from_here.file_name()),
128 weak_factory_(this) {
129 MojoResult rv = CreateWatcher(&Context::CallNotify, &watcher_handle_);
130 DCHECK_EQ(MOJO_RESULT_OK, rv);
131 DCHECK(task_runner_->BelongsToCurrentThread());
132 }
133
134 SimpleWatcher::~SimpleWatcher() {
135 if (IsWatching())
136 Cancel();
137 }
138
139 bool SimpleWatcher::IsWatching() const {
140 DCHECK(thread_checker_.CalledOnValidThread());
141 return context_ != nullptr;
142 }
143
144 MojoResult SimpleWatcher::Watch(Handle handle,
145 MojoHandleSignals signals,
146 const ReadyCallback& callback) {
147 DCHECK(thread_checker_.CalledOnValidThread());
148 DCHECK(!IsWatching());
149 DCHECK(!callback.is_null());
150
151 callback_ = callback;
152 handle_ = handle;
153
154 MojoResult watch_result = MOJO_RESULT_UNKNOWN;
155 context_ =
156 Context::Create(weak_factory_.GetWeakPtr(), task_runner_,
157 watcher_handle_.get(), handle_, signals, &watch_result);
158 if (!context_) {
159 handle_.set_value(kInvalidHandleValue);
160 callback_.Reset();
161 DCHECK_EQ(MOJO_RESULT_INVALID_ARGUMENT, watch_result);
162 return watch_result;
163 }
164
165 if (arming_policy_ == ArmingPolicy::AUTOMATIC)
166 ArmOrNotify();
167
168 return MOJO_RESULT_OK;
169 }
170
171 void SimpleWatcher::Cancel() {
172 DCHECK(thread_checker_.CalledOnValidThread());
173
174 // The watcher may have already been cancelled if the handle was closed.
175 if (!context_)
176 return;
177
178 context_->DisableCancellationNotifications();
179
180 handle_.set_value(kInvalidHandleValue);
181 callback_.Reset();
182
183 // Ensure |context_| is unset by the time we call MojoCancelWatch, as may
184 // re-enter the notification callback and we want to ensure |context_| is
185 // unset by then. This prevents the cancellation notification from reaching
yzshen1 2017/03/13 20:21:18 Is the last sentence a comment for line 178 instea
Ken Rockot(use gerrit already) 2017/03/13 22:00:10 Ah good catch. It used to be accurate here, but I
186 // OnHandleReady() when cancellation is explicit.
187 scoped_refptr<Context> context;
188 std::swap(context, context_);
189 MojoResult rv =
190 MojoCancelWatch(watcher_handle_.get().value(), context->value());
191
192 // It's possible this cancellation could race with a handle closure
193 // notification, in which case the watch may have already been implicitly
194 // cancelled.
195 DCHECK(rv == MOJO_RESULT_OK || rv == MOJO_RESULT_NOT_FOUND);
196 }
197
198 MojoResult SimpleWatcher::Arm(MojoResult* ready_result) {
199 DCHECK(thread_checker_.CalledOnValidThread());
200 uint32_t num_ready_contexts = 1;
201 uintptr_t ready_context;
202 MojoResult local_ready_result;
203 MojoHandleSignalsState ready_state;
204 MojoResult rv =
205 MojoArmWatcher(watcher_handle_.get().value(), &num_ready_contexts,
206 &ready_context, &local_ready_result, &ready_state);
207 if (rv == MOJO_RESULT_FAILED_PRECONDITION) {
208 DCHECK(context_);
209 DCHECK_EQ(1u, num_ready_contexts);
210 DCHECK_EQ(context_->value(), ready_context);
211 if (ready_result)
212 *ready_result = local_ready_result;
213 }
214
215 return rv;
216 }
217
218 void SimpleWatcher::ArmOrNotify() {
219 DCHECK(thread_checker_.CalledOnValidThread());
220
221 // Already cancelled, nothing to do.
222 if (!IsWatching())
223 return;
224
225 MojoResult ready_result;
226 MojoResult rv = Arm(&ready_result);
227 if (rv == MOJO_RESULT_OK)
228 return;
229
230 DCHECK_EQ(MOJO_RESULT_FAILED_PRECONDITION, rv);
231 task_runner_->PostTask(FROM_HERE, base::Bind(&SimpleWatcher::OnHandleReady,
232 weak_factory_.GetWeakPtr(),
233 context_, ready_result));
234 }
235
236 void SimpleWatcher::OnHandleReady(scoped_refptr<const Context> context,
237 MojoResult result) {
238 DCHECK(thread_checker_.CalledOnValidThread());
239
240 // This notification may be for a previously watched context, in which case
241 // we just ignore it.
242 if (context != context_)
243 return;
244
245 ReadyCallback callback = callback_;
246 if (result == MOJO_RESULT_CANCELLED) {
247 // Implicit cancellation due to someone closing the watched handle. We clear
248 // the SimppleWatcher's state before dispatching this.
249 context_ = nullptr;
250 handle_.set_value(kInvalidHandleValue);
251 callback_.Reset();
252 }
253
254 // NOTE: It's legal for |callback| to delete |this|.
255 if (!callback.is_null()) {
256 TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION event(heap_profiler_tag_);
257
258 base::WeakPtr<SimpleWatcher> weak_self = weak_factory_.GetWeakPtr();
259 callback.Run(result);
260 if (!weak_self)
261 return;
262
263 if (unsatisfiable_)
264 return;
265
266 // Prevent |MOJO_RESULT_FAILED_PRECONDITION| task spam by only notifying
267 // at most once in AUTOMATIC arming mode.
268 if (result == MOJO_RESULT_FAILED_PRECONDITION)
269 unsatisfiable_ = true;
270
271 if (arming_policy_ == ArmingPolicy::AUTOMATIC && IsWatching())
272 ArmOrNotify();
273 }
274 }
275
276 } // namespace mojo
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698