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

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

Issue 2093763002: Implement WaitSetDispatcher::WaitSetWaitImpl(). (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: doh 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') | mojo/edk/system/wait_set_dispatcher_unittest.cc » ('j') | 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 <string.h>
8
9 #include <algorithm>
10 #include <limits>
7 #include <utility> 11 #include <utility>
8 12
9 #include "base/logging.h" 13 #include "base/logging.h"
14 #include "mojo/edk/platform/time_ticks.h"
10 #include "mojo/edk/system/configuration.h" 15 #include "mojo/edk/system/configuration.h"
11 #include "mojo/edk/system/options_validation.h" 16 #include "mojo/edk/system/options_validation.h"
12 17
18 using mojo::platform::GetTimeTicks;
13 using mojo::util::MutexLocker; 19 using mojo::util::MutexLocker;
14 using mojo::util::RefPtr; 20 using mojo::util::RefPtr;
15 21
16 namespace mojo { 22 namespace mojo {
17 namespace system { 23 namespace system {
18 24
19 WaitSetDispatcher::Entry::Entry(RefPtr<Dispatcher>&& dispatcher, 25 WaitSetDispatcher::Entry::Entry(RefPtr<Dispatcher>&& dispatcher,
20 MojoHandleSignals signals, 26 MojoHandleSignals signals,
21 uint64_t cookie) 27 uint64_t cookie)
22 : dispatcher(std::move(dispatcher)), signals(signals), cookie(cookie) {} 28 : dispatcher(std::move(dispatcher)), signals(signals), cookie(cookie) {}
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
106 112
107 void WaitSetDispatcher::CloseImplNoLock() { 113 void WaitSetDispatcher::CloseImplNoLock() {
108 mutex().AssertHeld(); 114 mutex().AssertHeld();
109 115
110 CookieToEntryMap entries; 116 CookieToEntryMap entries;
111 std::swap(entries_, entries); 117 std::swap(entries_, entries);
112 triggered_head_ = nullptr; 118 triggered_head_ = nullptr;
113 triggered_tail_ = nullptr; 119 triggered_tail_ = nullptr;
114 triggered_count_ = 0u; 120 triggered_count_ = 0u;
115 121
122 cv_.Signal();
123
116 // We want to remove the awakables outside the lock, so we have to unlock 124 // We want to remove the awakables outside the lock, so we have to unlock
117 // |mutex()|. Note that while unlocked, |Awake()| may get called. 125 // |mutex()|. Note that while unlocked, |Awake()| may get called.
118 // TODO(vtl): This is pretty terrible, but changing it would require pretty 126 // TODO(vtl): This is pretty terrible, but changing it would require pretty
119 // invasive changes in many other places. We really count on |Dispatcher| not 127 // invasive changes in many other places. We really count on |Dispatcher| not
120 // doing anything interesting after calling |CloseImplNoLock()|, and since 128 // doing anything interesting after calling |CloseImplNoLock()|, and since
121 // |CloseImplNoLock()| is allowed to do nothing all the lock invariants are 129 // |CloseImplNoLock()| is allowed to do nothing all the lock invariants are
122 // satisfied. 130 // satisfied.
123 DCHECK(is_closed_no_lock()); 131 DCHECK(is_closed_no_lock());
124 mutex().Unlock(); 132 mutex().Unlock();
125 133
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after
223 auto it = entries_.find(cookie); 231 auto it = entries_.find(cookie);
224 if (it == entries_.end()) 232 if (it == entries_.end())
225 return MOJO_RESULT_NOT_FOUND; 233 return MOJO_RESULT_NOT_FOUND;
226 234
227 Entry* entry = it->second.get(); 235 Entry* entry = it->second.get();
228 if (!entry->ready) { 236 if (!entry->ready) {
229 // |WaitSetAddImpl()| isn't done yet so, as far as user code is concerned, 237 // |WaitSetAddImpl()| isn't done yet so, as far as user code is concerned,
230 // the entry with this cookie hasn't been added yet. 238 // the entry with this cookie hasn't been added yet.
231 return MOJO_RESULT_NOT_FOUND; 239 return MOJO_RESULT_NOT_FOUND;
232 } 240 }
241 if (entry->is_being_removed) {
242 // This entry is being removed on another thread!
243 return MOJO_RESULT_NOT_FOUND;
244 }
245
246 entry->is_being_removed = true;
233 247
234 // We'll remove ourself from the target dispatcher's awakable list outside 248 // We'll remove ourself from the target dispatcher's awakable list outside
235 // the lock. 249 // the lock.
236 dispatcher = std::move(entry->dispatcher); 250 dispatcher = entry->dispatcher;
237
238 if (entry->is_triggered)
239 RemoveTriggeredNoLock(entry);
240
241 // Note: This invalidates |entry|.
242 entries_.erase(it);
243 } 251 }
244 252
245 if (dispatcher) 253 if (dispatcher)
246 dispatcher->RemoveAwakable(true, this, cookie, nullptr); 254 dispatcher->RemoveAwakable(true, this, cookie, nullptr);
255
256 {
257 MutexLocker locker(&mutex());
258
259 if (is_closed_no_lock())
260 return MOJO_RESULT_OK;
261
262 auto it = entries_.find(cookie);
263 DCHECK(it != entries_.end());
264 Entry* entry = it->second.get();
265 DCHECK(entry->is_being_removed);
266 if (entry->is_triggered)
267 RemoveTriggeredNoLock(entry);
268 entries_.erase(cookie);
269 }
270
247 return MOJO_RESULT_OK; 271 return MOJO_RESULT_OK;
248 } 272 }
249 273
250 MojoResult WaitSetDispatcher::WaitSetWaitImpl( 274 MojoResult WaitSetDispatcher::WaitSetWaitImpl(
251 MojoDeadline deadline, 275 MojoDeadline deadline,
252 UserPointer<uint32_t> num_results, 276 UserPointer<uint32_t> num_results,
253 UserPointer<MojoWaitSetResult> results, 277 UserPointer<MojoWaitSetResult> results,
254 UserPointer<uint32_t> max_results) { 278 UserPointer<uint32_t> max_results) {
255 MutexLocker locker(&mutex()); 279 MutexLocker locker(&mutex());
256 if (is_closed_no_lock()) 280 if (is_closed_no_lock())
257 return MOJO_RESULT_INVALID_ARGUMENT; 281 return MOJO_RESULT_INVALID_ARGUMENT;
258 282
259 // TODO(vtl) 283 // Read this before waiting. (If we're going to crash due to reading input
260 NOTIMPLEMENTED(); 284 // values, we'd like to do so before waiting.)
261 return MOJO_RESULT_UNIMPLEMENTED; 285 uint32_t num_results_in = num_results.Get();
286
287 if (deadline == MOJO_DEADLINE_INDEFINITE) {
288 while (!is_closed_no_lock() && triggered_count_ == 0u)
289 cv_.Wait(&mutex());
290 } else {
291 // We may get spurious wakeups, so record the start time and track the
292 // remaining timeout.
293 uint64_t wait_remaining = deadline;
294 MojoTimeTicks start = GetTimeTicks();
295 while (!is_closed_no_lock() && triggered_count_ == 0u) {
296 // NOTE(vtl): Possibly, we should add a version of |WaitWithTimeout()|
297 // that takes an absolute deadline, since that's what pthreads takes.
298 if (cv_.WaitWithTimeout(&mutex(), wait_remaining))
299 return MOJO_RESULT_DEADLINE_EXCEEDED; // Definitely timed out.
300
301 MojoTimeTicks now = GetTimeTicks();
302 DCHECK_GE(now, start);
303 uint64_t elapsed = static_cast<uint64_t>(now - start);
304 // It's possible that the deadline has passed anyway.
305 if (elapsed >= deadline)
306 return MOJO_RESULT_DEADLINE_EXCEEDED;
307
308 // Otherwise, recalculate the amount that we have left to wait.
309 wait_remaining = deadline - elapsed;
310 }
311 }
312 if (is_closed_no_lock())
313 return MOJO_RESULT_CANCELLED;
314 DCHECK_GT(triggered_count_, 0u);
315
316 uint32_t num_results_out =
317 static_cast<uint32_t>(std::min<size_t>(num_results_in, triggered_count_));
318 std::vector<MojoWaitSetResult> results_out(num_results_out);
319 if (num_results_out > 0u) {
320 // We're going to copy out all this memory, so we should make sure it's all
321 // been zeroed.
322 memset(results_out.data(), 0,
323 results_out.size() * sizeof(MojoWaitSetResult));
324
325 const Entry* entry = triggered_head_;
326 for (auto& wait_set_result : results_out) {
327 DCHECK(entry);
328 DCHECK(entry->is_triggered);
329
330 wait_set_result.cookie = entry->cookie;
331 // |wait_set_result.reserved| has already been zeroed, as has
332 // |wait_set_result.signals_state| (for the cases below where we don't set
333 // it explicitly).
334 wait_set_result.reserved = 0u;
335 if (!entry->dispatcher) {
336 wait_set_result.wait_result = MOJO_RESULT_CANCELLED;
337 } else if (entry->signals_state.satisfies(entry->signals)) {
338 wait_set_result.wait_result = MOJO_RESULT_OK;
339 wait_set_result.signals_state = entry->signals_state;
340 } else if (!entry->signals_state.can_satisfy(entry->signals)) {
341 wait_set_result.wait_result = MOJO_RESULT_FAILED_PRECONDITION;
342 wait_set_result.signals_state = entry->signals_state;
343 } else {
344 NOTREACHED();
345 wait_set_result.wait_result = MOJO_RESULT_INTERNAL;
346 }
347 // TODO(vtl): The comment in mojo/public/c/system/wait_set.h indicates
348 // that we may have to provide |MOJO_RESULT_BUSY|, but we never do that
349 // here. Is that right or am I missing something?
350
351 entry = entry->triggered_next;
352 }
353 }
354 uint32_t max_results_out = static_cast<uint32_t>(
355 std::min<size_t>(std::numeric_limits<uint32_t>::max(), triggered_count_));
356
357 DCHECK_LE(num_results_out, num_results_in);
358 num_results.Put(num_results_out);
359 if (num_results_out > 0u) {
360 DCHECK_EQ(num_results_out, results_out.size());
361 results.PutArray(results_out.data(), results_out.size());
362 } else {
363 // We were awoken and didn't time out, so the only reason we should be
364 // providing no results is if none were requested.
365 DCHECK_EQ(num_results_in, 0u);
366 }
367 if (!max_results.IsNull())
368 max_results.Put(max_results_out);
369 return MOJO_RESULT_OK;
262 } 370 }
263 371
264 void WaitSetDispatcher::Awake(uint64_t context, 372 void WaitSetDispatcher::Awake(uint64_t context,
265 AwakeReason reason, 373 AwakeReason reason,
266 const HandleSignalsState& signals_state) { 374 const HandleSignalsState& signals_state) {
267 MutexLocker locker(&mutex()); 375 MutexLocker locker(&mutex());
268 376
269 if (is_closed_no_lock()) { 377 if (is_closed_no_lock()) {
270 // See |CloseImplNoLock()|: This case may occur while we're unlocked in 378 // See |CloseImplNoLock()|: This case may occur while we're unlocked in
271 // |CloseImplNoLock()| (after that, we will have been removed from all the 379 // |CloseImplNoLock()| (after that, we will have been removed from all the
272 // awakable lists, so |Awake()| should no longer be called). 380 // awakable lists, so |Awake()| should no longer be called).
273 return; 381 return;
274 } 382 }
275 383
276 auto it = entries_.find(context); 384 auto it = entries_.find(context);
277 DCHECK(it != entries_.end()); 385 DCHECK(it != entries_.end());
278 const auto& entry = it->second; 386 const auto& entry = it->second;
279 // Once we get "cancelled", we should never be awoken again. 387 // Once we get "cancelled", we should never be awoken again.
280 DCHECK(entry->dispatcher); 388 DCHECK(entry->dispatcher);
389 if (entry->is_being_removed)
390 return;
391
281 switch (reason) { 392 switch (reason) {
282 case AwakeReason::CANCELLED: 393 case AwakeReason::CANCELLED:
283 if (!entry->is_triggered) 394 if (!entry->is_triggered)
284 AddTriggeredNoLock(entry.get()); 395 AddTriggeredNoLock(entry.get());
285 entry->dispatcher = nullptr; 396 entry->dispatcher = nullptr;
286 break; 397 break;
287 case AwakeReason::SATISFIED: 398 case AwakeReason::SATISFIED:
288 case AwakeReason::UNSATISFIABLE: 399 case AwakeReason::UNSATISFIABLE:
289 // We shouldn't see these since we're used as a persistent |Awakable|. 400 // We shouldn't see these since we're used as a persistent |Awakable|.
290 NOTREACHED(); 401 NOTREACHED();
(...skipping 15 matching lines...) Expand all
306 break; 417 break;
307 } 418 }
308 } 419 }
309 420
310 void WaitSetDispatcher::AddTriggeredNoLock(Entry* entry) { 421 void WaitSetDispatcher::AddTriggeredNoLock(Entry* entry) {
311 DCHECK(!entry->is_triggered); 422 DCHECK(!entry->is_triggered);
312 DCHECK(!entry->triggered_previous); 423 DCHECK(!entry->triggered_previous);
313 DCHECK(!entry->triggered_next); 424 DCHECK(!entry->triggered_next);
314 425
315 entry->is_triggered = true; 426 entry->is_triggered = true;
427 if (!triggered_count_)
428 cv_.Signal();
316 triggered_count_++; 429 triggered_count_++;
317 430
318 if (!triggered_tail_) { 431 if (!triggered_tail_) {
319 DCHECK(!triggered_head_); 432 DCHECK(!triggered_head_);
320 triggered_head_ = entry; 433 triggered_head_ = entry;
321 triggered_tail_ = entry; 434 triggered_tail_ = entry;
322 return; 435 return;
323 } 436 }
324 437
325 Entry* old_tail = triggered_tail_; 438 Entry* old_tail = triggered_tail_;
(...skipping 21 matching lines...) Expand all
347 } else { 460 } else {
348 entry->triggered_next->triggered_previous = entry->triggered_previous; 461 entry->triggered_next->triggered_previous = entry->triggered_previous;
349 } 462 }
350 463
351 entry->triggered_previous = nullptr; 464 entry->triggered_previous = nullptr;
352 entry->triggered_next = nullptr; 465 entry->triggered_next = nullptr;
353 } 466 }
354 467
355 } // namespace system 468 } // namespace system
356 } // namespace mojo 469 } // namespace mojo
OLDNEW
« no previous file with comments | « mojo/edk/system/wait_set_dispatcher.h ('k') | mojo/edk/system/wait_set_dispatcher_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698