| OLD | NEW |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 |
| OLD | NEW |