summaryrefslogtreecommitdiffstats
path: root/base/synchronization/condition_variable_win.cc
diff options
context:
space:
mode:
authorcpu@chromium.org <cpu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-12-07 19:30:45 +0000
committercpu@chromium.org <cpu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-12-07 19:30:45 +0000
commit58f66ede2875d1af9757e0c9585ddc2adcc139aa (patch)
tree8d5a9d4c317859c924a7f01860d3b69b39af8829 /base/synchronization/condition_variable_win.cc
parent317c6415bea2bc5d184326188349746911fe5c28 (diff)
downloadchromium_src-58f66ede2875d1af9757e0c9585ddc2adcc139aa.zip
chromium_src-58f66ede2875d1af9757e0c9585ddc2adcc139aa.tar.gz
chromium_src-58f66ede2875d1af9757e0c9585ddc2adcc139aa.tar.bz2
Prep work for win7-specific condition variable
Move windows implementation to pimpl idiom http://c2.com/cgi/wiki?PimplIdiom There should be no behavior change. BUG=none TEST=ConditionVariableTest in base_unittests suffice Review URL: http://codereview.chromium.org/8823012 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@113439 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/synchronization/condition_variable_win.cc')
-rw-r--r--base/synchronization/condition_variable_win.cc164
1 files changed, 140 insertions, 24 deletions
diff --git a/base/synchronization/condition_variable_win.cc b/base/synchronization/condition_variable_win.cc
index 7b9919a..cd74120 100644
--- a/base/synchronization/condition_variable_win.cc
+++ b/base/synchronization/condition_variable_win.cc
@@ -4,15 +4,107 @@
#include "base/synchronization/condition_variable.h"
+#include <windows.h>
#include <stack>
+#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/synchronization/lock.h"
#include "base/time.h"
namespace base {
-
-ConditionVariable::ConditionVariable(Lock* user_lock)
+// Abstract class of the pimpl, idiom. TODO(cpu): create the
+// WinVistaCondVar once the WinXPCondVar lands.
+class ConditionVarImpl {
+ public:
+ virtual ~ConditionVarImpl() {};
+ virtual void Wait() = 0;
+ virtual void TimedWait(const TimeDelta& max_time) = 0;
+ virtual void Broadcast() = 0;
+ virtual void Signal() = 0;
+};
+
+class WinXPCondVar : public ConditionVarImpl {
+ public:
+ WinXPCondVar(Lock* user_lock);
+ ~WinXPCondVar();
+ // Overridden from ConditionVarImpl.
+ virtual void Wait() OVERRIDE;
+ virtual void TimedWait(const TimeDelta& max_time) OVERRIDE;
+ virtual void Broadcast() OVERRIDE;
+ virtual void Signal() OVERRIDE;
+
+ // Define Event class that is used to form circularly linked lists.
+ // The list container is an element with NULL as its handle_ value.
+ // The actual list elements have a non-zero handle_ value.
+ // All calls to methods MUST be done under protection of a lock so that links
+ // can be validated. Without the lock, some links might asynchronously
+ // change, and the assertions would fail (as would list change operations).
+ class Event {
+ public:
+ // Default constructor with no arguments creates a list container.
+ Event();
+ ~Event();
+
+ // InitListElement transitions an instance from a container, to an element.
+ void InitListElement();
+
+ // Methods for use on lists.
+ bool IsEmpty() const;
+ void PushBack(Event* other);
+ Event* PopFront();
+ Event* PopBack();
+
+ // Methods for use on list elements.
+ // Accessor method.
+ HANDLE handle() const;
+ // Pull an element from a list (if it's in one).
+ Event* Extract();
+
+ // Method for use on a list element or on a list.
+ bool IsSingleton() const;
+
+ private:
+ // Provide pre/post conditions to validate correct manipulations.
+ bool ValidateAsDistinct(Event* other) const;
+ bool ValidateAsItem() const;
+ bool ValidateAsList() const;
+ bool ValidateLinks() const;
+
+ HANDLE handle_;
+ Event* next_;
+ Event* prev_;
+ DISALLOW_COPY_AND_ASSIGN(Event);
+ };
+
+ // Note that RUNNING is an unlikely number to have in RAM by accident.
+ // This helps with defensive destructor coding in the face of user error.
+ enum RunState { SHUTDOWN = 0, RUNNING = 64213 };
+
+ // Internal implementation methods supporting Wait().
+ Event* GetEventForWaiting();
+ void RecycleEvent(Event* used_event);
+
+ RunState run_state_;
+
+ // Private critical section for access to member data.
+ base::Lock internal_lock_;
+
+ // Lock that is acquired before calling Wait().
+ base::Lock& user_lock_;
+
+ // Events that threads are blocked on.
+ Event waiting_list_;
+
+ // Free list for old events.
+ Event recycling_list_;
+ int recycling_list_size_;
+
+ // The number of allocated, but not yet deleted events.
+ int allocation_counter_;
+};
+
+WinXPCondVar::WinXPCondVar(Lock* user_lock)
: user_lock_(*user_lock),
run_state_(RUNNING),
allocation_counter_(0),
@@ -20,7 +112,7 @@ ConditionVariable::ConditionVariable(Lock* user_lock)
DCHECK(user_lock);
}
-ConditionVariable::~ConditionVariable() {
+WinXPCondVar::~WinXPCondVar() {
AutoLock auto_lock(internal_lock_);
run_state_ = SHUTDOWN; // Prevent any more waiting.
@@ -43,13 +135,13 @@ ConditionVariable::~ConditionVariable() {
DCHECK_EQ(recycling_list_size_, allocation_counter_);
}
-void ConditionVariable::Wait() {
+void WinXPCondVar::Wait() {
// Default to "wait forever" timing, which means have to get a Signal()
// or Broadcast() to come out of this wait state.
TimedWait(TimeDelta::FromMilliseconds(INFINITE));
}
-void ConditionVariable::TimedWait(const TimeDelta& max_time) {
+void WinXPCondVar::TimedWait(const TimeDelta& max_time) {
Event* waiting_event;
HANDLE handle;
{
@@ -72,7 +164,7 @@ void ConditionVariable::TimedWait(const TimeDelta& max_time) {
// Broadcast() is guaranteed to signal all threads that were waiting (i.e., had
// a cv_event internally allocated for them) before Broadcast() was called.
-void ConditionVariable::Broadcast() {
+void WinXPCondVar::Broadcast() {
std::stack<HANDLE> handles; // See FAQ-question-10.
{
AutoLock auto_lock(internal_lock_);
@@ -92,7 +184,7 @@ void ConditionVariable::Broadcast() {
// cv_event). For better performance we signal the thread that went to sleep
// most recently (LIFO). If we want fairness, then we wake the thread that has
// been sleeping the longest (FIFO).
-void ConditionVariable::Signal() {
+void WinXPCondVar::Signal() {
HANDLE handle;
{
AutoLock auto_lock(internal_lock_);
@@ -109,7 +201,7 @@ void ConditionVariable::Signal() {
// wait. This means that (worst case) we may over time create as many cv_event
// objects as there are threads simultaneously using this instance's Wait()
// functionality.
-ConditionVariable::Event* ConditionVariable::GetEventForWaiting() {
+WinXPCondVar::Event* WinXPCondVar::GetEventForWaiting() {
// We hold internal_lock, courtesy of Wait().
Event* cv_event;
if (0 == recycling_list_size_) {
@@ -131,7 +223,7 @@ ConditionVariable::Event* ConditionVariable::GetEventForWaiting() {
// Note that there is a tiny chance that the cv_event is still signaled when we
// obtain it, and that can cause spurious signals (if/when we re-use the
// cv_event), but such is quite rare (see FAQ-question-5).
-void ConditionVariable::RecycleEvent(Event* used_event) {
+void WinXPCondVar::RecycleEvent(Event* used_event) {
// We hold internal_lock, courtesy of Wait().
// If the cv_event timed out, then it is necessary to remove it from
// waiting_list_. If it was selected by Broadcast() or Signal(), then it is
@@ -169,11 +261,11 @@ void ConditionVariable::RecycleEvent(Event* used_event) {
// Multiple containers also makes correctness more difficult to assert, as
// data is redundantly stored and maintained, which is generally evil.
-ConditionVariable::Event::Event() : handle_(0) {
+WinXPCondVar::Event::Event() : handle_(0) {
next_ = prev_ = this; // Self referencing circular.
}
-ConditionVariable::Event::~Event() {
+WinXPCondVar::Event::~Event() {
if (0 == handle_) {
// This is the list holder
while (!IsEmpty()) {
@@ -190,19 +282,19 @@ ConditionVariable::Event::~Event() {
}
// Change a container instance permanently into an element of a list.
-void ConditionVariable::Event::InitListElement() {
+void WinXPCondVar::Event::InitListElement() {
DCHECK(!handle_);
handle_ = CreateEvent(NULL, false, false, NULL);
DCHECK(handle_);
}
// Methods for use on lists.
-bool ConditionVariable::Event::IsEmpty() const {
+bool WinXPCondVar::Event::IsEmpty() const {
DCHECK(ValidateAsList());
return IsSingleton();
}
-void ConditionVariable::Event::PushBack(Event* other) {
+void WinXPCondVar::Event::PushBack(Event* other) {
DCHECK(ValidateAsList());
DCHECK(other->ValidateAsItem());
DCHECK(other->IsSingleton());
@@ -215,13 +307,13 @@ void ConditionVariable::Event::PushBack(Event* other) {
DCHECK(ValidateAsDistinct(other));
}
-ConditionVariable::Event* ConditionVariable::Event::PopFront() {
+WinXPCondVar::Event* WinXPCondVar::Event::PopFront() {
DCHECK(ValidateAsList());
DCHECK(!IsSingleton());
return next_->Extract();
}
-ConditionVariable::Event* ConditionVariable::Event::PopBack() {
+WinXPCondVar::Event* WinXPCondVar::Event::PopBack() {
DCHECK(ValidateAsList());
DCHECK(!IsSingleton());
return prev_->Extract();
@@ -229,13 +321,13 @@ ConditionVariable::Event* ConditionVariable::Event::PopBack() {
// Methods for use on list elements.
// Accessor method.
-HANDLE ConditionVariable::Event::handle() const {
+HANDLE WinXPCondVar::Event::handle() const {
DCHECK(ValidateAsItem());
return handle_;
}
// Pull an element from a list (if it's in one).
-ConditionVariable::Event* ConditionVariable::Event::Extract() {
+WinXPCondVar::Event* WinXPCondVar::Event::Extract() {
DCHECK(ValidateAsItem());
if (!IsSingleton()) {
// Stitch neighbors together.
@@ -249,25 +341,25 @@ ConditionVariable::Event* ConditionVariable::Event::Extract() {
}
// Method for use on a list element or on a list.
-bool ConditionVariable::Event::IsSingleton() const {
+bool WinXPCondVar::Event::IsSingleton() const {
DCHECK(ValidateLinks());
return next_ == this;
}
// Provide pre/post conditions to validate correct manipulations.
-bool ConditionVariable::Event::ValidateAsDistinct(Event* other) const {
+bool WinXPCondVar::Event::ValidateAsDistinct(Event* other) const {
return ValidateLinks() && other->ValidateLinks() && (this != other);
}
-bool ConditionVariable::Event::ValidateAsItem() const {
+bool WinXPCondVar::Event::ValidateAsItem() const {
return (0 != handle_) && ValidateLinks();
}
-bool ConditionVariable::Event::ValidateAsList() const {
+bool WinXPCondVar::Event::ValidateAsList() const {
return (0 == handle_) && ValidateLinks();
}
-bool ConditionVariable::Event::ValidateLinks() const {
+bool WinXPCondVar::Event::ValidateLinks() const {
// Make sure both of our neighbors have links that point back to us.
// We don't do the O(n) check and traverse the whole loop, and instead only
// do a local check to (and returning from) our immediate neighbors.
@@ -276,7 +368,7 @@ bool ConditionVariable::Event::ValidateLinks() const {
/*
-FAQ On subtle implementation details:
+FAQ On WinXPCondVar subtle implementation details:
1) What makes this problem subtle? Please take a look at "Strategies
for Implementing POSIX Condition Variables on Win32" by Douglas
@@ -444,4 +536,28 @@ code review and validate its correctness.
*/
+ConditionVariable::ConditionVariable(Lock* user_lock)
+ : impl_(new WinXPCondVar(user_lock)) {
+}
+
+ConditionVariable::~ConditionVariable() {
+ delete impl_;
+}
+
+void ConditionVariable::Wait() {
+ impl_->Wait();
+}
+
+void ConditionVariable::TimedWait(const TimeDelta& max_time) {
+ impl_->TimedWait(max_time);
+}
+
+void ConditionVariable::Broadcast() {
+ impl_->Broadcast();
+}
+
+void ConditionVariable::Signal() {
+ impl_->Signal();
+}
+
} // namespace base