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

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

Issue 2085373003: Update WaitSetDispatcher to use the new persistent Awakable stuff. (Closed) Base URL: https://github.com/domokit/mojo.git@work796_wait_set_4.8-x-work795_wait_set_4.7-x-work794_wait_set_4.6
Patch Set: 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
« no previous file with comments | « mojo/edk/system/wait_set_dispatcher.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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> 7 #include <utility>
8 8
9 #include "base/logging.h" 9 #include "base/logging.h"
10 #include "mojo/edk/system/configuration.h" 10 #include "mojo/edk/system/configuration.h"
11 #include "mojo/edk/system/options_validation.h" 11 #include "mojo/edk/system/options_validation.h"
12 12
13 using mojo::util::MutexLocker; 13 using mojo::util::MutexLocker;
14 using mojo::util::RefPtr; 14 using mojo::util::RefPtr;
15 15
16 namespace mojo { 16 namespace mojo {
17 namespace system { 17 namespace system {
18 18
19 WaitSetDispatcher::Entry::Entry(MojoHandleSignals signals, uint64_t cookie) 19 WaitSetDispatcher::Entry::Entry(RefPtr<Dispatcher>&& dispatcher,
20 : signals(signals), cookie(cookie) {} 20 MojoHandleSignals signals,
21 uint64_t cookie)
22 : dispatcher(std::move(dispatcher)), signals(signals), cookie(cookie) {}
21 23
22 WaitSetDispatcher::Entry::~Entry() {} 24 WaitSetDispatcher::Entry::~Entry() {}
23 25
24 // static 26 // static
25 constexpr MojoHandleRights WaitSetDispatcher::kDefaultHandleRights; 27 constexpr MojoHandleRights WaitSetDispatcher::kDefaultHandleRights;
26 28
27 // static 29 // static
28 const MojoCreateWaitSetOptions WaitSetDispatcher::kDefaultCreateOptions = { 30 const MojoCreateWaitSetOptions WaitSetDispatcher::kDefaultCreateOptions = {
29 static_cast<uint32_t>(sizeof(MojoCreateWaitSetOptions)), 31 static_cast<uint32_t>(sizeof(MojoCreateWaitSetOptions)),
30 MOJO_CREATE_WAIT_SET_OPTIONS_FLAG_NONE}; 32 MOJO_CREATE_WAIT_SET_OPTIONS_FLAG_NONE};
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
100 102
101 WaitSetDispatcher::WaitSetDispatcher() {} 103 WaitSetDispatcher::WaitSetDispatcher() {}
102 104
103 WaitSetDispatcher::~WaitSetDispatcher() {} 105 WaitSetDispatcher::~WaitSetDispatcher() {}
104 106
105 void WaitSetDispatcher::CloseImplNoLock() { 107 void WaitSetDispatcher::CloseImplNoLock() {
106 mutex().AssertHeld(); 108 mutex().AssertHeld();
107 109
108 CookieToEntryMap entries; 110 CookieToEntryMap entries;
109 std::swap(entries_, entries); 111 std::swap(entries_, entries);
110 possibly_triggered_head_ = nullptr; 112 triggered_head_ = nullptr;
111 possibly_triggered_tail_ = nullptr; 113 triggered_tail_ = nullptr;
112 possibly_triggered_count_ = 0u; 114 triggered_count_ = 0u;
113 115
114 // We want to remove the awakables outside the lock, so we have to unlock 116 // We want to remove the awakables outside the lock, so we have to unlock
115 // |mutex()|. Note that while unlocked, |Awake()| may get called. 117 // |mutex()|. Note that while unlocked, |Awake()| may get called.
116 // TODO(vtl): This is pretty terrible, but changing it would require pretty 118 // 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 119 // invasive changes in many other places. We really count on |Dispatcher| not
118 // doing anything interesting after calling |CloseImplNoLock()|, and since 120 // doing anything interesting after calling |CloseImplNoLock()|, and since
119 // |CloseImplNoLock()| is allowed to do nothing all the lock invariants are 121 // |CloseImplNoLock()| is allowed to do nothing all the lock invariants are
120 // satisfied. 122 // satisfied.
121 DCHECK(is_closed_no_lock()); 123 DCHECK(is_closed_no_lock());
122 mutex().Unlock(); 124 mutex().Unlock();
(...skipping 16 matching lines...) Expand all
139 mutex().AssertHeld(); 141 mutex().AssertHeld();
140 NOTREACHED(); 142 NOTREACHED();
141 return nullptr; 143 return nullptr;
142 } 144 }
143 145
144 MojoResult WaitSetDispatcher::WaitSetAddImpl( 146 MojoResult WaitSetDispatcher::WaitSetAddImpl(
145 UserPointer<const MojoWaitSetAddOptions> options, 147 UserPointer<const MojoWaitSetAddOptions> options,
146 RefPtr<Dispatcher>&& dispatcher, 148 RefPtr<Dispatcher>&& dispatcher,
147 MojoHandleSignals signals, 149 MojoHandleSignals signals,
148 uint64_t cookie) { 150 uint64_t cookie) {
151 // This will be owned by |entries_|, so it should only be used under
152 // |mutex()|.
149 Entry* entry = nullptr; 153 Entry* entry = nullptr;
150 { 154 {
151 MutexLocker locker(&mutex()); 155 MutexLocker locker(&mutex());
152 if (is_closed_no_lock()) 156 if (is_closed_no_lock())
153 return MOJO_RESULT_INVALID_ARGUMENT; 157 return MOJO_RESULT_INVALID_ARGUMENT;
154 if (is_busy_)
155 return MOJO_RESULT_BUSY;
156 MojoWaitSetAddOptions validated_options; 158 MojoWaitSetAddOptions validated_options;
157 MojoResult result = ValidateWaitSetAddOptions(options, &validated_options); 159 MojoResult result = ValidateWaitSetAddOptions(options, &validated_options);
158 if (result != MOJO_RESULT_OK) 160 if (result != MOJO_RESULT_OK)
159 return result; 161 return result;
160 if (entries_.find(cookie) != entries_.end()) 162 if (entries_.find(cookie) != entries_.end())
161 return MOJO_RESULT_ALREADY_EXISTS; 163 return MOJO_RESULT_ALREADY_EXISTS;
162 if (entries_.size() >= GetConfiguration().max_wait_set_num_entries) 164 if (entries_.size() >= GetConfiguration().max_wait_set_num_entries)
163 return MOJO_RESULT_RESOURCE_EXHAUSTED; 165 return MOJO_RESULT_RESOURCE_EXHAUSTED;
164 // Note: We'll have to set the entry's dispatcher later. 166 entry = new Entry(dispatcher.Clone(), signals, cookie);
165 entry = new Entry(signals, cookie);
166 entries_[cookie] = std::unique_ptr<Entry>(entry); 167 entries_[cookie] = std::unique_ptr<Entry>(entry);
167
168 is_busy_ = true;
169 } 168 }
170 169
171 HandleSignalsState signals_state; 170 // Note: We can only call into another dispatcher outside the lock. This means
171 // that *we* may be closed at any time!
172
173 // We add ourself as a persistent awakable, which means that even if the
174 // condition is already satisfied or never-satisfiable we'll still be added.
172 MojoResult result = 175 MojoResult result =
173 dispatcher->AddAwakable(this, cookie, true, signals, &signals_state); 176 dispatcher->AddAwakable(this, cookie, true, signals, nullptr);
174 177
175 // Can't use |MutexLocker|, since we need to do some work outside the lock 178 MutexLocker locker(&mutex());
176 // in some code paths.
177 mutex().Lock();
178 DCHECK(is_busy_);
179 is_busy_ = false;
180 179
181 // Note: We may have been closed while |mutex()| was unlocked, so we have to 180 // Everywhere below, if |entry| is used, its validity must be justified. E.g.,
182 // check again! 181 // us being closed invalidates |entry|.
182
183 if (is_closed_no_lock()) { 183 if (is_closed_no_lock()) {
184 // Warning: In this case, |entry| has been invalidated, since it was owned 184 // WARNING: |entry| is invalid here.
185 // by |entries_|. 185
186 DCHECK(entries_.empty()); 186 // We need to remove ourself from the target dispatcher's awakable list.
187 mutex().Unlock(); 187 // Regardless of |result|, it's OK to just call |RemoveAwakable()|.
188 if (result == MOJO_RESULT_OK || result == MOJO_RESULT_ALREADY_EXISTS) { 188 dispatcher->RemoveAwakable(true, this, cookie, nullptr);
189 // We have to remove ourself from the target dispatcher's awakable list. 189
190 dispatcher->RemoveAwakable(true, this, cookie, nullptr);
191 }
192 return MOJO_RESULT_INVALID_ARGUMENT; 190 return MOJO_RESULT_INVALID_ARGUMENT;
193 } 191 }
194 192
195 DCHECK(entries_.find(cookie) != entries_.end()); 193 // |entry| is valid: Since we weren't closed and the entry is still not marked
194 // as ready, nothing should have removed it from |entries_|.
196 DCHECK_EQ(entries_[cookie].get(), entry); 195 DCHECK_EQ(entries_[cookie].get(), entry);
197 196
198 if (result == MOJO_RESULT_ALREADY_EXISTS) { 197 if (result == MOJO_RESULT_INVALID_ARGUMENT) {
199 // It was added, but the wait condition is already satisfied. 198 // The target dispatcher was closed, so we weren't added to its awakable
200 AddPossiblyTriggeredNoLock(entry, Entry::TriggerState::SATISFIED); 199 // list. This means that user code has a race condition in this case. We
201 } else if (result == MOJO_RESULT_FAILED_PRECONDITION) { 200 // could try to keep the entry around and signal it as "cancelled", but it's
202 AddPossiblyTriggeredNoLock(entry, Entry::TriggerState::UNSATISFIABLE); 201 // simpler to just say that the target dispatcher was bad in the first
203 } else if (result != MOJO_RESULT_OK) { 202 // place.
204 size_t num_erased = entries_.erase(cookie); 203 entries_.erase(cookie);
205 DCHECK_EQ(num_erased, 1u); 204 return MOJO_RESULT_INVALID_ARGUMENT;
206 mutex().Unlock();
207 return result;
208 } 205 }
209 206
210 // Update the entry to actually have the dispatcher. 207 // In all other cases, we were added.
211 entry->dispatcher = std::move(dispatcher); 208 DCHECK(result == MOJO_RESULT_OK || result == MOJO_RESULT_ALREADY_EXISTS ||
209 result == MOJO_RESULT_FAILED_PRECONDITION);
212 210
213 mutex().Unlock(); 211 DCHECK(!entry->ready);
212 entry->ready = true;
213
214 return MOJO_RESULT_OK; 214 return MOJO_RESULT_OK;
215 } 215 }
216 216
217 MojoResult WaitSetDispatcher::WaitSetRemoveImpl(uint64_t cookie) { 217 MojoResult WaitSetDispatcher::WaitSetRemoveImpl(uint64_t cookie) {
218 RefPtr<Dispatcher> dispatcher; 218 RefPtr<Dispatcher> dispatcher;
219 { 219 {
220 MutexLocker locker(&mutex()); 220 MutexLocker locker(&mutex());
221 if (is_closed_no_lock()) 221 if (is_closed_no_lock())
222 return MOJO_RESULT_INVALID_ARGUMENT; 222 return MOJO_RESULT_INVALID_ARGUMENT;
223 if (is_busy_)
224 return MOJO_RESULT_BUSY;
225 auto it = entries_.find(cookie); 223 auto it = entries_.find(cookie);
226 if (it == entries_.end()) 224 if (it == entries_.end())
227 return MOJO_RESULT_NOT_FOUND; 225 return MOJO_RESULT_NOT_FOUND;
228 226
229 Entry* entry = it->second.get(); 227 Entry* entry = it->second.get();
228 if (!entry->ready) {
229 // |WaitSetAddImpl()| isn't done yet so, as far as user code is concerned,
230 // the entry with this cookie hasn't been added yet.
231 return MOJO_RESULT_NOT_FOUND;
232 }
233
230 // We'll remove ourself from the target dispatcher's awakable list outside 234 // We'll remove ourself from the target dispatcher's awakable list outside
231 // the lock. 235 // the lock.
232 dispatcher = std::move(entry->dispatcher); 236 dispatcher = std::move(entry->dispatcher);
233 237
234 if (entry->trigger_state != Entry::TriggerState::NOT_TRIGGERED) 238 if (entry->is_triggered)
235 RemovePossiblyTriggeredNoLock(entry); 239 RemoveTriggeredNoLock(entry);
236 240
237 // Note: This invalidates |entry|. 241 // Note: This invalidates |entry|.
238 entries_.erase(it); 242 entries_.erase(it);
239 } 243 }
240 244
241 if (dispatcher) 245 if (dispatcher)
242 dispatcher->RemoveAwakable(true, this, cookie, nullptr); 246 dispatcher->RemoveAwakable(true, this, cookie, nullptr);
243 return MOJO_RESULT_OK; 247 return MOJO_RESULT_OK;
244 } 248 }
245 249
(...skipping 19 matching lines...) Expand all
265 if (is_closed_no_lock()) { 269 if (is_closed_no_lock()) {
266 // See |CloseImplNoLock()|: This case may occur while we're unlocked in 270 // See |CloseImplNoLock()|: This case may occur while we're unlocked in
267 // |CloseImplNoLock()| (after that, we will have been removed from all the 271 // |CloseImplNoLock()| (after that, we will have been removed from all the
268 // awakable lists, so |Awake()| should no longer be called). 272 // awakable lists, so |Awake()| should no longer be called).
269 return; 273 return;
270 } 274 }
271 275
272 auto it = entries_.find(context); 276 auto it = entries_.find(context);
273 DCHECK(it != entries_.end()); 277 DCHECK(it != entries_.end());
274 const auto& entry = it->second; 278 const auto& entry = it->second;
275 // We should only ever get at most one "closed" (cancelled). 279 // Once we get "cancelled", we should never be awoken again.
276 DCHECK_NE(static_cast<int>(entry->trigger_state), 280 DCHECK(entry->dispatcher);
277 static_cast<int>(Entry::TriggerState::CLOSED));
278 switch (reason) { 281 switch (reason) {
279 case AwakeReason::CANCELLED: 282 case AwakeReason::CANCELLED:
280 if (entry->trigger_state == Entry::TriggerState::NOT_TRIGGERED) { 283 if (!entry->is_triggered)
281 AddPossiblyTriggeredNoLock(entry.get(), Entry::TriggerState::CLOSED); 284 AddTriggeredNoLock(entry.get());
282 } else {
283 entry->trigger_state = Entry::TriggerState::CLOSED;
284 }
285 entry->dispatcher = nullptr; 285 entry->dispatcher = nullptr;
286 break; 286 break;
287 case AwakeReason::SATISFIED: 287 case AwakeReason::SATISFIED:
288 case AwakeReason::UNSATISFIABLE: 288 case AwakeReason::UNSATISFIABLE:
289 // We shouldn't see these since we're used as a persistent |Awakable|. 289 // We shouldn't see these since we're used as a persistent |Awakable|.
290 NOTREACHED(); 290 NOTREACHED();
291 break; 291 break;
292 case AwakeReason::INITIALIZE: 292 case AwakeReason::INITIALIZE:
293 // TODO(vtl): Save the initial state here (and maybe add to the triggered 293 DCHECK(entry->signals_state.equals(HandleSignalsState()));
294 // list), and then update state on CHANGED. 294 // Fall through.
295 break;
296 case AwakeReason::CHANGED: 295 case AwakeReason::CHANGED:
297 if (signals_state.satisfies(entry->signals)) { 296 // Record the state for anyone waiting.
298 if (entry->trigger_state == Entry::TriggerState::NOT_TRIGGERED) { 297 entry->signals_state = signals_state;
299 AddPossiblyTriggeredNoLock(entry.get(), 298 if (entry->signals_state.satisfies(entry->signals) ||
300 Entry::TriggerState::SATISFIED); 299 !entry->signals_state.can_satisfy(entry->signals)) {
vardhan 2016/06/23 18:26:11 is this right? if entry's signal state cannot sati
viettrungluu 2016/06/23 20:01:05 Yes, we want to report that it's become "never"-sa
301 } else { 300 if (!entry->is_triggered)
302 entry->trigger_state = Entry::TriggerState::SATISFIED; 301 AddTriggeredNoLock(entry.get());
303 }
304 } else if (!signals_state.can_satisfy(entry->signals)) {
305 if (entry->trigger_state == Entry::TriggerState::NOT_TRIGGERED) {
306 AddPossiblyTriggeredNoLock(entry.get(),
307 Entry::TriggerState::UNSATISFIABLE);
308 } else {
309 entry->trigger_state = Entry::TriggerState::UNSATISFIABLE;
310 }
311 } else { 302 } else {
312 if (entry->trigger_state != Entry::TriggerState::NOT_TRIGGERED) 303 if (entry->is_triggered)
313 RemovePossiblyTriggeredNoLock(entry.get()); 304 RemoveTriggeredNoLock(entry.get());
314 } 305 }
315 break; 306 break;
316 } 307 }
317 } 308 }
318 309
319 void WaitSetDispatcher::AddPossiblyTriggeredNoLock( 310 void WaitSetDispatcher::AddTriggeredNoLock(Entry* entry) {
320 Entry* entry, 311 DCHECK(!entry->is_triggered);
321 Entry::TriggerState new_trigger_state) { 312 DCHECK(!entry->triggered_previous);
322 DCHECK_EQ(static_cast<int>(entry->trigger_state), 313 DCHECK(!entry->triggered_next);
323 static_cast<int>(Entry::TriggerState::NOT_TRIGGERED));
324 DCHECK(!entry->possibly_triggered_previous);
325 DCHECK(!entry->possibly_triggered_next);
326 DCHECK_NE(static_cast<int>(new_trigger_state),
327 static_cast<int>(Entry::TriggerState::NOT_TRIGGERED));
328 314
329 entry->trigger_state = new_trigger_state; 315 entry->is_triggered = true;
330 possibly_triggered_count_++; 316 triggered_count_++;
331 317
332 if (!possibly_triggered_tail_) { 318 if (!triggered_tail_) {
333 DCHECK(!possibly_triggered_head_); 319 DCHECK(!triggered_head_);
334 possibly_triggered_head_ = entry; 320 triggered_head_ = entry;
335 possibly_triggered_tail_ = entry; 321 triggered_tail_ = entry;
336 return; 322 return;
337 } 323 }
338 324
339 Entry* old_tail = possibly_triggered_tail_; 325 Entry* old_tail = triggered_tail_;
340 entry->possibly_triggered_previous = old_tail; 326 entry->triggered_previous = old_tail;
341 DCHECK(!old_tail->possibly_triggered_next); 327 DCHECK(!old_tail->triggered_next);
342 old_tail->possibly_triggered_next = entry; 328 old_tail->triggered_next = entry;
343 possibly_triggered_tail_ = entry; 329 triggered_tail_ = entry;
344 } 330 }
345 331
346 void WaitSetDispatcher::RemovePossiblyTriggeredNoLock(Entry* entry) { 332 void WaitSetDispatcher::RemoveTriggeredNoLock(Entry* entry) {
347 DCHECK_NE(static_cast<int>(entry->trigger_state), 333 DCHECK(entry->is_triggered);
348 static_cast<int>(Entry::TriggerState::NOT_TRIGGERED)); 334 entry->is_triggered = false;
349 entry->trigger_state = Entry::TriggerState::NOT_TRIGGERED; 335 triggered_count_--;
350 possibly_triggered_count_--;
351 336
352 if (!entry->possibly_triggered_previous) { 337 if (!entry->triggered_previous) {
353 DCHECK_EQ(entry, possibly_triggered_head_); 338 DCHECK_EQ(entry, triggered_head_);
354 possibly_triggered_head_ = entry->possibly_triggered_next; 339 triggered_head_ = entry->triggered_next;
355 } else { 340 } else {
356 entry->possibly_triggered_previous->possibly_triggered_next = 341 entry->triggered_previous->triggered_next = entry->triggered_next;
357 entry->possibly_triggered_next;
358 } 342 }
359 343
360 if (!entry->possibly_triggered_next) { 344 if (!entry->triggered_next) {
361 DCHECK_EQ(entry, possibly_triggered_tail_); 345 DCHECK_EQ(entry, triggered_tail_);
362 possibly_triggered_tail_ = entry->possibly_triggered_previous; 346 triggered_tail_ = entry->triggered_previous;
363 } else { 347 } else {
364 entry->possibly_triggered_next->possibly_triggered_previous = 348 entry->triggered_next->triggered_previous = entry->triggered_previous;
365 entry->possibly_triggered_previous;
366 } 349 }
367 350
368 entry->possibly_triggered_previous = nullptr; 351 entry->triggered_previous = nullptr;
369 entry->possibly_triggered_next = nullptr; 352 entry->triggered_next = nullptr;
370 } 353 }
371 354
372 } // namespace system 355 } // namespace system
373 } // namespace mojo 356 } // namespace mojo
OLDNEW
« no previous file with comments | « mojo/edk/system/wait_set_dispatcher.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698