OLD | NEW |
---|---|
(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 | |
OLD | NEW |