OLD | NEW |
| (Empty) |
1 // Copyright 2015 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 "third_party/mojo/src/mojo/edk/system/wait_set_dispatcher.h" | |
6 | |
7 #include <algorithm> | |
8 #include <utility> | |
9 | |
10 #include "base/logging.h" | |
11 #include "third_party/mojo/src/mojo/edk/system/awakable.h" | |
12 | |
13 namespace mojo { | |
14 namespace system { | |
15 | |
16 class WaitSetDispatcher::Waiter final : public Awakable { | |
17 public: | |
18 explicit Waiter(WaitSetDispatcher* dispatcher) : dispatcher_(dispatcher) {} | |
19 ~Waiter() {} | |
20 | |
21 // |Awakable| implementation. | |
22 bool Awake(MojoResult result, uintptr_t context) override { | |
23 // Note: This is called with various Mojo locks held. | |
24 dispatcher_->WakeDispatcher(result, context); | |
25 // Removes |this| from the dispatcher's list of waiters. | |
26 return false; | |
27 } | |
28 | |
29 private: | |
30 WaitSetDispatcher* const dispatcher_; | |
31 }; | |
32 | |
33 WaitSetDispatcher::WaitSetDispatcher() | |
34 : waiter_(new WaitSetDispatcher::Waiter(this)) {} | |
35 | |
36 WaitSetDispatcher::~WaitSetDispatcher() { | |
37 DCHECK(waiting_dispatchers_.empty()); | |
38 DCHECK(awoken_queue_.empty()); | |
39 DCHECK(processed_dispatchers_.empty()); | |
40 } | |
41 | |
42 Dispatcher::Type WaitSetDispatcher::GetType() const { | |
43 return Type::WAIT_SET; | |
44 } | |
45 | |
46 void WaitSetDispatcher::CloseImplNoLock() { | |
47 mutex().AssertHeld(); | |
48 for (const auto& entry : waiting_dispatchers_) | |
49 entry.second.dispatcher->RemoveAwakable(waiter_.get(), nullptr); | |
50 waiting_dispatchers_.clear(); | |
51 | |
52 MutexLocker locker(&awoken_mutex_); | |
53 awoken_queue_.clear(); | |
54 processed_dispatchers_.clear(); | |
55 } | |
56 | |
57 MojoResult WaitSetDispatcher::AddWaitingDispatcherImplNoLock( | |
58 const scoped_refptr<Dispatcher>& dispatcher, | |
59 MojoHandleSignals signals, | |
60 uintptr_t context) { | |
61 mutex().AssertHeld(); | |
62 if (dispatcher == this) | |
63 return MOJO_RESULT_INVALID_ARGUMENT; | |
64 | |
65 uintptr_t dispatcher_handle = reinterpret_cast<uintptr_t>(dispatcher.get()); | |
66 auto it = waiting_dispatchers_.find(dispatcher_handle); | |
67 if (it != waiting_dispatchers_.end()) { | |
68 return MOJO_RESULT_ALREADY_EXISTS; | |
69 } | |
70 | |
71 const MojoResult result = dispatcher->AddAwakable(waiter_.get(), signals, | |
72 dispatcher_handle, nullptr); | |
73 if (result == MOJO_RESULT_INVALID_ARGUMENT) { | |
74 // Dispatcher is closed. | |
75 return result; | |
76 } else if (result != MOJO_RESULT_OK) { | |
77 WakeDispatcher(result, dispatcher_handle); | |
78 } | |
79 | |
80 WaitState state; | |
81 state.dispatcher = dispatcher; | |
82 state.context = context; | |
83 state.signals = signals; | |
84 bool inserted = | |
85 waiting_dispatchers_.insert(std::make_pair(dispatcher_handle, state)) | |
86 .second; | |
87 DCHECK(inserted); | |
88 | |
89 return MOJO_RESULT_OK; | |
90 } | |
91 | |
92 MojoResult WaitSetDispatcher::RemoveWaitingDispatcherImplNoLock( | |
93 const scoped_refptr<Dispatcher>& dispatcher) { | |
94 mutex().AssertHeld(); | |
95 uintptr_t dispatcher_handle = reinterpret_cast<uintptr_t>(dispatcher.get()); | |
96 auto it = waiting_dispatchers_.find(dispatcher_handle); | |
97 if (it == waiting_dispatchers_.end()) | |
98 return MOJO_RESULT_NOT_FOUND; | |
99 | |
100 dispatcher->RemoveAwakable(waiter_.get(), nullptr); | |
101 // At this point, it should not be possible for |waiter_| to be woken with | |
102 // |dispatcher|. | |
103 waiting_dispatchers_.erase(it); | |
104 | |
105 MutexLocker locker(&awoken_mutex_); | |
106 int num_erased = 0; | |
107 for (auto it = awoken_queue_.begin(); it != awoken_queue_.end();) { | |
108 if (it->first == dispatcher_handle) { | |
109 it = awoken_queue_.erase(it); | |
110 num_erased++; | |
111 } else { | |
112 ++it; | |
113 } | |
114 } | |
115 // The dispatcher should only exist in the queue once. | |
116 DCHECK_LE(num_erased, 1); | |
117 processed_dispatchers_.erase( | |
118 std::remove(processed_dispatchers_.begin(), processed_dispatchers_.end(), | |
119 dispatcher_handle), | |
120 processed_dispatchers_.end()); | |
121 | |
122 return MOJO_RESULT_OK; | |
123 } | |
124 | |
125 MojoResult WaitSetDispatcher::GetReadyDispatchersImplNoLock( | |
126 UserPointer<uint32_t> count, | |
127 DispatcherVector* dispatchers, | |
128 UserPointer<MojoResult> results, | |
129 UserPointer<uintptr_t> contexts) { | |
130 mutex().AssertHeld(); | |
131 dispatchers->clear(); | |
132 | |
133 // Re-queue any already retrieved dispatchers. These should be the dispatchers | |
134 // that were returned on the last call to this function. This loop is | |
135 // necessary to preserve the logically level-triggering behaviour of waiting | |
136 // in Mojo. In particular, if no action is taken on a signal, that signal | |
137 // continues to be satisfied, and therefore a |MojoWait()| on that | |
138 // handle/signal continues to return immediately. | |
139 std::deque<uintptr_t> pending; | |
140 { | |
141 MutexLocker locker(&awoken_mutex_); | |
142 pending.swap(processed_dispatchers_); | |
143 } | |
144 for (uintptr_t d : pending) { | |
145 auto it = waiting_dispatchers_.find(d); | |
146 // Anything in |processed_dispatchers_| should also be in | |
147 // |waiting_dispatchers_| since dispatchers are removed from both in | |
148 // |RemoveWaitingDispatcherImplNoLock()|. | |
149 DCHECK(it != waiting_dispatchers_.end()); | |
150 | |
151 // |awoken_mutex_| cannot be held here because | |
152 // |Dispatcher::AddAwakable()| acquires the Dispatcher's mutex. This | |
153 // mutex is held while running |WakeDispatcher()| below, which needs to | |
154 // acquire |awoken_mutex_|. Holding |awoken_mutex_| here would result in | |
155 // a deadlock. | |
156 const MojoResult result = it->second.dispatcher->AddAwakable( | |
157 waiter_.get(), it->second.signals, d, nullptr); | |
158 | |
159 if (result == MOJO_RESULT_INVALID_ARGUMENT) { | |
160 // Dispatcher is closed. Implicitly remove it from the wait set since | |
161 // it may be impossible to remove using |MojoRemoveHandle()|. | |
162 waiting_dispatchers_.erase(it); | |
163 } else if (result != MOJO_RESULT_OK) { | |
164 WakeDispatcher(result, d); | |
165 } | |
166 } | |
167 | |
168 const uint32_t max_woken = count.Get(); | |
169 uint32_t num_woken = 0; | |
170 | |
171 MutexLocker locker(&awoken_mutex_); | |
172 while (!awoken_queue_.empty() && num_woken < max_woken) { | |
173 uintptr_t d = awoken_queue_.front().first; | |
174 MojoResult result = awoken_queue_.front().second; | |
175 awoken_queue_.pop_front(); | |
176 | |
177 auto it = waiting_dispatchers_.find(d); | |
178 DCHECK(it != waiting_dispatchers_.end()); | |
179 | |
180 results.At(num_woken).Put(result); | |
181 dispatchers->push_back(it->second.dispatcher); | |
182 if (!contexts.IsNull()) | |
183 contexts.At(num_woken).Put(it->second.context); | |
184 | |
185 if (result != MOJO_RESULT_CANCELLED) { | |
186 processed_dispatchers_.push_back(d); | |
187 } else { | |
188 waiting_dispatchers_.erase(it); | |
189 } | |
190 | |
191 num_woken++; | |
192 } | |
193 | |
194 count.Put(num_woken); | |
195 if (!num_woken) | |
196 return MOJO_RESULT_SHOULD_WAIT; | |
197 | |
198 return MOJO_RESULT_OK; | |
199 } | |
200 | |
201 void WaitSetDispatcher::CancelAllAwakablesNoLock() { | |
202 mutex().AssertHeld(); | |
203 MutexLocker locker(&awakable_mutex_); | |
204 awakable_list_.CancelAll(); | |
205 } | |
206 | |
207 MojoResult WaitSetDispatcher::AddAwakableImplNoLock( | |
208 Awakable* awakable, | |
209 MojoHandleSignals signals, | |
210 uintptr_t context, | |
211 HandleSignalsState* signals_state) { | |
212 mutex().AssertHeld(); | |
213 | |
214 HandleSignalsState state(GetHandleSignalsStateImplNoLock()); | |
215 if (state.satisfies(signals)) { | |
216 if (signals_state) | |
217 *signals_state = state; | |
218 return MOJO_RESULT_ALREADY_EXISTS; | |
219 } | |
220 if (!state.can_satisfy(signals)) { | |
221 if (signals_state) | |
222 *signals_state = state; | |
223 return MOJO_RESULT_FAILED_PRECONDITION; | |
224 } | |
225 | |
226 MutexLocker locker(&awakable_mutex_); | |
227 awakable_list_.Add(awakable, signals, context); | |
228 return MOJO_RESULT_OK; | |
229 } | |
230 | |
231 void WaitSetDispatcher::RemoveAwakableImplNoLock( | |
232 Awakable* awakable, | |
233 HandleSignalsState* signals_state) { | |
234 mutex().AssertHeld(); | |
235 MutexLocker locker(&awakable_mutex_); | |
236 awakable_list_.Remove(awakable); | |
237 if (signals_state) | |
238 *signals_state = GetHandleSignalsStateImplNoLock(); | |
239 } | |
240 | |
241 HandleSignalsState WaitSetDispatcher::GetHandleSignalsStateImplNoLock() const { | |
242 mutex().AssertHeld(); | |
243 HandleSignalsState rv; | |
244 rv.satisfiable_signals = MOJO_HANDLE_SIGNAL_READABLE; | |
245 MutexLocker locker(&awoken_mutex_); | |
246 if (!awoken_queue_.empty() || !processed_dispatchers_.empty()) | |
247 rv.satisfied_signals = MOJO_HANDLE_SIGNAL_READABLE; | |
248 return rv; | |
249 } | |
250 | |
251 scoped_refptr<Dispatcher> | |
252 WaitSetDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock() { | |
253 mutex().AssertHeld(); | |
254 LOG(ERROR) << "Attempting to serialize WaitSet"; | |
255 CloseImplNoLock(); | |
256 return new WaitSetDispatcher(); | |
257 } | |
258 | |
259 void WaitSetDispatcher::WakeDispatcher(MojoResult result, uintptr_t context) { | |
260 { | |
261 MutexLocker locker(&awoken_mutex_); | |
262 | |
263 if (result == MOJO_RESULT_ALREADY_EXISTS) | |
264 result = MOJO_RESULT_OK; | |
265 | |
266 awoken_queue_.push_back(std::make_pair(context, result)); | |
267 } | |
268 | |
269 MutexLocker locker(&awakable_mutex_); | |
270 HandleSignalsState signals_state; | |
271 signals_state.satisfiable_signals = MOJO_HANDLE_SIGNAL_READABLE; | |
272 signals_state.satisfied_signals = MOJO_HANDLE_SIGNAL_READABLE; | |
273 awakable_list_.AwakeForStateChange(signals_state); | |
274 } | |
275 | |
276 } // namespace system | |
277 } // namespace mojo | |
OLD | NEW |