diff options
Diffstat (limited to 'base/synchronization')
-rw-r--r-- | base/synchronization/cancellation_flag.cc | 22 | ||||
-rw-r--r-- | base/synchronization/cancellation_flag.h | 43 | ||||
-rw-r--r-- | base/synchronization/cancellation_flag_unittest.cc | 67 | ||||
-rw-r--r-- | base/synchronization/waitable_event.h | 179 | ||||
-rw-r--r-- | base/synchronization/waitable_event_posix.cc | 404 | ||||
-rw-r--r-- | base/synchronization/waitable_event_unittest.cc | 106 | ||||
-rw-r--r-- | base/synchronization/waitable_event_watcher.h | 162 | ||||
-rw-r--r-- | base/synchronization/waitable_event_watcher_posix.cc | 280 | ||||
-rw-r--r-- | base/synchronization/waitable_event_watcher_unittest.cc | 165 | ||||
-rw-r--r-- | base/synchronization/waitable_event_watcher_win.cc | 60 | ||||
-rw-r--r-- | base/synchronization/waitable_event_win.cc | 99 |
11 files changed, 1587 insertions, 0 deletions
diff --git a/base/synchronization/cancellation_flag.cc b/base/synchronization/cancellation_flag.cc new file mode 100644 index 0000000..ad3b551 --- /dev/null +++ b/base/synchronization/cancellation_flag.cc @@ -0,0 +1,22 @@ +// Copyright (c) 2011 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/synchronization/cancellation_flag.h" + +#include "base/logging.h" + +namespace base { + +void CancellationFlag::Set() { +#if !defined(NDEBUG) + DCHECK_EQ(set_on_, PlatformThread::CurrentId()); +#endif + base::subtle::Release_Store(&flag_, 1); +} + +bool CancellationFlag::IsSet() const { + return base::subtle::Acquire_Load(&flag_) != 0; +} + +} // namespace base diff --git a/base/synchronization/cancellation_flag.h b/base/synchronization/cancellation_flag.h new file mode 100644 index 0000000..29ecd89 --- /dev/null +++ b/base/synchronization/cancellation_flag.h @@ -0,0 +1,43 @@ +// Copyright (c) 2011 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. + +#ifndef BASE_SYNCHRONIZATION_CANCELLATION_FLAG_H_ +#define BASE_SYNCHRONIZATION_CANCELLATION_FLAG_H_ +#pragma once + +#include "base/atomicops.h" +#include "base/threading/platform_thread.h" + +namespace base { + +// CancellationFlag allows one thread to cancel jobs executed on some worker +// thread. Calling Set() from one thread and IsSet() from a number of threads +// is thread-safe. +// +// This class IS NOT intended for synchronization between threads. +class CancellationFlag { + public: + CancellationFlag() : flag_(false) { +#if !defined(NDEBUG) + set_on_ = PlatformThread::CurrentId(); +#endif + } + ~CancellationFlag() {} + + // Set the flag. May only be called on the thread which owns the object. + void Set(); + bool IsSet() const; // Returns true iff the flag was set. + + private: + base::subtle::Atomic32 flag_; +#if !defined(NDEBUG) + PlatformThreadId set_on_; +#endif + + DISALLOW_COPY_AND_ASSIGN(CancellationFlag); +}; + +} // namespace base + +#endif // BASE_SYNCHRONIZATION_CANCELLATION_FLAG_H_ diff --git a/base/synchronization/cancellation_flag_unittest.cc b/base/synchronization/cancellation_flag_unittest.cc new file mode 100644 index 0000000..c405c79 --- /dev/null +++ b/base/synchronization/cancellation_flag_unittest.cc @@ -0,0 +1,67 @@ +// Copyright (c) 2011 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. + +// Tests of CancellationFlag class. + +#include "base/synchronization/cancellation_flag.h" + +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/spin_wait.h" +#include "base/time.h" +#include "base/threading/thread.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" + +namespace base { + +namespace { + +//------------------------------------------------------------------------------ +// Define our test class. +//------------------------------------------------------------------------------ + +class CancelTask : public Task { + public: + explicit CancelTask(CancellationFlag* flag) : flag_(flag) {} + virtual void Run() { + ASSERT_DEBUG_DEATH(flag_->Set(), ""); + } + private: + CancellationFlag* flag_; +}; + +TEST(CancellationFlagTest, SimpleSingleThreadedTest) { + CancellationFlag flag; + ASSERT_FALSE(flag.IsSet()); + flag.Set(); + ASSERT_TRUE(flag.IsSet()); +} + +TEST(CancellationFlagTest, DoubleSetTest) { + CancellationFlag flag; + ASSERT_FALSE(flag.IsSet()); + flag.Set(); + ASSERT_TRUE(flag.IsSet()); + flag.Set(); + ASSERT_TRUE(flag.IsSet()); +} + +TEST(CancellationFlagTest, SetOnDifferentThreadDeathTest) { + // Checks that Set() can't be called from any other thread. + // CancellationFlag should die on a DCHECK if Set() is called from + // other thread. + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + Thread t("CancellationFlagTest.SetOnDifferentThreadDeathTest"); + ASSERT_TRUE(t.Start()); + ASSERT_TRUE(t.message_loop()); + ASSERT_TRUE(t.IsRunning()); + + CancellationFlag flag; + t.message_loop()->PostTask(FROM_HERE, new CancelTask(&flag)); +} + +} // namespace + +} // namespace base diff --git a/base/synchronization/waitable_event.h b/base/synchronization/waitable_event.h new file mode 100644 index 0000000..70b9da0 --- /dev/null +++ b/base/synchronization/waitable_event.h @@ -0,0 +1,179 @@ +// Copyright (c) 2011 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. + +#ifndef BASE_SYNCHRONIZATION_WAITABLE_EVENT_H_ +#define BASE_SYNCHRONIZATION_WAITABLE_EVENT_H_ +#pragma once + +#include "base/basictypes.h" + +#if defined(OS_WIN) +#include <windows.h> +#endif + +#if defined(OS_POSIX) +#include <list> +#include <utility> +#include "base/lock.h" +#include "base/ref_counted.h" +#endif + +namespace base { + +// This replaces INFINITE from Win32 +static const int kNoTimeout = -1; + +class TimeDelta; + +// A WaitableEvent can be a useful thread synchronization tool when you want to +// allow one thread to wait for another thread to finish some work. For +// non-Windows systems, this can only be used from within a single address +// space. +// +// Use a WaitableEvent when you would otherwise use a Lock+ConditionVariable to +// protect a simple boolean value. However, if you find yourself using a +// WaitableEvent in conjunction with a Lock to wait for a more complex state +// change (e.g., for an item to be added to a queue), then you should probably +// be using a ConditionVariable instead of a WaitableEvent. +// +// NOTE: On Windows, this class provides a subset of the functionality afforded +// by a Windows event object. This is intentional. If you are writing Windows +// specific code and you need other features of a Windows event, then you might +// be better off just using an Windows event directly. +class WaitableEvent { + public: + // If manual_reset is true, then to set the event state to non-signaled, a + // consumer must call the Reset method. If this parameter is false, then the + // system automatically resets the event state to non-signaled after a single + // waiting thread has been released. + WaitableEvent(bool manual_reset, bool initially_signaled); + +#if defined(OS_WIN) + // Create a WaitableEvent from an Event HANDLE which has already been + // created. This objects takes ownership of the HANDLE and will close it when + // deleted. + explicit WaitableEvent(HANDLE event_handle); + + // Releases ownership of the handle from this object. + HANDLE Release(); +#endif + + ~WaitableEvent(); + + // Put the event in the un-signaled state. + void Reset(); + + // Put the event in the signaled state. Causing any thread blocked on Wait + // to be woken up. + void Signal(); + + // Returns true if the event is in the signaled state, else false. If this + // is not a manual reset event, then this test will cause a reset. + bool IsSignaled(); + + // Wait indefinitely for the event to be signaled. Returns true if the event + // was signaled, else false is returned to indicate that waiting failed. + bool Wait(); + + // Wait up until max_time has passed for the event to be signaled. Returns + // true if the event was signaled. If this method returns false, then it + // does not necessarily mean that max_time was exceeded. + bool TimedWait(const TimeDelta& max_time); + +#if defined(OS_WIN) + HANDLE handle() const { return handle_; } +#endif + + // Wait, synchronously, on multiple events. + // waitables: an array of WaitableEvent pointers + // count: the number of elements in @waitables + // + // returns: the index of a WaitableEvent which has been signaled. + // + // You MUST NOT delete any of the WaitableEvent objects while this wait is + // happening. + static size_t WaitMany(WaitableEvent** waitables, size_t count); + + // For asynchronous waiting, see WaitableEventWatcher + + // This is a private helper class. It's here because it's used by friends of + // this class (such as WaitableEventWatcher) to be able to enqueue elements + // of the wait-list + class Waiter { + public: + // Signal the waiter to wake up. + // + // Consider the case of a Waiter which is in multiple WaitableEvent's + // wait-lists. Each WaitableEvent is automatic-reset and two of them are + // signaled at the same time. Now, each will wake only the first waiter in + // the wake-list before resetting. However, if those two waiters happen to + // be the same object (as can happen if another thread didn't have a chance + // to dequeue the waiter from the other wait-list in time), two auto-resets + // will have happened, but only one waiter has been signaled! + // + // Because of this, a Waiter may "reject" a wake by returning false. In + // this case, the auto-reset WaitableEvent shouldn't act as if anything has + // been notified. + virtual bool Fire(WaitableEvent* signaling_event) = 0; + + // Waiters may implement this in order to provide an extra condition for + // two Waiters to be considered equal. In WaitableEvent::Dequeue, if the + // pointers match then this function is called as a final check. See the + // comments in ~Handle for why. + virtual bool Compare(void* tag) = 0; + + protected: + virtual ~Waiter() {} + }; + + private: + friend class WaitableEventWatcher; + +#if defined(OS_WIN) + HANDLE handle_; +#else + // On Windows, one can close a HANDLE which is currently being waited on. The + // MSDN documentation says that the resulting behaviour is 'undefined', but + // it doesn't crash. However, if we were to include the following members + // directly then, on POSIX, one couldn't use WaitableEventWatcher to watch an + // event which gets deleted. This mismatch has bitten us several times now, + // so we have a kernel of the WaitableEvent, which is reference counted. + // WaitableEventWatchers may then take a reference and thus match the Windows + // behaviour. + struct WaitableEventKernel : + public RefCountedThreadSafe<WaitableEventKernel> { + public: + WaitableEventKernel(bool manual_reset, bool initially_signaled); + virtual ~WaitableEventKernel(); + + bool Dequeue(Waiter* waiter, void* tag); + + Lock lock_; + const bool manual_reset_; + bool signaled_; + std::list<Waiter*> waiters_; + }; + + scoped_refptr<WaitableEventKernel> kernel_; + + bool SignalAll(); + bool SignalOne(); + void Enqueue(Waiter* waiter); + + // When dealing with arrays of WaitableEvent*, we want to sort by the address + // of the WaitableEvent in order to have a globally consistent locking order. + // In that case we keep them, in sorted order, in an array of pairs where the + // second element is the index of the WaitableEvent in the original, + // unsorted, array. + typedef std::pair<WaitableEvent*, size_t> WaiterAndIndex; + static size_t EnqueueMany(WaiterAndIndex* waitables, + size_t count, Waiter* waiter); +#endif + + DISALLOW_COPY_AND_ASSIGN(WaitableEvent); +}; + +} // namespace base + +#endif // BASE_SYNCHRONIZATION_WAITABLE_EVENT_H_ diff --git a/base/synchronization/waitable_event_posix.cc b/base/synchronization/waitable_event_posix.cc new file mode 100644 index 0000000..9cbc03a --- /dev/null +++ b/base/synchronization/waitable_event_posix.cc @@ -0,0 +1,404 @@ +// Copyright (c) 2011 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/synchronization/waitable_event.h" + +#include "base/synchronization/condition_variable.h" +#include "base/synchronization/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) + : kernel_(new WaitableEventKernel(manual_reset, initially_signaled)) { +} + +WaitableEvent::~WaitableEvent() { +} + +void WaitableEvent::Reset() { + base::AutoLock locked(kernel_->lock_); + kernel_->signaled_ = false; +} + +void WaitableEvent::Signal() { + base::AutoLock locked(kernel_->lock_); + + if (kernel_->signaled_) + return; + + if (kernel_->manual_reset_) { + SignalAll(); + kernel_->signaled_ = true; + } else { + // In the case of auto reset, if no waiters were woken, we remain + // signaled. + if (!SignalOne()) + kernel_->signaled_ = true; + } +} + +bool WaitableEvent::IsSignaled() { + base::AutoLock locked(kernel_->lock_); + + const bool result = kernel_->signaled_; + if (result && !kernel_->manual_reset_) + kernel_->signaled_ = false; + return result; +} + +// ----------------------------------------------------------------------------- +// Synchronous waits + +// ----------------------------------------------------------------------------- +// This is a 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() + : fired_(false), + signaling_event_(NULL), + lock_(), + cv_(&lock_) { + } + + bool Fire(WaitableEvent* signaling_event) { + base::AutoLock locked(lock_); + + if (fired_) + return false; + + fired_ = true; + signaling_event_ = signaling_event; + + cv_.Broadcast(); + + // Unlike AsyncWaiter objects, SyncWaiter objects are stack-allocated on + // the blocking thread's stack. There is no |delete this;| in Fire. The + // SyncWaiter object is destroyed when it goes out of scope. + + return true; + } + + WaitableEvent* signaling_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; + } + + base::Lock* lock() { + return &lock_; + } + + base::ConditionVariable* cv() { + return &cv_; + } + + private: + bool fired_; + WaitableEvent* signaling_event_; // The WaitableEvent which woke us + base::Lock lock_; + base::ConditionVariable cv_; +}; + +bool WaitableEvent::TimedWait(const TimeDelta& max_time) { + const Time end_time(Time::Now() + max_time); + const bool finite_time = max_time.ToInternalValue() >= 0; + + kernel_->lock_.Acquire(); + if (kernel_->signaled_) { + if (!kernel_->manual_reset_) { + // In this case we were signaled when we had no waiters. Now that + // someone has waited upon us, we can automatically reset. + kernel_->signaled_ = false; + } + + kernel_->lock_.Release(); + return true; + } + + SyncWaiter sw; + sw.lock()->Acquire(); + + Enqueue(&sw); + kernel_->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 (;;) { + const Time current_time(Time::Now()); + + if (sw.fired() || (finite_time && current_time >= end_time)) { + const bool return_value = sw.fired(); + + // We can't acquire @lock_ before releasing the SyncWaiter lock (because + // of locking order), however, in between 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(); + sw.lock()->Release(); + + kernel_->lock_.Acquire(); + kernel_->Dequeue(&sw, &sw); + kernel_->lock_.Release(); + + return return_value; + } + + if (finite_time) { + const TimeDelta max_wait(end_time - current_time); + sw.cv()->TimedWait(max_wait); + } else { + sw.cv()->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); + } + + SyncWaiter sw; + + 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. + sw.lock()->Acquire(); + // Release the WaitableEvent locks in the reverse order + for (size_t i = 0; i < count; ++i) { + waitables[count - (1 + i)].first->kernel_->lock_.Release(); + } + + for (;;) { + if (sw.fired()) + break; + + sw.cv()->Wait(); + } + sw.lock()->Release(); + + // The address of the WaitableEvent which fired is stored in the SyncWaiter. + WaitableEvent *const signaled_event = sw.signaling_event(); + // This will store the index of the raw_waitables which fired. + size_t signaled_index = 0; + + // 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]->kernel_->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]->kernel_->Dequeue(&sw, &sw); + raw_waitables[i]->kernel_->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 +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; + } + + const size_t r = EnqueueMany(waitables + 1, count - 1, waiter); + if (r) { + waitables[0].first->kernel_->lock_.Release(); + } else { + waitables[0].first->Enqueue(waiter); + } + + return r; +} + +// ----------------------------------------------------------------------------- + + +// ----------------------------------------------------------------------------- +// Private functions... + +WaitableEvent::WaitableEventKernel::WaitableEventKernel(bool manual_reset, + bool initially_signaled) + : manual_reset_(manual_reset), + signaled_(initially_signaled) { +} + +WaitableEvent::WaitableEventKernel::~WaitableEventKernel() { +} + +// ----------------------------------------------------------------------------- +// Wake all waiting waiters. Called with lock held. +// ----------------------------------------------------------------------------- +bool WaitableEvent::SignalAll() { + bool signaled_at_least_one = false; + + for (std::list<Waiter*>::iterator + i = kernel_->waiters_.begin(); i != kernel_->waiters_.end(); ++i) { + if ((*i)->Fire(this)) + signaled_at_least_one = true; + } + + kernel_->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 (kernel_->waiters_.empty()) + return false; + + const bool r = (*kernel_->waiters_.begin())->Fire(this); + kernel_->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) { + kernel_->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::WaitableEventKernel::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 diff --git a/base/synchronization/waitable_event_unittest.cc b/base/synchronization/waitable_event_unittest.cc new file mode 100644 index 0000000..47e7ff7 --- /dev/null +++ b/base/synchronization/waitable_event_unittest.cc @@ -0,0 +1,106 @@ +// Copyright (c) 2011 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/time.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/platform_thread.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { + +TEST(WaitableEventTest, ManualBasics) { + WaitableEvent event(true, false); + + EXPECT_FALSE(event.IsSignaled()); + + event.Signal(); + EXPECT_TRUE(event.IsSignaled()); + EXPECT_TRUE(event.IsSignaled()); + + event.Reset(); + EXPECT_FALSE(event.IsSignaled()); + EXPECT_FALSE(event.TimedWait(TimeDelta::FromMilliseconds(10))); + + event.Signal(); + EXPECT_TRUE(event.Wait()); + EXPECT_TRUE(event.TimedWait(TimeDelta::FromMilliseconds(10))); +} + +TEST(WaitableEventTest, AutoBasics) { + WaitableEvent event(false, false); + + EXPECT_FALSE(event.IsSignaled()); + + event.Signal(); + EXPECT_TRUE(event.IsSignaled()); + EXPECT_FALSE(event.IsSignaled()); + + event.Reset(); + EXPECT_FALSE(event.IsSignaled()); + EXPECT_FALSE(event.TimedWait(TimeDelta::FromMilliseconds(10))); + + event.Signal(); + EXPECT_TRUE(event.Wait()); + EXPECT_FALSE(event.TimedWait(TimeDelta::FromMilliseconds(10))); + + event.Signal(); + EXPECT_TRUE(event.TimedWait(TimeDelta::FromMilliseconds(10))); +} + +TEST(WaitableEventTest, WaitManyShortcut) { + WaitableEvent* ev[5]; + for (unsigned i = 0; i < 5; ++i) + ev[i] = new WaitableEvent(false, false); + + ev[3]->Signal(); + EXPECT_EQ(WaitableEvent::WaitMany(ev, 5), 3u); + + ev[3]->Signal(); + EXPECT_EQ(WaitableEvent::WaitMany(ev, 5), 3u); + + ev[4]->Signal(); + EXPECT_EQ(WaitableEvent::WaitMany(ev, 5), 4u); + + ev[0]->Signal(); + EXPECT_EQ(WaitableEvent::WaitMany(ev, 5), 0u); + + for (unsigned i = 0; i < 5; ++i) + delete ev[i]; +} + +class WaitableEventSignaler : public PlatformThread::Delegate { + public: + WaitableEventSignaler(double seconds, WaitableEvent* ev) + : seconds_(seconds), + ev_(ev) { + } + + void ThreadMain() { + PlatformThread::Sleep(static_cast<int>(seconds_ * 1000)); + ev_->Signal(); + } + + private: + const double seconds_; + WaitableEvent *const ev_; +}; + +TEST(WaitableEventTest, WaitMany) { + WaitableEvent* ev[5]; + for (unsigned i = 0; i < 5; ++i) + ev[i] = new WaitableEvent(false, false); + + WaitableEventSignaler signaler(0.1, ev[2]); + PlatformThreadHandle thread; + PlatformThread::Create(0, &signaler, &thread); + + EXPECT_EQ(WaitableEvent::WaitMany(ev, 5), 2u); + + PlatformThread::Join(thread); + + for (unsigned i = 0; i < 5; ++i) + delete ev[i]; +} + +} // namespace base diff --git a/base/synchronization/waitable_event_watcher.h b/base/synchronization/waitable_event_watcher.h new file mode 100644 index 0000000..e396368 --- /dev/null +++ b/base/synchronization/waitable_event_watcher.h @@ -0,0 +1,162 @@ +// Copyright (c) 2011 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. + +#ifndef BASE_SYNCHRONIZATION_WAITABLE_EVENT_WATCHER_H_ +#define BASE_SYNCHRONIZATION_WAITABLE_EVENT_WATCHER_H_ +#pragma once + +#include "build/build_config.h" + +#if defined(OS_WIN) +#include "base/win/object_watcher.h" +#else +#include "base/message_loop.h" +#include "base/synchronization/waitable_event.h" +#endif + +namespace base { + +class Flag; +class AsyncWaiter; +class AsyncCallbackTask; +class WaitableEvent; + +// ----------------------------------------------------------------------------- +// This class provides a way to wait on a WaitableEvent asynchronously. +// +// Each instance of this object can be waiting on a single WaitableEvent. When +// the waitable event is signaled, a callback is made in the thread of a given +// MessageLoop. This callback can be deleted by deleting the waiter. +// +// Typical usage: +// +// class MyClass : public base::WaitableEventWatcher::Delegate { +// public: +// void DoStuffWhenSignaled(WaitableEvent *waitable_event) { +// watcher_.StartWatching(waitable_event, this); +// } +// virtual void OnWaitableEventSignaled(WaitableEvent* waitable_event) { +// // OK, time to do stuff! +// } +// private: +// base::WaitableEventWatcher watcher_; +// }; +// +// In the above example, MyClass wants to "do stuff" when waitable_event +// becomes signaled. WaitableEventWatcher makes this task easy. When MyClass +// goes out of scope, the watcher_ will be destroyed, and there is no need to +// worry about OnWaitableEventSignaled being called on a deleted MyClass +// pointer. +// +// BEWARE: With automatically reset WaitableEvents, a signal may be lost if it +// occurs just before a WaitableEventWatcher is deleted. There is currently no +// safe way to stop watching an automatic reset WaitableEvent without possibly +// missing a signal. +// +// NOTE: you /are/ allowed to delete the WaitableEvent while still waiting on +// it with a Watcher. It will act as if the event was never signaled. +// ----------------------------------------------------------------------------- + +class WaitableEventWatcher +#if defined(OS_POSIX) + : public MessageLoop::DestructionObserver +#endif +{ + public: + + WaitableEventWatcher(); + ~WaitableEventWatcher(); + + class Delegate { + public: + virtual ~Delegate() { } + + // ------------------------------------------------------------------------- + // This is called on the MessageLoop thread when WaitableEvent has been + // signaled. + // + // Note: the event may not be signaled by the time that this function is + // called. This indicates only that it has been signaled at some point in + // the past. + // ------------------------------------------------------------------------- + virtual void OnWaitableEventSignaled(WaitableEvent* waitable_event) = 0; + }; + + // --------------------------------------------------------------------------- + // When @event is signaled, the given delegate is called on the thread of the + // current message loop when StartWatching is called. The delegate is not + // deleted. + // --------------------------------------------------------------------------- + bool StartWatching(WaitableEvent* event, Delegate* delegate); + + // --------------------------------------------------------------------------- + // Cancel the current watch. Must be called from the same thread which + // started the watch. + // + // Does nothing if no event is being watched, nor if the watch has completed. + // The delegate will *not* be called for the current watch after this + // function returns. Since the delegate runs on the same thread as this + // function, it cannot be called during this function either. + // --------------------------------------------------------------------------- + void StopWatching(); + + // --------------------------------------------------------------------------- + // Return the currently watched event, or NULL if no object is currently being + // watched. + // --------------------------------------------------------------------------- + WaitableEvent* GetWatchedEvent(); + + // --------------------------------------------------------------------------- + // Return the delegate, or NULL if there is no delegate. + // --------------------------------------------------------------------------- + Delegate* delegate() { + return delegate_; + } + + private: + WaitableEvent* event_; + +#if defined(OS_WIN) + // --------------------------------------------------------------------------- + // The helper class exists because, if WaitableEventWatcher were to inherit + // from ObjectWatcher::Delegate, then it couldn't also have an inner class + // called Delegate (at least on Windows). Thus this object exists to proxy + // the callback function + // --------------------------------------------------------------------------- + class ObjectWatcherHelper : public win::ObjectWatcher::Delegate { + public: + ObjectWatcherHelper(WaitableEventWatcher* watcher); + + // ------------------------------------------------------------------------- + // Implementation of ObjectWatcher::Delegate + // ------------------------------------------------------------------------- + void OnObjectSignaled(HANDLE h); + + private: + WaitableEventWatcher *const watcher_; + }; + + void OnObjectSignaled(); + + ObjectWatcherHelper helper_; + win::ObjectWatcher watcher_; +#else + // --------------------------------------------------------------------------- + // Implementation of MessageLoop::DestructionObserver + // --------------------------------------------------------------------------- + virtual void WillDestroyCurrentMessageLoop(); + + MessageLoop* message_loop_; + scoped_refptr<Flag> cancel_flag_; + AsyncWaiter* waiter_; + AsyncCallbackTask* callback_task_; + scoped_refptr<WaitableEvent::WaitableEventKernel> kernel_; +#endif + + Delegate* delegate_; +}; + +} // namespace base + +#endif // BASE_SYNCHRONIZATION_WAITABLE_EVENT_WATCHER_H_ diff --git a/base/synchronization/waitable_event_watcher_posix.cc b/base/synchronization/waitable_event_watcher_posix.cc new file mode 100644 index 0000000..048bbd0 --- /dev/null +++ b/base/synchronization/waitable_event_watcher_posix.cc @@ -0,0 +1,280 @@ +// Copyright (c) 2011 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/synchronization/waitable_event_watcher.h" + +#include "base/message_loop.h" +#include "base/synchronization/lock.h" +#include "base/synchronization/waitable_event.h" + +namespace base { + +// ----------------------------------------------------------------------------- +// WaitableEventWatcher (async waits). +// +// The basic design is that we add an AsyncWaiter to the wait-list of the event. +// That AsyncWaiter has a pointer to MessageLoop, and a Task to be posted to it. +// The MessageLoop ends up running the task, which calls the delegate. +// +// Since the wait can be canceled, we have a thread-safe Flag object which is +// set when the wait has been canceled. At each stage in the above, we check the +// flag before going onto the next stage. Since the wait may only be canceled in +// the MessageLoop which runs the Task, we are assured that the delegate cannot +// be called after canceling... + +// ----------------------------------------------------------------------------- +// A thread-safe, reference-counted, write-once flag. +// ----------------------------------------------------------------------------- +class Flag : public RefCountedThreadSafe<Flag> { + public: + Flag() { flag_ = false; } + + void Set() { + AutoLock locked(lock_); + flag_ = true; + } + + bool value() const { + AutoLock locked(lock_); + return flag_; + } + + private: + mutable Lock lock_; + bool flag_; +}; + +// ----------------------------------------------------------------------------- +// This is an asynchronous waiter which posts a task to a MessageLoop when +// fired. An AsyncWaiter may only be in a single wait-list. +// ----------------------------------------------------------------------------- +class AsyncWaiter : public WaitableEvent::Waiter { + public: + AsyncWaiter(MessageLoop* message_loop, Task* task, Flag* flag) + : message_loop_(message_loop), + cb_task_(task), + flag_(flag) { } + + bool Fire(WaitableEvent* event) { + if (flag_->value()) { + // If the callback has been canceled, we don't enqueue the task, we just + // delete it instead. + delete cb_task_; + } else { + message_loop_->PostTask(FROM_HERE, cb_task_); + } + + // We are removed from the wait-list by the WaitableEvent itself. It only + // remains to delete ourselves. + delete this; + + // We can always return true because an AsyncWaiter is never in two + // different wait-lists at the same time. + return true; + } + + // See StopWatching for discussion + bool Compare(void* tag) { + return tag == flag_.get(); + } + + private: + MessageLoop *const message_loop_; + Task *const cb_task_; + scoped_refptr<Flag> flag_; +}; + +// ----------------------------------------------------------------------------- +// For async waits we need to make a callback in a MessageLoop thread. We do +// this by posting this task, which calls the delegate and keeps track of when +// the event is canceled. +// ----------------------------------------------------------------------------- +class AsyncCallbackTask : public Task { + public: + AsyncCallbackTask(Flag* flag, WaitableEventWatcher::Delegate* delegate, + WaitableEvent* event) + : flag_(flag), + delegate_(delegate), + event_(event) { + } + + void Run() { + // Runs in MessageLoop thread. + if (!flag_->value()) { + // This is to let the WaitableEventWatcher know that the event has occured + // because it needs to be able to return NULL from GetWatchedObject + flag_->Set(); + delegate_->OnWaitableEventSignaled(event_); + } + + // We are deleted by the MessageLoop + } + + private: + scoped_refptr<Flag> flag_; + WaitableEventWatcher::Delegate *const delegate_; + WaitableEvent *const event_; +}; + +WaitableEventWatcher::WaitableEventWatcher() + : event_(NULL), + message_loop_(NULL), + cancel_flag_(NULL), + waiter_(NULL), + callback_task_(NULL), + delegate_(NULL) { +} + +WaitableEventWatcher::~WaitableEventWatcher() { + StopWatching(); +} + +// ----------------------------------------------------------------------------- +// The Handle is how the user cancels a wait. After deleting the Handle we +// insure that the delegate cannot be called. +// ----------------------------------------------------------------------------- +bool WaitableEventWatcher::StartWatching + (WaitableEvent* event, WaitableEventWatcher::Delegate* delegate) { + MessageLoop *const current_ml = MessageLoop::current(); + DCHECK(current_ml) << "Cannot create WaitableEventWatcher without a " + "current MessageLoop"; + + // A user may call StartWatching from within the callback function. In this + // case, we won't know that we have finished watching, expect that the Flag + // will have been set in AsyncCallbackTask::Run() + if (cancel_flag_.get() && cancel_flag_->value()) { + if (message_loop_) { + message_loop_->RemoveDestructionObserver(this); + message_loop_ = NULL; + } + + cancel_flag_ = NULL; + } + + DCHECK(!cancel_flag_.get()) << "StartWatching called while still watching"; + + cancel_flag_ = new Flag; + callback_task_ = new AsyncCallbackTask(cancel_flag_, delegate, event); + WaitableEvent::WaitableEventKernel* kernel = event->kernel_.get(); + + AutoLock locked(kernel->lock_); + + delegate_ = delegate; + event_ = event; + + if (kernel->signaled_) { + if (!kernel->manual_reset_) + kernel->signaled_ = false; + + // No hairpinning - we can't call the delegate directly here. We have to + // enqueue a task on the MessageLoop as normal. + current_ml->PostTask(FROM_HERE, callback_task_); + return true; + } + + message_loop_ = current_ml; + current_ml->AddDestructionObserver(this); + + kernel_ = kernel; + waiter_ = new AsyncWaiter(current_ml, callback_task_, cancel_flag_); + event->Enqueue(waiter_); + + return true; +} + +void WaitableEventWatcher::StopWatching() { + delegate_ = NULL; + + if (message_loop_) { + message_loop_->RemoveDestructionObserver(this); + message_loop_ = NULL; + } + + if (!cancel_flag_.get()) // if not currently watching... + return; + + if (cancel_flag_->value()) { + // In this case, the event has fired, but we haven't figured that out yet. + // The WaitableEvent may have been deleted too. + cancel_flag_ = NULL; + return; + } + + if (!kernel_.get()) { + // We have no kernel. This means that we never enqueued a Waiter on an + // event because the event was already signaled when StartWatching was + // called. + // + // In this case, a task was enqueued on the MessageLoop and will run. + // We set the flag in case the task hasn't yet run. The flag will stop the + // delegate getting called. If the task has run then we have the last + // reference to the flag and it will be deleted immedately after. + cancel_flag_->Set(); + cancel_flag_ = NULL; + return; + } + + AutoLock locked(kernel_->lock_); + // We have a lock on the kernel. No one else can signal the event while we + // have it. + + // We have a possible ABA issue here. If Dequeue was to compare only the + // pointer values then it's possible that the AsyncWaiter could have been + // fired, freed and the memory reused for a different Waiter which was + // enqueued in the same wait-list. We would think that that waiter was our + // AsyncWaiter and remove it. + // + // To stop this, Dequeue also takes a tag argument which is passed to the + // virtual Compare function before the two are considered a match. So we need + // a tag which is good for the lifetime of this handle: the Flag. Since we + // have a reference to the Flag, its memory cannot be reused while this object + // still exists. So if we find a waiter with the correct pointer value, and + // which shares a Flag pointer, we have a real match. + if (kernel_->Dequeue(waiter_, cancel_flag_.get())) { + // Case 2: the waiter hasn't been signaled yet; it was still on the wait + // list. We've removed it, thus we can delete it and the task (which cannot + // have been enqueued with the MessageLoop because the waiter was never + // signaled) + delete waiter_; + delete callback_task_; + cancel_flag_ = NULL; + return; + } + + // Case 3: the waiter isn't on the wait-list, thus it was signaled. It may + // not have run yet, so we set the flag to tell it not to bother enqueuing the + // task on the MessageLoop, but to delete it instead. The Waiter deletes + // itself once run. + cancel_flag_->Set(); + cancel_flag_ = NULL; + + // If the waiter has already run then the task has been enqueued. If the Task + // hasn't yet run, the flag will stop the delegate from getting called. (This + // is thread safe because one may only delete a Handle from the MessageLoop + // thread.) + // + // If the delegate has already been called then we have nothing to do. The + // task has been deleted by the MessageLoop. +} + +WaitableEvent* WaitableEventWatcher::GetWatchedEvent() { + if (!cancel_flag_.get()) + return NULL; + + if (cancel_flag_->value()) + return NULL; + + return event_; +} + +// ----------------------------------------------------------------------------- +// This is called when the MessageLoop which the callback will be run it is +// deleted. We need to cancel the callback as if we had been deleted, but we +// will still be deleted at some point in the future. +// ----------------------------------------------------------------------------- +void WaitableEventWatcher::WillDestroyCurrentMessageLoop() { + StopWatching(); +} + +} // namespace base diff --git a/base/synchronization/waitable_event_watcher_unittest.cc b/base/synchronization/waitable_event_watcher_unittest.cc new file mode 100644 index 0000000..1715dff --- /dev/null +++ b/base/synchronization/waitable_event_watcher_unittest.cc @@ -0,0 +1,165 @@ +// Copyright (c) 2011 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/message_loop.h" +#include "base/synchronization/waitable_event.h" +#include "base/synchronization/waitable_event_watcher.h" +#include "base/threading/platform_thread.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { + +namespace { + +class QuitDelegate : public WaitableEventWatcher::Delegate { + public: + virtual void OnWaitableEventSignaled(WaitableEvent* event) { + MessageLoop::current()->Quit(); + } +}; + +class DecrementCountDelegate : public WaitableEventWatcher::Delegate { + public: + explicit DecrementCountDelegate(int* counter) : counter_(counter) { + } + virtual void OnWaitableEventSignaled(WaitableEvent* object) { + --(*counter_); + } + private: + int* counter_; +}; + +void RunTest_BasicSignal(MessageLoop::Type message_loop_type) { + MessageLoop message_loop(message_loop_type); + + // A manual-reset event that is not yet signaled. + WaitableEvent event(true, false); + + WaitableEventWatcher watcher; + EXPECT_TRUE(watcher.GetWatchedEvent() == NULL); + + QuitDelegate delegate; + watcher.StartWatching(&event, &delegate); + EXPECT_EQ(&event, watcher.GetWatchedEvent()); + + event.Signal(); + + MessageLoop::current()->Run(); + + EXPECT_TRUE(watcher.GetWatchedEvent() == NULL); +} + +void RunTest_BasicCancel(MessageLoop::Type message_loop_type) { + MessageLoop message_loop(message_loop_type); + + // A manual-reset event that is not yet signaled. + WaitableEvent event(true, false); + + WaitableEventWatcher watcher; + + QuitDelegate delegate; + watcher.StartWatching(&event, &delegate); + + watcher.StopWatching(); +} + +void RunTest_CancelAfterSet(MessageLoop::Type message_loop_type) { + MessageLoop message_loop(message_loop_type); + + // A manual-reset event that is not yet signaled. + WaitableEvent event(true, false); + + WaitableEventWatcher watcher; + + int counter = 1; + DecrementCountDelegate delegate(&counter); + + watcher.StartWatching(&event, &delegate); + + event.Signal(); + + // Let the background thread do its business + base::PlatformThread::Sleep(30); + + watcher.StopWatching(); + + MessageLoop::current()->RunAllPending(); + + // Our delegate should not have fired. + EXPECT_EQ(1, counter); +} + +void RunTest_OutlivesMessageLoop(MessageLoop::Type message_loop_type) { + // Simulate a MessageLoop that dies before an WaitableEventWatcher. This + // ordinarily doesn't happen when people use the Thread class, but it can + // happen when people use the Singleton pattern or atexit. + WaitableEvent event(true, false); + { + WaitableEventWatcher watcher; + { + MessageLoop message_loop(message_loop_type); + + QuitDelegate delegate; + watcher.StartWatching(&event, &delegate); + } + } +} + +void RunTest_DeleteUnder(MessageLoop::Type message_loop_type) { + // Delete the WaitableEvent out from under the Watcher. This is explictly + // allowed by the interface. + + MessageLoop message_loop(message_loop_type); + + { + WaitableEventWatcher watcher; + + WaitableEvent* event = new WaitableEvent(false, false); + QuitDelegate delegate; + watcher.StartWatching(event, &delegate); + delete event; + } +} + +} // namespace + +//----------------------------------------------------------------------------- + +TEST(WaitableEventWatcherTest, BasicSignal) { + RunTest_BasicSignal(MessageLoop::TYPE_DEFAULT); + RunTest_BasicSignal(MessageLoop::TYPE_IO); + RunTest_BasicSignal(MessageLoop::TYPE_UI); +} + +TEST(WaitableEventWatcherTest, BasicCancel) { + RunTest_BasicCancel(MessageLoop::TYPE_DEFAULT); + RunTest_BasicCancel(MessageLoop::TYPE_IO); + RunTest_BasicCancel(MessageLoop::TYPE_UI); +} + +TEST(WaitableEventWatcherTest, CancelAfterSet) { + RunTest_CancelAfterSet(MessageLoop::TYPE_DEFAULT); + RunTest_CancelAfterSet(MessageLoop::TYPE_IO); + RunTest_CancelAfterSet(MessageLoop::TYPE_UI); +} + +TEST(WaitableEventWatcherTest, OutlivesMessageLoop) { + RunTest_OutlivesMessageLoop(MessageLoop::TYPE_DEFAULT); + RunTest_OutlivesMessageLoop(MessageLoop::TYPE_IO); + RunTest_OutlivesMessageLoop(MessageLoop::TYPE_UI); +} + +#if defined(OS_WIN) +// Crashes sometimes on vista. http://crbug.com/62119 +#define MAYBE_DeleteUnder DISABLED_DeleteUnder +#else +#define MAYBE_DeleteUnder DeleteUnder +#endif +TEST(WaitableEventWatcherTest, MAYBE_DeleteUnder) { + RunTest_DeleteUnder(MessageLoop::TYPE_DEFAULT); + RunTest_DeleteUnder(MessageLoop::TYPE_IO); + RunTest_DeleteUnder(MessageLoop::TYPE_UI); +} + +} // namespace base diff --git a/base/synchronization/waitable_event_watcher_win.cc b/base/synchronization/waitable_event_watcher_win.cc new file mode 100644 index 0000000..9c02a4c --- /dev/null +++ b/base/synchronization/waitable_event_watcher_win.cc @@ -0,0 +1,60 @@ +// Copyright (c) 2011 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/synchronization/waitable_event_watcher.h" + +#include "base/compiler_specific.h" +#include "base/synchronization/waitable_event.h" +#include "base/win/object_watcher.h" + +namespace base { + +WaitableEventWatcher::ObjectWatcherHelper::ObjectWatcherHelper( + WaitableEventWatcher* watcher) + : watcher_(watcher) { +}; + +void WaitableEventWatcher::ObjectWatcherHelper::OnObjectSignaled(HANDLE h) { + watcher_->OnObjectSignaled(); +} + + +WaitableEventWatcher::WaitableEventWatcher() + : event_(NULL), + ALLOW_THIS_IN_INITIALIZER_LIST(helper_(this)), + delegate_(NULL) { +} + +WaitableEventWatcher::~WaitableEventWatcher() { +} + +bool WaitableEventWatcher::StartWatching(WaitableEvent* event, + Delegate* delegate) { + delegate_ = delegate; + event_ = event; + + return watcher_.StartWatching(event->handle(), &helper_); +} + +void WaitableEventWatcher::StopWatching() { + delegate_ = NULL; + event_ = NULL; + watcher_.StopWatching(); +} + +WaitableEvent* WaitableEventWatcher::GetWatchedEvent() { + return event_; +} + +void WaitableEventWatcher::OnObjectSignaled() { + WaitableEvent* event = event_; + Delegate* delegate = delegate_; + event_ = NULL; + delegate_ = NULL; + DCHECK(event); + + delegate->OnWaitableEventSignaled(event); +} + +} // namespace base diff --git a/base/synchronization/waitable_event_win.cc b/base/synchronization/waitable_event_win.cc new file mode 100644 index 0000000..0fcf488 --- /dev/null +++ b/base/synchronization/waitable_event_win.cc @@ -0,0 +1,99 @@ +// Copyright (c) 2011 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/synchronization/waitable_event.h" + +#include <math.h> +#include <windows.h> + +#include "base/logging.h" +#include "base/time.h" + +namespace base { + +WaitableEvent::WaitableEvent(bool manual_reset, bool signaled) + : handle_(CreateEvent(NULL, manual_reset, signaled, NULL)) { + // We're probably going to crash anyways if this is ever NULL, so we might as + // well make our stack reports more informative by crashing here. + CHECK(handle_); +} + +WaitableEvent::WaitableEvent(HANDLE handle) + : handle_(handle) { + CHECK(handle) << "Tried to create WaitableEvent from NULL handle"; +} + +WaitableEvent::~WaitableEvent() { + CloseHandle(handle_); +} + +HANDLE WaitableEvent::Release() { + HANDLE rv = handle_; + handle_ = INVALID_HANDLE_VALUE; + return rv; +} + +void WaitableEvent::Reset() { + ResetEvent(handle_); +} + +void WaitableEvent::Signal() { + SetEvent(handle_); +} + +bool WaitableEvent::IsSignaled() { + return TimedWait(TimeDelta::FromMilliseconds(0)); +} + +bool WaitableEvent::Wait() { + DWORD result = WaitForSingleObject(handle_, INFINITE); + // It is most unexpected that this should ever fail. Help consumers learn + // about it if it should ever fail. + DCHECK(result == WAIT_OBJECT_0) << "WaitForSingleObject failed"; + return result == WAIT_OBJECT_0; +} + +bool WaitableEvent::TimedWait(const TimeDelta& max_time) { + DCHECK(max_time >= TimeDelta::FromMicroseconds(0)); + // Be careful here. TimeDelta has a precision of microseconds, but this API + // is in milliseconds. If there are 5.5ms left, should the delay be 5 or 6? + // It should be 6 to avoid returning too early. + double timeout = ceil(max_time.InMillisecondsF()); + DWORD result = WaitForSingleObject(handle_, static_cast<DWORD>(timeout)); + switch (result) { + case WAIT_OBJECT_0: + return true; + case WAIT_TIMEOUT: + return false; + } + // It is most unexpected that this should ever fail. Help consumers learn + // about it if it should ever fail. + NOTREACHED() << "WaitForSingleObject failed"; + return false; +} + +// static +size_t WaitableEvent::WaitMany(WaitableEvent** events, size_t count) { + HANDLE handles[MAXIMUM_WAIT_OBJECTS]; + CHECK_LE(count, MAXIMUM_WAIT_OBJECTS) + << "Can only wait on " << MAXIMUM_WAIT_OBJECTS << " with WaitMany"; + + for (size_t i = 0; i < count; ++i) + handles[i] = events[i]->handle(); + + // The cast is safe because count is small - see the CHECK above. + DWORD result = + WaitForMultipleObjects(static_cast<DWORD>(count), + handles, + FALSE, // don't wait for all the objects + INFINITE); // no timeout + if (result >= WAIT_OBJECT_0 + count) { + NOTREACHED() << "WaitForMultipleObjects failed: " << GetLastError(); + return 0; + } + + return result - WAIT_OBJECT_0; +} + +} // namespace base |