Index: base/synchronization/waitable_event_posix.cc |
diff --git a/base/synchronization/waitable_event_posix.cc b/base/synchronization/waitable_event_posix.cc |
index 5dfff468ad6acb0f2c81df98bcc31564f7029506..846fa06700e1dcb1c1c5976eb2397643c0698759 100644 |
--- a/base/synchronization/waitable_event_posix.cc |
+++ b/base/synchronization/waitable_event_posix.cc |
@@ -5,6 +5,7 @@ |
#include <stddef.h> |
#include <algorithm> |
+#include <limits> |
#include <vector> |
#include "base/debug/activity_tracker.h" |
@@ -266,12 +267,10 @@ size_t WaitableEvent::WaitMany(WaitableEvent** raw_waitables, |
SyncWaiter sw; |
const size_t r = EnqueueMany(&waitables[0], count, &sw); |
- if (r) { |
+ if (r < count) { |
// One of the events is already signaled. The SyncWaiter has not been |
- // enqueued anywhere. EnqueueMany returns the count of remaining waitables |
- // when the signaled one was seen, so the index of the signaled event is |
- // @count - @r. |
- return waitables[count - r].second; |
+ // enqueued anywhere. |
+ return waitables[r].second; |
} |
// At this point, we hold the locks on all the WaitableEvents and we have |
@@ -319,38 +318,50 @@ size_t WaitableEvent::WaitMany(WaitableEvent** raw_waitables, |
} |
// ----------------------------------------------------------------------------- |
-// If return value == 0: |
+// If return value == count: |
// The locks of the WaitableEvents have been taken in order and the Waiter has |
// been enqueued in the wait-list of each. None of the WaitableEvents are |
// currently signaled |
// else: |
// None of the WaitableEvent locks are held. The Waiter has not been enqueued |
-// in any of them and the return value is the index of the first WaitableEvent |
-// which was signaled, from the end of the array. |
+// in any of them and the return value is the index of the WaitableEvent which |
+// was signaled with the lowest input index from the original WaitMany call. |
// ----------------------------------------------------------------------------- |
// static |
-size_t WaitableEvent::EnqueueMany |
- (std::pair<WaitableEvent*, size_t>* waitables, |
- size_t count, Waiter* waiter) { |
- if (!count) |
- return 0; |
- |
- waitables[0].first->kernel_->lock_.Acquire(); |
- if (waitables[0].first->kernel_->signaled_) { |
- if (!waitables[0].first->kernel_->manual_reset_) |
- waitables[0].first->kernel_->signaled_ = false; |
- waitables[0].first->kernel_->lock_.Release(); |
- return count; |
+size_t WaitableEvent::EnqueueMany(std::pair<WaitableEvent*, size_t>* waitables, |
+ size_t count, |
+ Waiter* waiter) { |
+ size_t winner = count; |
+ size_t winner_index = count; |
+ for (size_t i = 0; i < count; ++i) { |
+ auto& kernel = waitables[i].first->kernel_; |
+ kernel->lock_.Acquire(); |
+ if (kernel->signaled_ && waitables[i].second < winner) { |
+ winner = waitables[i].second; |
+ winner_index = i; |
} |
+ } |
- const size_t r = EnqueueMany(waitables + 1, count - 1, waiter); |
- if (r) { |
- waitables[0].first->kernel_->lock_.Release(); |
- } else { |
- waitables[0].first->Enqueue(waiter); |
+ // No events signaled. All locks acquired. Enqueue the Waiter on all of them |
+ // and return. |
+ if (winner == count) { |
+ for (size_t i = 0; i < count; ++i) |
+ waitables[i].first->Enqueue(waiter); |
+ return count; |
+ } |
+ |
+ // Unlock in reverse order and possibly clear the chosen winner's signal |
+ // before returning its index. |
+ for (auto* w = waitables + count - 1; w >= waitables; --w) { |
+ auto& kernel = w->first->kernel_; |
+ if (w->second == winner) { |
+ if (!kernel->manual_reset_) |
+ kernel->signaled_ = false; |
} |
+ kernel->lock_.Release(); |
+ } |
- return r; |
+ return winner_index; |
} |
// ----------------------------------------------------------------------------- |