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

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

Issue 2070523003: EDK: wait sets: Implement WaitSetDispatcher::WaitSet{AddRemove}Impl(). (Closed) Base URL: https://github.com/domokit/mojo.git@work796_wait_set_3.5
Patch Set: many fixes Created 4 years, 6 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
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "mojo/edk/system/wait_set_dispatcher.h" 5 #include "mojo/edk/system/wait_set_dispatcher.h"
6 6
7 #include <utility>
8
7 #include "base/logging.h" 9 #include "base/logging.h"
10 #include "mojo/edk/system/configuration.h"
8 #include "mojo/edk/system/options_validation.h" 11 #include "mojo/edk/system/options_validation.h"
9 12
10 using mojo::util::MutexLocker; 13 using mojo::util::MutexLocker;
11 using mojo::util::RefPtr; 14 using mojo::util::RefPtr;
12 15
13 namespace mojo { 16 namespace mojo {
14 namespace system { 17 namespace system {
15 18
19 WaitSetDispatcher::Entry::Entry(MojoHandleSignals signals, uint64_t cookie)
20 : signals(signals), cookie(cookie) {}
21
22 WaitSetDispatcher::Entry::~Entry() {}
23
16 // static 24 // static
17 constexpr MojoHandleRights WaitSetDispatcher::kDefaultHandleRights; 25 constexpr MojoHandleRights WaitSetDispatcher::kDefaultHandleRights;
18 26
19 // static 27 // static
20 const MojoCreateWaitSetOptions WaitSetDispatcher::kDefaultCreateOptions = { 28 const MojoCreateWaitSetOptions WaitSetDispatcher::kDefaultCreateOptions = {
21 static_cast<uint32_t>(sizeof(MojoCreateWaitSetOptions)), 29 static_cast<uint32_t>(sizeof(MojoCreateWaitSetOptions)),
22 MOJO_CREATE_WAIT_SET_OPTIONS_FLAG_NONE}; 30 MOJO_CREATE_WAIT_SET_OPTIONS_FLAG_NONE};
23 31
24 // static 32 // static
25 MojoResult WaitSetDispatcher::ValidateCreateOptions( 33 MojoResult WaitSetDispatcher::ValidateCreateOptions(
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
87 bool WaitSetDispatcher::SupportsEntrypointClass( 95 bool WaitSetDispatcher::SupportsEntrypointClass(
88 EntrypointClass entrypoint_class) const { 96 EntrypointClass entrypoint_class) const {
89 return (entrypoint_class == EntrypointClass::NONE || 97 return (entrypoint_class == EntrypointClass::NONE ||
90 entrypoint_class == EntrypointClass::WAIT_SET); 98 entrypoint_class == EntrypointClass::WAIT_SET);
91 } 99 }
92 100
93 WaitSetDispatcher::WaitSetDispatcher() {} 101 WaitSetDispatcher::WaitSetDispatcher() {}
94 102
95 WaitSetDispatcher::~WaitSetDispatcher() {} 103 WaitSetDispatcher::~WaitSetDispatcher() {}
96 104
105 void WaitSetDispatcher::CloseImplNoLock() {
106 mutex().AssertHeld();
107
108 CookieToEntryMap entries;
109 std::swap(entries_, entries);
110 possibly_triggered_head_ = nullptr;
111 possibly_triggered_tail_ = nullptr;
112 possibly_triggered_count_ = 0u;
113
114 // We want to remove the awakables outside the lock, so we have to unlock
115 // |mutex()|. Note that while unlocked, |Awake()| may get called.
116 // TODO(vtl): This is pretty terrible, but changing it would require pretty
117 // invasive changes in many other places. We really count on |Dispatcher| not
118 // doing anything interesting after calling |CloseImplNoLock()|, and since
119 // |CloseImplNoLock()| is allowed to do nothing all the lock invariants are
120 // satisfied.
121 DCHECK(is_closed_no_lock());
122 mutex().Unlock();
123
124 for (auto& p : entries) {
125 const auto& entry = p.second;
126 if (entry->dispatcher)
127 entry->dispatcher->RemoveAwakableWithContext(this, entry->cookie,
128 nullptr);
129 }
130
131 // The caller of |CloseImplNoLock()| expects |mutex()| to be locked, so we
132 // have to re-lock it (even though it really should do nothing afterwards).
133 mutex().Lock();
134 }
135
97 RefPtr<Dispatcher> 136 RefPtr<Dispatcher>
98 WaitSetDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock( 137 WaitSetDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock(
99 MessagePipe* /*message_pipe*/, 138 MessagePipe* /*message_pipe*/,
100 unsigned /*port*/) { 139 unsigned /*port*/) {
101 mutex().AssertHeld(); 140 mutex().AssertHeld();
102 NOTREACHED(); 141 NOTREACHED();
103 return nullptr; 142 return nullptr;
104 } 143 }
105 144
106 MojoResult WaitSetDispatcher::WaitSetAddImpl( 145 MojoResult WaitSetDispatcher::WaitSetAddImpl(
107 UserPointer<const MojoWaitSetAddOptions> options, 146 UserPointer<const MojoWaitSetAddOptions> options,
108 Handle&& handle, 147 RefPtr<Dispatcher>&& dispatcher,
109 MojoHandleSignals signals, 148 MojoHandleSignals signals,
110 uint64_t cookie) { 149 uint64_t cookie) {
111 MutexLocker locker(&mutex()); 150 Entry* entry = nullptr;
112 if (is_closed_no_lock()) 151 {
152 MutexLocker locker(&mutex());
153 if (is_closed_no_lock())
154 return MOJO_RESULT_INVALID_ARGUMENT;
155 if (is_busy_)
156 return MOJO_RESULT_BUSY;
157 MojoWaitSetAddOptions validated_options;
158 MojoResult result = ValidateWaitSetAddOptions(options, &validated_options);
159 if (result != MOJO_RESULT_OK)
160 return result;
161 if (entries_.find(cookie) != entries_.end())
162 return MOJO_RESULT_ALREADY_EXISTS;
163 if (entries_.size() >= GetConfiguration().max_wait_set_num_entries)
164 return MOJO_RESULT_RESOURCE_EXHAUSTED;
165 // Note: We'll have to set the entry's dispatcher later.
166 entry = new Entry(signals, cookie);
167 entries_[cookie] = std::unique_ptr<Entry>(entry);
168
169 is_busy_ = true;
170 }
171
172 HandleSignalsState signals_state;
173 MojoResult result = dispatcher->AddAwakableUnconditional(
174 this, signals, cookie, &signals_state);
175
176 // Can't use |MutexLocker|, since we need to do some work outside the lock
177 // in some code paths.
178 mutex().Lock();
179 DCHECK(is_busy_);
180 is_busy_ = false;
181
182 // Note: We may have been closed while |mutex()| was unlocked, so we have to
183 // check again!
184 if (is_closed_no_lock()) {
185 // Warning: In this case, |entry| has been invalidated, since it was owned
186 // by |entries_|.
187 DCHECK(entries_.empty());
188 mutex().Unlock();
189 if (result == MOJO_RESULT_OK || result == MOJO_RESULT_ALREADY_EXISTS) {
190 // We have to remove ourself from the target dispatcher's awakable list.
191 dispatcher->RemoveAwakableWithContext(this, cookie, nullptr);
192 }
113 return MOJO_RESULT_INVALID_ARGUMENT; 193 return MOJO_RESULT_INVALID_ARGUMENT;
114 MojoWaitSetAddOptions validated_options; 194 }
115 MojoResult result = ValidateWaitSetAddOptions(options, &validated_options); 195
116 if (result != MOJO_RESULT_OK) 196 DCHECK(entries_.find(cookie) != entries_.end());
197 DCHECK_EQ(entries_[cookie].get(), entry);
198
199 if (result == MOJO_RESULT_ALREADY_EXISTS) {
200 // It was added, but the wait condition is already satisfied.
201 AddPossiblyTriggeredNoLock(entry, Entry::TriggerState::POSSIBLY_SATISFIED);
202 } else if (result == MOJO_RESULT_FAILED_PRECONDITION) {
203 // The condition is never-satisfiable. Leave a zombie entry (i.e., leave
204 // |dispatcher| null).
205 mutex().Unlock();
206 return MOJO_RESULT_OK;
207 } else if (result != MOJO_RESULT_OK) {
208 size_t num_erased = entries_.erase(cookie);
209 DCHECK_EQ(num_erased, 1u);
210 mutex().Unlock();
117 return result; 211 return result;
212 }
118 213
119 // TODO(vtl) 214 // Update the entry to actually have the dispatcher.
120 NOTIMPLEMENTED(); 215 entry->dispatcher = std::move(dispatcher);
121 return MOJO_RESULT_UNIMPLEMENTED; 216
217 mutex().Unlock();
218 return MOJO_RESULT_OK;
122 } 219 }
123 220
124 MojoResult WaitSetDispatcher::WaitSetRemoveImpl(uint64_t cookie) { 221 MojoResult WaitSetDispatcher::WaitSetRemoveImpl(uint64_t cookie) {
125 MutexLocker locker(&mutex()); 222 RefPtr<Dispatcher> dispatcher;
126 if (is_closed_no_lock()) 223 {
127 return MOJO_RESULT_INVALID_ARGUMENT; 224 MutexLocker locker(&mutex());
225 if (is_closed_no_lock())
226 return MOJO_RESULT_INVALID_ARGUMENT;
227 if (is_busy_)
228 return MOJO_RESULT_BUSY;
229 auto it = entries_.find(cookie);
230 if (it == entries_.end())
231 return MOJO_RESULT_NOT_FOUND;
128 232
129 // TODO(vtl) 233 Entry* entry = it->second.get();
130 NOTIMPLEMENTED(); 234 // We'll remove ourself from the target dispatcher's awakable list outside
131 return MOJO_RESULT_UNIMPLEMENTED; 235 // the lock.
236 dispatcher = std::move(entry->dispatcher);
237
238 if (entry->trigger_state != Entry::TriggerState::NOT_TRIGGERED)
239 RemovePossiblyTriggeredNoLock(entry);
240
241 // Note: This invalidates |entry|.
242 entries_.erase(it);
243 }
244
245 if (dispatcher)
246 dispatcher->RemoveAwakableWithContext(this, cookie, nullptr);
247 return MOJO_RESULT_OK;
132 } 248 }
133 249
134 MojoResult WaitSetDispatcher::WaitSetWaitImpl( 250 MojoResult WaitSetDispatcher::WaitSetWaitImpl(
135 MojoDeadline deadline, 251 MojoDeadline deadline,
136 UserPointer<uint32_t> num_results, 252 UserPointer<uint32_t> num_results,
137 UserPointer<MojoWaitSetResult> results, 253 UserPointer<MojoWaitSetResult> results,
138 UserPointer<uint32_t> max_results) { 254 UserPointer<uint32_t> max_results) {
139 MutexLocker locker(&mutex()); 255 MutexLocker locker(&mutex());
140 if (is_closed_no_lock()) 256 if (is_closed_no_lock())
141 return MOJO_RESULT_INVALID_ARGUMENT; 257 return MOJO_RESULT_INVALID_ARGUMENT;
142 258
143 // TODO(vtl) 259 // TODO(vtl)
144 NOTIMPLEMENTED(); 260 NOTIMPLEMENTED();
145 return MOJO_RESULT_UNIMPLEMENTED; 261 return MOJO_RESULT_UNIMPLEMENTED;
146 } 262 }
147 263
264 bool WaitSetDispatcher::Awake(MojoResult result, uint64_t context) {
265 MutexLocker locker(&mutex());
266
267 if (is_closed_no_lock()) {
268 // See |CloseImplNoLock()|: This case may occur while we're unlocked in
269 // |CloseImplNoLock()| (after that, we will have been removed from all the
270 // awakable lists, so |Awake()| should no longer be called). We may as well
271 // return false here, which will automatically remove ourselves from the
272 // awakable list (|CloseImplNoLock()| will call
273 // |RemoveAwakableWithContext()| anyway, but that's OK).
274 return false;
275 }
276
277 auto it = entries_.find(context);
278 DCHECK(it != entries_.end());
279 const auto& entry = it->second;
280 switch (result) {
281 case MOJO_RESULT_OK:
282 if (entry->trigger_state == Entry::TriggerState::NOT_TRIGGERED) {
283 AddPossiblyTriggeredNoLock(entry.get(),
284 Entry::TriggerState::POSSIBLY_SATISFIED);
285 }
286 return true;
287 case MOJO_RESULT_CANCELLED:
288 if (entry->trigger_state == Entry::TriggerState::NOT_TRIGGERED) {
289 AddPossiblyTriggeredNoLock(entry.get(), Entry::TriggerState::CLOSED);
290 } else {
291 // We should only ever get at most one "closed".
292 DCHECK_NE(static_cast<int>(entry->trigger_state),
293 static_cast<int>(Entry::TriggerState::CLOSED));
294 entry->trigger_state = Entry::TriggerState::CLOSED;
295 }
296 entry->dispatcher = nullptr;
297 return false;
298 case MOJO_RESULT_FAILED_PRECONDITION:
299 // Never satisfiable.
300 if (entry->trigger_state == Entry::TriggerState::NOT_TRIGGERED) {
301 AddPossiblyTriggeredNoLock(entry.get(),
302 Entry::TriggerState::NEVER_SATISFIABLE);
303 } else {
304 if (entry->trigger_state == Entry::TriggerState::POSSIBLY_SATISFIED) {
305 entry->trigger_state = Entry::TriggerState::NEVER_SATISFIABLE;
306 } else {
307 // It's possible to get repeated "never satisfiable" triggers, but we
308 // shouldn't get anything after "closed".
309 DCHECK_NE(static_cast<int>(entry->trigger_state),
310 static_cast<int>(Entry::TriggerState::CLOSED));
311 }
312 }
313 // Due to some action on some other thread, it may become satisfiable
314 // again, so continue to be awoken.
315 return true;
316 default:
317 NOTREACHED();
318 break;
319 }
320 return false;
321 }
322
323 void WaitSetDispatcher::AddPossiblyTriggeredNoLock(
324 Entry* entry,
325 Entry::TriggerState new_trigger_state) {
326 DCHECK_EQ(static_cast<int>(entry->trigger_state),
327 static_cast<int>(Entry::TriggerState::NOT_TRIGGERED));
328 DCHECK(!entry->possibly_triggered_previous);
329 DCHECK(!entry->possibly_triggered_next);
330 DCHECK_NE(static_cast<int>(new_trigger_state),
331 static_cast<int>(Entry::TriggerState::NOT_TRIGGERED));
332
333 entry->trigger_state = new_trigger_state;
334 possibly_triggered_count_++;
335
336 if (!possibly_triggered_tail_) {
337 DCHECK(!possibly_triggered_head_);
338 possibly_triggered_head_ = entry;
339 possibly_triggered_tail_ = entry;
340 return;
341 }
342
343 Entry* old_tail = possibly_triggered_tail_;
344 entry->possibly_triggered_previous = old_tail;
345 DCHECK(!old_tail->possibly_triggered_next);
346 old_tail->possibly_triggered_next = entry;
347 possibly_triggered_tail_ = entry;
348 }
349
350 void WaitSetDispatcher::RemovePossiblyTriggeredNoLock(Entry* entry) {
351 DCHECK_NE(static_cast<int>(entry->trigger_state),
352 static_cast<int>(Entry::TriggerState::NOT_TRIGGERED));
353 entry->trigger_state = Entry::TriggerState::NOT_TRIGGERED;
354 possibly_triggered_count_--;
355
356 if (!entry->possibly_triggered_previous) {
357 DCHECK_EQ(entry, possibly_triggered_head_);
358 possibly_triggered_head_ = entry->possibly_triggered_next;
359 } else {
360 entry->possibly_triggered_previous->possibly_triggered_next =
361 entry->possibly_triggered_next;
362 }
363
364 if (!entry->possibly_triggered_next) {
365 DCHECK_EQ(entry, possibly_triggered_tail_);
366 possibly_triggered_tail_ = entry->possibly_triggered_previous;
367 } else {
368 entry->possibly_triggered_next->possibly_triggered_previous =
369 entry->possibly_triggered_previous;
370 }
371
372 entry->possibly_triggered_previous = nullptr;
373 entry->possibly_triggered_next = nullptr;
374 }
375
148 } // namespace system 376 } // namespace system
149 } // namespace mojo 377 } // namespace mojo
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698