| Index: base/waitable_event_posix.cc
|
| diff --git a/base/waitable_event_posix.cc b/base/waitable_event_posix.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..be5af6e375f78a26ae894f1598d3659c9967d2f6
|
| --- /dev/null
|
| +++ b/base/waitable_event_posix.cc
|
| @@ -0,0 +1,392 @@
|
| +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "base/waitable_event.h"
|
| +
|
| +#include "base/condition_variable.h"
|
| +#include "base/lock.h"
|
| +#include "base/message_loop.h"
|
| +
|
| +// -----------------------------------------------------------------------------
|
| +// A WaitableEvent on POSIX is implemented as a wait-list. Currently we don't
|
| +// support cross-process events (where one process can signal an event which
|
| +// others are waiting on). Because of this, we can avoid having one thread per
|
| +// listener in several cases.
|
| +//
|
| +// The WaitableEvent maintains a list of waiters, protected by a lock. Each
|
| +// waiter is either an async wait, in which case we have a Task and the
|
| +// MessageLoop to run it on, or a blocking wait, in which case we have the
|
| +// condition variable to signal.
|
| +//
|
| +// Waiting involves grabbing the lock and adding oneself to the wait list. Async
|
| +// waits can be canceled, which means grabbing the lock and removing oneself
|
| +// from the list.
|
| +//
|
| +// Waiting on multiple events is handled by adding a single, synchronous wait to
|
| +// the wait-list of many events. An event passes a pointer to itself when
|
| +// firing a waiter and so we can store that pointer to find out which event
|
| +// triggered.
|
| +// -----------------------------------------------------------------------------
|
| +
|
| +namespace base {
|
| +
|
| +// -----------------------------------------------------------------------------
|
| +// This is just an abstract base class for waking the two types of waiters
|
| +// -----------------------------------------------------------------------------
|
| +WaitableEvent::WaitableEvent(bool manual_reset, bool initially_signaled)
|
| + : signaled_(false),
|
| + manual_reset_(manual_reset) {
|
| + DCHECK(!initially_signaled) << "Not implemented";
|
| +}
|
| +
|
| +WaitableEvent::~WaitableEvent() {
|
| + DCHECK(waiters_.empty()) << "Deleting WaitableEvent with listeners!";
|
| +}
|
| +
|
| +void WaitableEvent::Reset() {
|
| + AutoLock locked(lock_);
|
| + signaled_ = false;
|
| +}
|
| +
|
| +void WaitableEvent::Signal() {
|
| + AutoLock locked(lock_);
|
| +
|
| + if (signaled_)
|
| + return;
|
| +
|
| + if (manual_reset_) {
|
| + SignalAll();
|
| + signaled_ = true;
|
| + } else {
|
| + // In the case of auto reset, if no waiters were woken, we remain
|
| + // signaled.
|
| + if (!SignalOne())
|
| + signaled_ = true;
|
| + }
|
| +}
|
| +
|
| +bool WaitableEvent::IsSignaled() {
|
| + AutoLock locked(lock_);
|
| +
|
| + const bool result = signaled_;
|
| + if (result && !manual_reset_)
|
| + signaled_ = false;
|
| + return result;
|
| +}
|
| +
|
| +// -----------------------------------------------------------------------------
|
| +// Synchronous waits
|
| +
|
| +// -----------------------------------------------------------------------------
|
| +// This is an synchronous waiter. The thread is waiting on the given condition
|
| +// variable and the fired flag in this object.
|
| +// -----------------------------------------------------------------------------
|
| +class SyncWaiter : public WaitableEvent::Waiter {
|
| + public:
|
| + SyncWaiter(ConditionVariable* cv, Lock* lock)
|
| + : fired_(false),
|
| + cv_(cv),
|
| + lock_(lock),
|
| + signaling_event_(NULL) { }
|
| +
|
| + bool Fire(WaitableEvent *signaling_event) {
|
| + lock_->Acquire();
|
| + const bool previous_value = fired_;
|
| + fired_ = true;
|
| + if (!previous_value)
|
| + signaling_event_ = signaling_event;
|
| + lock_->Release();
|
| +
|
| + if (previous_value)
|
| + return false;
|
| +
|
| + cv_->Broadcast();
|
| +
|
| + // SyncWaiters are stack allocated on the stack of the blocking thread.
|
| + return true;
|
| + }
|
| +
|
| + WaitableEvent* signaled_event() const {
|
| + return signaling_event_;
|
| + }
|
| +
|
| + // ---------------------------------------------------------------------------
|
| + // 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.
|
| + // ---------------------------------------------------------------------------
|
| + bool Compare(void* tag) {
|
| + return this == tag;
|
| + }
|
| +
|
| + // ---------------------------------------------------------------------------
|
| + // Called with lock held.
|
| + // ---------------------------------------------------------------------------
|
| + bool fired() const {
|
| + return fired_;
|
| + }
|
| +
|
| + // ---------------------------------------------------------------------------
|
| + // During a TimedWait, we need a way to make sure that an auto-reset
|
| + // WaitableEvent doesn't think that this event has been signaled between
|
| + // unlocking it and removing it from the wait-list. Called with lock held.
|
| + // ---------------------------------------------------------------------------
|
| + void Disable() {
|
| + fired_ = true;
|
| + }
|
| +
|
| + private:
|
| + bool fired_;
|
| + ConditionVariable *const cv_;
|
| + Lock *const lock_;
|
| + WaitableEvent* signaling_event_; // The WaitableEvent which woke us
|
| +};
|
| +
|
| +bool WaitableEvent::TimedWait(const TimeDelta& max_time) {
|
| + const Time end_time(Time::Now() + max_time);
|
| +
|
| + lock_.Acquire();
|
| + if (signaled_) {
|
| + if (!manual_reset_) {
|
| + // In this case we were signaled when we had no waiters. Now that
|
| + // someone has waited upon us, we can automatically reset.
|
| + signaled_ = false;
|
| + }
|
| +
|
| + lock_.Release();
|
| + return true;
|
| + }
|
| +
|
| + Lock lock;
|
| + lock.Acquire();
|
| + ConditionVariable cv(&lock);
|
| + SyncWaiter sw(&cv, &lock);
|
| +
|
| + Enqueue(&sw);
|
| + lock_.Release();
|
| + // We are violating locking order here by holding the SyncWaiter lock but not
|
| + // the WaitableEvent lock. However, this is safe because we don't lock @lock_
|
| + // again before unlocking it.
|
| +
|
| + for (;;) {
|
| + if (sw.fired()) {
|
| + lock.Release();
|
| + return true;
|
| + }
|
| +
|
| + if (max_time.ToInternalValue() < 0) {
|
| + cv.Wait();
|
| + } else {
|
| + const Time current_time(Time::Now());
|
| + if (current_time >= end_time) {
|
| + // We can't acquire @lock_ before releasing @lock (because of locking
|
| + // order), however, inbetween the two a signal could be fired and @sw
|
| + // would accept it, however we will still return false, so the signal
|
| + // would be lost on an auto-reset WaitableEvent. Thus we call Disable
|
| + // which makes sw::Fire return false.
|
| + sw.Disable();
|
| + lock.Release();
|
| +
|
| + lock_.Acquire();
|
| + Dequeue(&sw, &sw);
|
| + lock_.Release();
|
| + return false;
|
| + }
|
| + const TimeDelta max_wait(end_time - current_time);
|
| +
|
| + cv.TimedWait(max_wait);
|
| + }
|
| + }
|
| +}
|
| +
|
| +bool WaitableEvent::Wait() {
|
| + return TimedWait(TimeDelta::FromSeconds(-1));
|
| +}
|
| +
|
| +// -----------------------------------------------------------------------------
|
| +
|
| +
|
| +// -----------------------------------------------------------------------------
|
| +// Synchronous waiting on multiple objects.
|
| +
|
| +static bool // StrictWeakOrdering
|
| +cmp_fst_addr(const std::pair<WaitableEvent*, unsigned> &a,
|
| + const std::pair<WaitableEvent*, unsigned> &b) {
|
| + return a.first < b.first;
|
| +}
|
| +
|
| +// static
|
| +size_t WaitableEvent::WaitMany(WaitableEvent** raw_waitables,
|
| + size_t count) {
|
| + DCHECK(count) << "Cannot wait on no events";
|
| +
|
| + // 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
|
| + // map back to the original index values later.
|
| + std::vector<std::pair<WaitableEvent*, size_t> > waitables;
|
| + waitables.reserve(count);
|
| + for (size_t i = 0; i < count; ++i)
|
| + waitables.push_back(std::make_pair(raw_waitables[i], i));
|
| +
|
| + DCHECK_EQ(count, waitables.size());
|
| +
|
| + sort(waitables.begin(), waitables.end(), cmp_fst_addr);
|
| +
|
| + // The set of waitables must be distinct. Since we have just sorted by
|
| + // address, we can check this cheaply by comparing pairs of consecutive
|
| + // elements.
|
| + for (size_t i = 0; i < waitables.size() - 1; ++i) {
|
| + DCHECK(waitables[i].first != waitables[i+1].first);
|
| + }
|
| +
|
| + Lock lock;
|
| + ConditionVariable cv(&lock);
|
| + SyncWaiter sw(&cv, &lock);
|
| +
|
| + const size_t r = EnqueueMany(&waitables[0], count, &sw);
|
| + if (r) {
|
| + // 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;
|
| + }
|
| +
|
| + // At this point, we hold the locks on all the WaitableEvents and we have
|
| + // enqueued our waiter in them all.
|
| + lock.Acquire();
|
| + // Release the WaitableEvent locks in the reverse order
|
| + for (size_t i = 0; i < count; ++i) {
|
| + waitables[count - (1 + i)].first->lock_.Release();
|
| + }
|
| +
|
| + for (;;) {
|
| + if (sw.fired())
|
| + break;
|
| +
|
| + cv.Wait();
|
| + }
|
| + lock.Release();
|
| +
|
| + // The address of the WaitableEvent which fired is stored in the SyncWaiter.
|
| + WaitableEvent *const signaled_event = sw.signaled_event();
|
| + // This will store the index of the raw_waitables which fired.
|
| + size_t signaled_index;
|
| +
|
| + // Take the locks of each WaitableEvent in turn (except the signaled one) and
|
| + // remove our SyncWaiter from the wait-list
|
| + for (size_t i = 0; i < count; ++i) {
|
| + if (raw_waitables[i] != signaled_event) {
|
| + raw_waitables[i]->lock_.Acquire();
|
| + // There's no possible ABA issue with the address of the SyncWaiter here
|
| + // because it lives on the stack. Thus the tag value is just the pointer
|
| + // value again.
|
| + raw_waitables[i]->Dequeue(&sw, &sw);
|
| + raw_waitables[i]->lock_.Release();
|
| + } else {
|
| + signaled_index = i;
|
| + }
|
| + }
|
| +
|
| + return signaled_index;
|
| +}
|
| +
|
| +// -----------------------------------------------------------------------------
|
| +// If return value == 0:
|
| +// 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.
|
| +// -----------------------------------------------------------------------------
|
| +// static
|
| +unsigned WaitableEvent::EnqueueMany
|
| + (std::pair<WaitableEvent*, unsigned>* waitables,
|
| + unsigned count, Waiter* waiter) {
|
| + if (!count)
|
| + return 0;
|
| +
|
| + waitables[0].first->lock_.Acquire();
|
| + if (waitables[0].first->signaled_) {
|
| + if (!waitables[0].first->manual_reset_)
|
| + waitables[0].first->signaled_ = false;
|
| + waitables[0].first->lock_.Release();
|
| + return count;
|
| + }
|
| +
|
| + const unsigned r = EnqueueMany(waitables + 1, count - 1, waiter);
|
| + if (r) {
|
| + waitables[0].first->lock_.Release();
|
| + } else {
|
| + waitables[0].first->Enqueue(waiter);
|
| + }
|
| +
|
| + return r;
|
| +}
|
| +
|
| +// -----------------------------------------------------------------------------
|
| +
|
| +
|
| +// -----------------------------------------------------------------------------
|
| +// Private functions...
|
| +
|
| +// -----------------------------------------------------------------------------
|
| +// Wake all waiting waiters. Called with lock held.
|
| +// -----------------------------------------------------------------------------
|
| +bool WaitableEvent::SignalAll() {
|
| + bool signaled_at_least_one = false;
|
| +
|
| + for (std::list<Waiter*>::iterator
|
| + i = waiters_.begin(); i != waiters_.end(); ++i) {
|
| + if ((*i)->Fire(this))
|
| + signaled_at_least_one = true;
|
| + }
|
| +
|
| + waiters_.clear();
|
| + return signaled_at_least_one;
|
| +}
|
| +
|
| +// ---------------------------------------------------------------------------
|
| +// Try to wake a single waiter. Return true if one was woken. Called with lock
|
| +// held.
|
| +// ---------------------------------------------------------------------------
|
| +bool WaitableEvent::SignalOne() {
|
| + for (;;) {
|
| + if (waiters_.empty())
|
| + return false;
|
| +
|
| + const bool r = (*waiters_.begin())->Fire(this);
|
| + waiters_.pop_front();
|
| + if (r)
|
| + return true;
|
| + }
|
| +}
|
| +
|
| +// -----------------------------------------------------------------------------
|
| +// Add a waiter to the list of those waiting. Called with lock held.
|
| +// -----------------------------------------------------------------------------
|
| +void WaitableEvent::Enqueue(Waiter* waiter) {
|
| + waiters_.push_back(waiter);
|
| +}
|
| +
|
| +// -----------------------------------------------------------------------------
|
| +// Remove a waiter from the list of those waiting. Return true if the waiter was
|
| +// actually removed. Called with lock held.
|
| +// -----------------------------------------------------------------------------
|
| +bool WaitableEvent::Dequeue(Waiter* waiter, void* tag) {
|
| + for (std::list<Waiter*>::iterator
|
| + i = waiters_.begin(); i != waiters_.end(); ++i) {
|
| + if (*i == waiter && (*i)->Compare(tag)) {
|
| + waiters_.erase(i);
|
| + return true;
|
| + }
|
| + }
|
| +
|
| + return false;
|
| +}
|
| +
|
| +// -----------------------------------------------------------------------------
|
| +
|
| +} // namespace base
|
|
|