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

Side by Side Diff: mojo/edk/system/watcher_dispatcher.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/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 WatcherDispatcher::WatcherDispatcher(MojoWatcherCallback callback)
18 : callback_(callback) {}
19
20 void WatcherDispatcher::NotifyHandleState(Dispatcher* dispatcher,
21 const HandleSignalsState& state) {
22 base::AutoLock lock(lock_);
23 auto it = watched_handles_.find(dispatcher);
24 if (it == watched_handles_.end())
25 return;
26
27 // Maybe fire a notification to the watch assoicated with this dispatcher,
28 // provided we're armed it cares about the new state.
29 if (it->second->NotifyState(state, armed_)) {
30 ready_watches_.insert(it->second.get());
31
32 // If we were armed and got here, we notified the watch. Disarm.
33 armed_ = false;
34 } else {
35 ready_watches_.erase(it->second.get());
36 }
37 }
38
39 void WatcherDispatcher::NotifyHandleClosed(Dispatcher* dispatcher) {
40 scoped_refptr<Watch> watch;
41 {
42 base::AutoLock lock(lock_);
43 auto it = watched_handles_.find(dispatcher);
44 if (it == watched_handles_.end())
45 return;
46
47 watch = std::move(it->second);
48
49 // Wipe out all state associated with the closed dispatcher.
50 watches_.erase(watch->context());
51 ready_watches_.erase(watch.get());
52 watched_handles_.erase(it);
53 }
54
55 // NOTE: It's important that this is called outside of |lock_| since it
56 // acquires internal Watch locks.
57 watch->Cancel();
58 }
59
60 void WatcherDispatcher::InvokeWatchCallback(
61 uintptr_t context,
62 MojoResult result,
63 const HandleSignalsState& state,
64 MojoWatcherNotificationFlags flags) {
65 {
66 // We avoid holding the lock during dispatch. It's OK for notification
67 // callbacks to close this watcher, and it's OK for notifications to race
68 // with closure, if for example the watcher is closed from another thread
69 // between this test and the invocation of |callback_| below.
70 //
71 // Because cancellation synchronously blocks all future notifications, and
72 // because notifications themselves are mutually exclusive for any given
73 // context, we still guarantee that a single MOJO_RESULT_CANCELLED result
74 // is the last notification received for any given context.
75 //
76 // This guarantee is sufficient to make safe, synchronized, per-context
77 // state management possible in user code.
78 base::AutoLock lock(lock_);
79 if (closed_ && result != MOJO_RESULT_CANCELLED)
80 return;
81 }
82
83 callback_(context, result, static_cast<MojoHandleSignalsState>(state), flags);
84 }
85
86 Dispatcher::Type WatcherDispatcher::GetType() const {
87 return Type::WATCHER;
88 }
89
90 MojoResult WatcherDispatcher::Close() {
91 // We swap out all the watched handle information onto the stack so we can
92 // call into their dispatchers without our own lock held.
93 std::map<uintptr_t, scoped_refptr<Watch>> watches;
94 {
95 base::AutoLock lock(lock_);
96 DCHECK(!closed_);
97 closed_ = true;
98 std::swap(watches, watches_);
99 watched_handles_.clear();
100 }
101
102 // Remove all refs from our watched dispatchers and fire cancellations.
103 for (auto& entry : watches) {
104 entry.second->dispatcher()->RemoveWatcherRef(this, entry.first);
105 entry.second->Cancel();
106 }
107
108 return MOJO_RESULT_OK;
109 }
110
111 MojoResult WatcherDispatcher::WatchDispatcher(
112 scoped_refptr<Dispatcher> dispatcher,
113 MojoHandleSignals signals,
114 uintptr_t context) {
115 // NOTE: Because it's critical to avoid acquiring any other dispatcher locks
116 // while |lock_| is held, we defer adding oursevles to the dispatcher until
117 // after we've updated all our own relevant state and released |lock_|.
118 {
119 base::AutoLock lock(lock_);
120 if (watches_.count(context) || watched_handles_.count(dispatcher.get()))
yzshen1 2017/03/13 20:21:18 [just to double check] IIUC, we want |context| to
Ken Rockot(use gerrit already) 2017/03/13 22:00:10 Correct. Unlike the WaitSet API, I didn't want wat
121 return MOJO_RESULT_ALREADY_EXISTS;
122
123 scoped_refptr<Watch> watch = new Watch(this, dispatcher, context, signals);
124 watches_.insert({context, watch});
125 auto result =
126 watched_handles_.insert(std::make_pair(dispatcher.get(), watch));
127 DCHECK(result.second);
128 }
129
130 MojoResult rv = dispatcher->AddWatcherRef(this, context);
131 if (rv != MOJO_RESULT_OK) {
132 // Oops. This was not a valid handle to watch. Undo the above work and
133 // fail gracefully.
134 base::AutoLock lock(lock_);
135 watches_.erase(context);
136 watched_handles_.erase(dispatcher.get());
137 return rv;
138 }
139
140 return MOJO_RESULT_OK;
141 }
142
143 MojoResult WatcherDispatcher::CancelWatch(uintptr_t context) {
144 // We may remove the last stored ref to the Watch below, so we retain
145 // a reference on the stack.
146 scoped_refptr<Watch> watch;
147 {
148 base::AutoLock lock(lock_);
149 auto it = watches_.find(context);
150 if (it == watches_.end())
151 return MOJO_RESULT_NOT_FOUND;
152 watch = it->second;
153 watches_.erase(it);
154 }
155
156 // Mark the watch as cancelled so no further notifications get through.
157 watch->Cancel();
158
159 // We remove the watcher ref for this context before updating any more
160 // internal watcher state, ensuring that we don't receiving further
161 // notifications for this context.
162 watch->dispatcher()->RemoveWatcherRef(this, context);
163
164 {
165 base::AutoLock lock(lock_);
166 auto handle_it = watched_handles_.find(watch->dispatcher().get());
167 DCHECK(handle_it != watched_handles_.end());
168 ready_watches_.erase(handle_it->second.get());
169 watched_handles_.erase(handle_it);
170 }
171
172 return MOJO_RESULT_OK;
173 }
174
175 MojoResult WatcherDispatcher::Arm(
176 uint32_t* num_ready_contexts,
177 uintptr_t* ready_contexts,
178 MojoResult* ready_results,
179 MojoHandleSignalsState* ready_signals_states) {
180 base::AutoLock lock(lock_);
181 if (num_ready_contexts &&
182 (!ready_contexts || !ready_results || !ready_signals_states)) {
183 return MOJO_RESULT_INVALID_ARGUMENT;
184 }
185
186 if (watched_handles_.empty())
187 return MOJO_RESULT_NOT_FOUND;
188
189 if (ready_watches_.empty()) {
190 // Fast path: No watches are ready to notify, so we're done.
191 armed_ = true;
192 return MOJO_RESULT_OK;
193 }
194
195 if (num_ready_contexts) {
196 uint32_t max_ready_contexts = *num_ready_contexts;
197 *num_ready_contexts = 0;
198 for (auto& entry : watched_handles_) {
199 if (max_ready_contexts == *num_ready_contexts)
200 break;
201
202 const Watch* watch = entry.second.get();
203 if (!watch->ready())
204 continue;
205
206 *(ready_contexts++) = watch->context();
207 *(ready_results++) = watch->last_known_result();
208 *(ready_signals_states++) = watch->last_known_signals_state();
209 ++(*num_ready_contexts);
210 }
211 }
212
213 return MOJO_RESULT_FAILED_PRECONDITION;
214 }
215
216 WatcherDispatcher::~WatcherDispatcher() {}
217
218 } // namespace edk
219 } // namespace mojo
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698