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

Side by Side Diff: mojo/edk/system/watcher_dispatcher.cc

Issue 2725133002: Mojo: Armed Watchers (Closed)
Patch Set: . 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/edk/system/watcher_dispatcher.h"
6
7 #include <limits>
8 #include <map>
9
10 #include "base/macros.h"
11 #include "base/memory/ptr_util.h"
12 #include "mojo/edk/system/watch.h"
13
14 namespace mojo {
15 namespace edk {
16
17 // Helper to track contexts relevant to a specific handle. This is used to avoid
18 // redundant lookups when notifying interested watches about a handle change.
19 //
20 // Not thread-safe: Should only be accessed while the dispatcher holds its lock,
21 // with the exception that during watch removal these may be moved out of the
22 // WatcherDispatcher and safely used without locking.
23 class WatcherDispatcher::WatchedHandle {
24 public:
25 struct WatchEntry {
26 explicit WatchEntry(const scoped_refptr<Watch>& watch) : watch(watch) {}
27 ~WatchEntry() {}
28
29 // Indicates whether the last computed MojoResult (combinatation of
30 // watched signals and current signal state) indicates that a notification
31 // should happen when our watcher is armed.
32 //
33 // |MOJO_RESULT_CANCELLED| is not included in the set of "ready" results
34 // because cancellation is a one-time event that always fires immediately.
35 bool ready() const {
36 return last_known_result != MOJO_RESULT_SHOULD_WAIT &&
37 last_known_result != MOJO_RESULT_CANCELLED;
38 }
39
40 scoped_refptr<Watch> watch;
41 MojoResult last_known_result = MOJO_RESULT_UNKNOWN;
42 };
43
44 using WatchMap = std::map<uintptr_t, std::unique_ptr<WatchEntry>>;
45
46 explicit WatchedHandle(const scoped_refptr<Dispatcher>& dispatcher)
47 : dispatcher_(dispatcher) {}
48 ~WatchedHandle() {}
49
50 void AddWatch(uintptr_t context, const scoped_refptr<Watch>& watch) {
51 auto result = watches_.insert(
52 std::make_pair(context, base::MakeUnique<WatchEntry>(watch)));
53 DCHECK(result.second);
54 }
55
56 std::unique_ptr<WatchEntry> RemoveWatch(uintptr_t context) {
57 auto it = watches_.find(context);
58 DCHECK(it != watches_.end());
59
60 std::unique_ptr<WatchEntry> entry = std::move(it->second);
61 watches_.erase(it);
62 return entry;
63 }
64
65 bool NotifyState(const HandleSignalsState& state,
66 bool allowed_to_call_callback) {
67 last_known_signals_state_ = state;
68 bool handle_ready = false;
69 for (auto& entry : watches_) {
70 MojoResult rv =
71 entry.second->watch->NotifyState(state, allowed_to_call_callback);
72 if (rv != MOJO_RESULT_SHOULD_WAIT && rv != MOJO_RESULT_CANCELLED)
73 handle_ready |= true;
74 entry.second->last_known_result = rv;
75 }
76 return handle_ready;
77 }
78
79 void CancelAllWatches() {
80 for (auto& entry : watches_)
81 entry.second->watch->Cancel();
82 }
83
84 // Determines if any watch on this handle is ready.
85 bool IsAnyWatchReady() const {
86 for (auto& entry : watches_)
87 if (entry.second->ready())
88 return true;
89 return false;
90 }
91
92 const WatchMap& watches() const { return watches_; }
93
94 // Stores at most |max_contexts| elements at the output locations, regarding
95 // ready contexts associated with this handle. Returns the number of elements
96 // actually stored.
97 //
98 // This is used to implement WatcherDispatcher::Arm()'s already-satisfied/
99 // already-unsatisfiable failure mode by filling in a user-provided buffer
100 // with as much information as we can fit.
101 uint32_t StoreReadyContextState(uint32_t max_contexts,
102 uintptr_t* out_contexts,
103 MojoResult* out_results,
104 MojoHandleSignalsState* out_states) {
105 uint32_t num_contexts = 0;
106 for (auto& entry : watches_) {
107 if (!max_contexts)
108 return num_contexts;
109
110 if (entry.second->last_known_result == MOJO_RESULT_OK ||
111 entry.second->last_known_result == MOJO_RESULT_FAILED_PRECONDITION) {
112 out_contexts[num_contexts] = entry.first;
113 out_results[num_contexts] = entry.second->last_known_result;
114 out_states[num_contexts] =
115 static_cast<MojoHandleSignalsState>(last_known_signals_state_);
116 num_contexts++;
117 max_contexts--;
118 }
119 }
120
121 return num_contexts;
122 }
123
124 private:
125 // The dispatcher to which all these Watch instances belong.
126 const scoped_refptr<Dispatcher> dispatcher_;
127
128 // A ref to every Watch actively watching |dispatcher_| on behalf of our
129 // owning watcher.
130 WatchMap watches_;
131
132 // The last known state of |dispatcher_|. This is tracked for quick access in
133 // StoreReadyContextState().
134 HandleSignalsState last_known_signals_state_ = {0, 0};
135
136 DISALLOW_COPY_AND_ASSIGN(WatchedHandle);
137 };
138
139 WatcherDispatcher::WatcherDispatcher(MojoWatcherCallback callback)
140 : callback_(callback) {}
141
142 void WatcherDispatcher::NotifyHandleState(Dispatcher* dispatcher,
143 const HandleSignalsState& state) {
144 base::AutoLock lock(lock_);
145 auto it = watched_handles_.find(dispatcher);
146 if (it == watched_handles_.end())
147 return;
148
149 // Maybe fire a notification to one or more watches assoicated with the
150 // dispatcher, provided we're armed and at least one of the watches cares
151 // about the new state.
152 if (it->second->NotifyState(state, armed_)) {
153 ready_handles_.insert(it->second.get());
154
155 // If we were armed, we also notified watchers. Ensure we're disarmed now.
156 armed_ = false;
157 } else {
158 ready_handles_.erase(it->second.get());
159 }
160 }
161
162 void WatcherDispatcher::NotifyHandleClosed(Dispatcher* dispatcher) {
163 std::unique_ptr<WatchedHandle> handle;
164 {
165 base::AutoLock lock(lock_);
166 auto it = watched_handles_.find(dispatcher);
167 if (it == watched_handles_.end())
168 return;
169
170 handle = std::move(it->second);
171
172 // Wipe out all watches associated with the closed dispatcher.
173 for (auto& entry : handle->watches())
174 watches_.erase(entry.first);
175 ready_handles_.erase(handle.get());
176 watched_handles_.erase(it);
177 }
178
179 // NOTE: It's important that this is called outside of |lock_| since it
180 // acquires internal Watch locks.
181 handle->CancelAllWatches();
182 }
183
184 void WatcherDispatcher::InvokeWatchCallback(
185 uintptr_t context,
186 MojoResult result,
187 const HandleSignalsState& state,
188 MojoWatcherNotificationFlags flags) {
189 {
190 // We avoid holding the lock during dispatch. It's OK for notification
191 // callbacks to close this watcher, and it's OK for notifications to race
192 // with closure, if for example the watcher is closed from another thread
193 // between this test and the invocation of |callback_| below.
194 //
195 // Because cancellation synchronously blocks all future notifications, and
196 // because notifications themselves are mutually exclusive for any given
197 // context, we still guarantee that a single MOJO_RESULT_CANCELLED result
198 // is the last notification received for any given context.
199 //
200 // This guarantee is sufficient to make safe, synchronized, per-context
201 // state management possible in user code.
202 base::AutoLock lock(lock_);
203 if (closed_ && result != MOJO_RESULT_CANCELLED)
204 return;
205 }
206
207 callback_(context, result, static_cast<MojoHandleSignalsState>(state), flags);
208 }
209
210 Dispatcher::Type WatcherDispatcher::GetType() const {
211 return Type::WATCHER;
212 }
213
214 MojoResult WatcherDispatcher::Close() {
215 // We swap out all the watched handle information onto the stack so we can
216 // call into their dispatchers without our own lock held.
217 std::map<uintptr_t, scoped_refptr<Watch>> watches;
218 {
219 base::AutoLock lock(lock_);
220 DCHECK(!closed_);
221 closed_ = true;
222 std::swap(watches, watches_);
223 watched_handles_.clear();
224 }
225
226 // Remove all refs from our watched dispatchers and fire cancellations.
227 for (auto& entry : watches) {
228 entry.second->dispatcher()->RemoveWatcherRef(this, entry.first);
229 entry.second->Cancel();
230 }
231
232 return MOJO_RESULT_OK;
233 }
234
235 MojoResult WatcherDispatcher::WatchDispatcher(
236 scoped_refptr<Dispatcher> dispatcher,
237 MojoHandleSignals signals,
238 uintptr_t context) {
239 // NOTE: Because it's critical to avoid acquiring any other dispatcher locks
240 // while |lock_| is held, we defer adding oursevles to the dispatcher until
241 // after we've updated all our own relevant state and released |lock_|.
242 {
243 base::AutoLock lock(lock_);
244 if (watches_.find(context) != watches_.end())
245 return MOJO_RESULT_ALREADY_EXISTS;
246
247 auto it = watched_handles_.find(dispatcher.get());
248 if (it == watched_handles_.end()) {
249 auto result = watched_handles_.insert(std::make_pair(
250 dispatcher.get(), base::MakeUnique<WatchedHandle>(dispatcher)));
251 DCHECK(result.second);
252 it = result.first;
253 }
254
255 scoped_refptr<Watch> watch = new Watch(this, dispatcher, context, signals);
256 watches_.insert({context, watch});
257 it->second->AddWatch(context, watch);
258 }
259
260 MojoResult rv = dispatcher->AddWatcherRef(this, context);
261 if (rv != MOJO_RESULT_OK) {
262 // Oops. This was not a valid handle to watch. Undo the above work and
263 // fail gracefully. Note that other watches may have been added for the
264 // handle since we last held the lock, so we can't necessarily just wipe
265 // out the |watched_handles_| entry.
266 base::AutoLock lock(lock_);
267 watches_.erase(context);
268
269 // Also note that it's possible for someone to have closed this
270 // WatcherDispatcher since we last held |lock_|, so the entry might already
271 // be gone.
272 auto it = watched_handles_.find(dispatcher.get());
273 if (it != watched_handles_.end()) {
274 it->second->RemoveWatch(context);
275 if (it->second->watches().empty())
276 watched_handles_.erase(dispatcher.get());
277 }
278
279 return rv;
280 }
281
282 return MOJO_RESULT_OK;
283 }
284
285 MojoResult WatcherDispatcher::CancelWatch(uintptr_t context) {
286 // We may remove the last stored ref to the Watch below, so we retain
287 // a reference on the stack.
288 scoped_refptr<Watch> watch;
289 {
290 base::AutoLock lock(lock_);
291 auto it = watches_.find(context);
292 if (it == watches_.end())
293 return MOJO_RESULT_NOT_FOUND;
294 watch = it->second;
295 watches_.erase(it);
296 }
297
298 // Mark the watch as cancelled so no further notifications get through.
299 watch->Cancel();
300
301 // We remove the watcher ref for this context before updating any more
302 // internal watcher state, ensuring that we don't receiving further
303 // notifications for this context.
304 watch->dispatcher()->RemoveWatcherRef(this, context);
305
306 {
307 base::AutoLock lock(lock_);
308 auto handle_it = watched_handles_.find(watch->dispatcher().get());
309 DCHECK(handle_it != watched_handles_.end());
310
311 std::unique_ptr<WatchedHandle::WatchEntry> entry =
312 handle_it->second->RemoveWatch(context);
313
314 if (handle_it->second->watches().empty()) {
315 // This was the only remaining watch we had for its dispatcher, so we
316 // can wipe out all knowledge of that dispatcher.
317 ready_handles_.erase(handle_it->second.get());
318 watched_handles_.erase(handle_it);
319 } else if (entry->ready() && !handle_it->second->IsAnyWatchReady()) {
320 // There are other watches for this dispatcher, but this was the only one
321 // keeping it in a "ready" state. Remove the handle from the ready-set.
322 ready_handles_.erase(handle_it->second.get());
323 }
324 }
325
326 return MOJO_RESULT_OK;
327 }
328
329 MojoResult WatcherDispatcher::Arm(
330 uint32_t* num_ready_contexts,
331 uintptr_t* ready_contexts,
332 MojoResult* ready_results,
333 MojoHandleSignalsState* ready_signals_states) {
334 base::AutoLock lock(lock_);
335 if (num_ready_contexts &&
336 (!ready_contexts || !ready_results || !ready_signals_states)) {
337 return MOJO_RESULT_INVALID_ARGUMENT;
338 }
339
340 if (watched_handles_.empty())
341 return MOJO_RESULT_NOT_FOUND;
342
343 if (ready_handles_.empty()) {
344 // Fast path: No handles are ready to notify, so we're done.
345 armed_ = true;
346 return MOJO_RESULT_OK;
347 }
348
349 if (num_ready_contexts) {
350 uint32_t max_ready_contexts = *num_ready_contexts;
351 *num_ready_contexts = 0;
352 for (auto& handles : watched_handles_) {
yzshen1 2017/03/11 00:44:58 nit: handles -> handle?
Ken Rockot(use gerrit already) 2017/03/12 22:24:13 Done
353 if (max_ready_contexts == 0)
354 break;
355 uint32_t n = handles.second->StoreReadyContextState(
356 max_ready_contexts, ready_contexts, ready_results,
357 ready_signals_states);
358 DCHECK_GE(max_ready_contexts, n);
359
360 *num_ready_contexts += n;
361 ready_contexts += n;
362 ready_results += n;
363 ready_signals_states += n;
364 max_ready_contexts -= n;
365 }
366 }
367
368 return MOJO_RESULT_FAILED_PRECONDITION;
369 }
370
371 WatcherDispatcher::~WatcherDispatcher() {}
372
373 } // namespace edk
374 } // namespace mojo
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698