Import Cobalt 19.master.0.205881
diff --git a/src/base/synchronization/waitable_event_posix.cc b/src/base/synchronization/waitable_event_posix.cc index a2a0c0b..121f8c8 100644 --- a/src/base/synchronization/waitable_event_posix.cc +++ b/src/base/synchronization/waitable_event_posix.cc
@@ -3,13 +3,17 @@ // found in the LICENSE file. #include <algorithm> +#include <limits> #include <vector> +#include "base/debug/activity_tracker.h" #include "base/logging.h" -#include "base/synchronization/waitable_event.h" #include "base/synchronization/condition_variable.h" #include "base/synchronization/lock.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/scoped_blocking_call.h" #include "base/threading/thread_restrictions.h" +#include "starboard/types.h" // ----------------------------------------------------------------------------- // A WaitableEvent on POSIX is implemented as a wait-list. Currently we don't @@ -37,12 +41,11 @@ // ----------------------------------------------------------------------------- // This is just an abstract base class for waking the two types of waiters // ----------------------------------------------------------------------------- -WaitableEvent::WaitableEvent(bool manual_reset, bool initially_signaled) - : kernel_(new WaitableEventKernel(manual_reset, initially_signaled)) { -} +WaitableEvent::WaitableEvent(ResetPolicy reset_policy, + InitialState initial_state) + : kernel_(new WaitableEventKernel(reset_policy, initial_state)) {} -WaitableEvent::~WaitableEvent() { -} +WaitableEvent::~WaitableEvent() = default; void WaitableEvent::Reset() { base::AutoLock locked(kernel_->lock_); @@ -85,13 +88,9 @@ class SyncWaiter : public WaitableEvent::Waiter { public: SyncWaiter() - : fired_(false), - signaling_event_(NULL), - lock_(), - cv_(&lock_) { - } + : fired_(false), signaling_event_(nullptr), lock_(), cv_(&lock_) {} - virtual bool Fire(WaitableEvent* signaling_event) override { + bool Fire(WaitableEvent* signaling_event) override { base::AutoLock locked(lock_); if (fired_) @@ -117,9 +116,7 @@ // These waiters are always stack allocated and don't delete themselves. Thus // there's no problem and the ABA tag is the same as the object pointer. // --------------------------------------------------------------------------- - virtual bool Compare(void* tag) override { - return this == tag; - } + bool Compare(void* tag) override { return this == tag; } // --------------------------------------------------------------------------- // Called with lock held. @@ -153,14 +150,26 @@ }; void WaitableEvent::Wait() { - bool result = TimedWait(TimeDelta::FromSeconds(-1)); + bool result = TimedWaitUntil(TimeTicks::Max()); DCHECK(result) << "TimedWait() should never fail with infinite timeout"; } -bool WaitableEvent::TimedWait(const TimeDelta& max_time) { - base::ThreadRestrictions::AssertWaitAllowed(); - const TimeTicks end_time(TimeTicks::Now() + max_time); - const bool finite_time = max_time.ToInternalValue() >= 0; +bool WaitableEvent::TimedWait(const TimeDelta& wait_delta) { + // TimeTicks takes care of overflow including the cases when wait_delta + // is a maximum value. + return TimedWaitUntil(TimeTicks::Now() + wait_delta); +} + +bool WaitableEvent::TimedWaitUntil(const TimeTicks& end_time) { +#if !defined(STARBOARD) + internal::ScopedBlockingCallWithBaseSyncPrimitives scoped_blocking_call( + BlockingType::MAY_BLOCK); + + // Record the event that this thread is blocking upon (for hang diagnosis). + base::debug::ScopedEventWaitActivity event_activity(this); +#endif + + const bool finite_time = !end_time.is_max(); kernel_->lock_.Acquire(); if (kernel_->signaled_) { @@ -197,6 +206,11 @@ sw.Disable(); sw.lock()->Release(); + // This is a bug that has been enshrined in the interface of + // WaitableEvent now: |Dequeue| is called even when |sw.fired()| is true, + // even though it'll always return false in that case. However, taking + // the lock ensures that |Signal| has completed before we return and + // means that a WaitableEvent can synchronise its own destruction. kernel_->lock_.Acquire(); kernel_->Dequeue(&sw, &sw); kernel_->lock_.Release(); @@ -216,17 +230,33 @@ // ----------------------------------------------------------------------------- // Synchronous waiting on multiple objects. -static bool // StrictWeakOrdering -cmp_fst_addr(const std::pair<WaitableEvent*, size_t> &a, +#if defined(STARBOARD) +struct EventComparator +{ + bool operator()(const std::pair<WaitableEvent*, size_t> &a, const std::pair<WaitableEvent*, size_t> &b) { + return a.first < b.first; + } +}; +#else +static bool // StrictWeakOrdering +cmp_fst_addr(const std::pair<WaitableEvent*, unsigned> &a, + const std::pair<WaitableEvent*, unsigned> &b) { return a.first < b.first; } +#endif // static size_t WaitableEvent::WaitMany(WaitableEvent** raw_waitables, size_t count) { - base::ThreadRestrictions::AssertWaitAllowed(); DCHECK(count) << "Cannot wait on no events"; +#if !defined(STARBOARD) + internal::ScopedBlockingCallWithBaseSyncPrimitives scoped_blocking_call( + BlockingType::MAY_BLOCK); + + // Record an event (the first) that this thread is blocking upon. + base::debug::ScopedEventWaitActivity event_activity(raw_waitables[0]); +#endif // !defined(STARBOARD) // We need to acquire the locks in a globally consistent order. Thus we sort // the array of waitables by address. We actually sort a pairs so that we can @@ -238,7 +268,11 @@ DCHECK_EQ(count, waitables.size()); +#if defined(STARBOARD) + sort(waitables.begin(), waitables.end(), EventComparator()); +#else sort(waitables.begin(), waitables.end(), cmp_fst_addr); +#endif // The set of waitables must be distinct. Since we have just sorted by // address, we can check this cheaply by comparing pairs of consecutive @@ -250,12 +284,10 @@ 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 @@ -290,6 +322,11 @@ raw_waitables[i]->kernel_->Dequeue(&sw, &sw); raw_waitables[i]->kernel_->lock_.Release(); } else { + // By taking this lock here we ensure that |Signal| has completed by the + // time we return, because |Signal| holds this lock. This matches the + // behaviour of |Wait| and |TimedWait|. + raw_waitables[i]->kernel_->lock_.Acquire(); + raw_waitables[i]->kernel_->lock_.Release(); signaled_index = i; } } @@ -298,38 +335,50 @@ } // ----------------------------------------------------------------------------- -// 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; } // ----------------------------------------------------------------------------- @@ -338,29 +387,21 @@ // ----------------------------------------------------------------------------- // Private functions... -WaitableEvent::WaitableEventKernel::WaitableEventKernel(bool manual_reset, - bool initially_signaled) - : manual_reset_(manual_reset), - signaled_(initially_signaled) { -#if defined(__LB_SHELL__) || defined(OS_STARBOARD) - waiters_.reserve(1); -#endif -} +WaitableEvent::WaitableEventKernel::WaitableEventKernel( + ResetPolicy reset_policy, + InitialState initial_state) + : manual_reset_(reset_policy == ResetPolicy::MANUAL), + signaled_(initial_state == InitialState::SIGNALED) {} -WaitableEvent::WaitableEventKernel::~WaitableEventKernel() { -} +WaitableEvent::WaitableEventKernel::~WaitableEventKernel() = default; // ----------------------------------------------------------------------------- // Wake all waiting waiters. Called with lock held. // ----------------------------------------------------------------------------- bool WaitableEvent::SignalAll() { bool signaled_at_least_one = false; -#if defined(__LB_SHELL__) || defined(OS_STARBOARD) - for (std::vector<Waiter*>::iterator -#else - for (std::list<Waiter*>::iterator -#endif - i = kernel_->waiters_.begin(); i != kernel_->waiters_.end(); ++i) { + + for (auto i = kernel_->waiters_.begin(); i != kernel_->waiters_.end(); ++i) { if ((*i)->Fire(this)) signaled_at_least_one = true; } @@ -379,13 +420,7 @@ return false; const bool r = (*kernel_->waiters_.begin())->Fire(this); -#if defined(__LB_SHELL__) || defined(OS_STARBOARD) - // This appears to be slow, but the size of the vector is almost always 1. - // Therefore, this line is usually constant time. - kernel_->waiters_.erase(kernel_->waiters_.begin()); -#else kernel_->waiters_.pop_front(); -#endif if (r) return true; } @@ -403,12 +438,7 @@ // actually removed. Called with lock held. // ----------------------------------------------------------------------------- bool WaitableEvent::WaitableEventKernel::Dequeue(Waiter* waiter, void* tag) { -#if defined(__LB_SHELL__) || defined(OS_STARBOARD) - for (std::vector<Waiter*>::iterator -#else - for (std::list<Waiter*>::iterator -#endif - i = waiters_.begin(); i != waiters_.end(); ++i) { + for (auto i = waiters_.begin(); i != waiters_.end(); ++i) { if (*i == waiter && (*i)->Compare(tag)) { waiters_.erase(i); return true;