Index: base/synchronization/condition_variable_win.cc |
=================================================================== |
--- base/synchronization/condition_variable_win.cc (revision 113067) |
+++ base/synchronization/condition_variable_win.cc (working copy) |
@@ -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 { |
+// 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; |
+}; |
-ConditionVariable::ConditionVariable(Lock* user_lock) |
+class WinXPCondVar : public ConditionVarImpl { |
+ public: |
+ WinXPCondVar(Lock* user_lock); |
+ ~WinXPCondVar(); |
+ // Overridden from ConditionVarImpl. |
brettw
2011/12/06 23:44:54
Can you put a blank line above this?
|
+ 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 @@ |
DCHECK(user_lock); |
} |
-ConditionVariable::~ConditionVariable() { |
+WinXPCondVar::~WinXPCondVar() { |
AutoLock auto_lock(internal_lock_); |
run_state_ = SHUTDOWN; // Prevent any more waiting. |
@@ -43,13 +135,13 @@ |
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 @@ |
// 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 @@ |
// 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 @@ |
// 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 @@ |
// 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 @@ |
// 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 @@ |
} |
// 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 @@ |
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 @@ |
// 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 @@ |
} |
// 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 @@ |
/* |
-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 @@ |
*/ |
+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 |