Home | History | Annotate | Download | only in synchronization
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/synchronization/condition_variable.h"
      6 
      7 #include <stack>
      8 
      9 #include "base/logging.h"
     10 #include "base/synchronization/lock.h"
     11 #include "base/time.h"
     12 
     13 namespace base {
     14 
     15 ConditionVariable::ConditionVariable(Lock* user_lock)
     16     : user_lock_(*user_lock),
     17       run_state_(RUNNING),
     18       allocation_counter_(0),
     19       recycling_list_size_(0) {
     20   DCHECK(user_lock);
     21 }
     22 
     23 ConditionVariable::~ConditionVariable() {
     24   AutoLock auto_lock(internal_lock_);
     25   run_state_ = SHUTDOWN;  // Prevent any more waiting.
     26 
     27   DCHECK_EQ(recycling_list_size_, allocation_counter_);
     28   if (recycling_list_size_ != allocation_counter_) {  // Rare shutdown problem.
     29     // There are threads of execution still in this->TimedWait() and yet the
     30     // caller has instigated the destruction of this instance :-/.
     31     // A common reason for such "overly hasty" destruction is that the caller
     32     // was not willing to wait for all the threads to terminate.  Such hasty
     33     // actions are a violation of our usage contract, but we'll give the
     34     // waiting thread(s) one last chance to exit gracefully (prior to our
     35     // destruction).
     36     // Note: waiting_list_ *might* be empty, but recycling is still pending.
     37     AutoUnlock auto_unlock(internal_lock_);
     38     Broadcast();  // Make sure all waiting threads have been signaled.
     39     Sleep(10);  // Give threads a chance to grab internal_lock_.
     40     // All contained threads should be blocked on user_lock_ by now :-).
     41   }  // Reacquire internal_lock_.
     42 
     43   DCHECK_EQ(recycling_list_size_, allocation_counter_);
     44 }
     45 
     46 void ConditionVariable::Wait() {
     47   // Default to "wait forever" timing, which means have to get a Signal()
     48   // or Broadcast() to come out of this wait state.
     49   TimedWait(TimeDelta::FromMilliseconds(INFINITE));
     50 }
     51 
     52 void ConditionVariable::TimedWait(const TimeDelta& max_time) {
     53   Event* waiting_event;
     54   HANDLE handle;
     55   {
     56     AutoLock auto_lock(internal_lock_);
     57     if (RUNNING != run_state_) return;  // Destruction in progress.
     58     waiting_event = GetEventForWaiting();
     59     handle = waiting_event->handle();
     60     DCHECK(handle);
     61   }  // Release internal_lock.
     62 
     63   {
     64     AutoUnlock unlock(user_lock_);  // Release caller's lock
     65     WaitForSingleObject(handle, static_cast<DWORD>(max_time.InMilliseconds()));
     66     // Minimize spurious signal creation window by recycling asap.
     67     AutoLock auto_lock(internal_lock_);
     68     RecycleEvent(waiting_event);
     69     // Release internal_lock_
     70   }  // Reacquire callers lock to depth at entry.
     71 }
     72 
     73 // Broadcast() is guaranteed to signal all threads that were waiting (i.e., had
     74 // a cv_event internally allocated for them) before Broadcast() was called.
     75 void ConditionVariable::Broadcast() {
     76   std::stack<HANDLE> handles;  // See FAQ-question-10.
     77   {
     78     AutoLock auto_lock(internal_lock_);
     79     if (waiting_list_.IsEmpty())
     80       return;
     81     while (!waiting_list_.IsEmpty())
     82       // This is not a leak from waiting_list_.  See FAQ-question 12.
     83       handles.push(waiting_list_.PopBack()->handle());
     84   }  // Release internal_lock_.
     85   while (!handles.empty()) {
     86     SetEvent(handles.top());
     87     handles.pop();
     88   }
     89 }
     90 
     91 // Signal() will select one of the waiting threads, and signal it (signal its
     92 // cv_event).  For better performance we signal the thread that went to sleep
     93 // most recently (LIFO).  If we want fairness, then we wake the thread that has
     94 // been sleeping the longest (FIFO).
     95 void ConditionVariable::Signal() {
     96   HANDLE handle;
     97   {
     98     AutoLock auto_lock(internal_lock_);
     99     if (waiting_list_.IsEmpty())
    100       return;  // No one to signal.
    101     // Only performance option should be used.
    102     // This is not a leak from waiting_list.  See FAQ-question 12.
    103      handle = waiting_list_.PopBack()->handle();  // LIFO.
    104   }  // Release internal_lock_.
    105   SetEvent(handle);
    106 }
    107 
    108 // GetEventForWaiting() provides a unique cv_event for any caller that needs to
    109 // wait.  This means that (worst case) we may over time create as many cv_event
    110 // objects as there are threads simultaneously using this instance's Wait()
    111 // functionality.
    112 ConditionVariable::Event* ConditionVariable::GetEventForWaiting() {
    113   // We hold internal_lock, courtesy of Wait().
    114   Event* cv_event;
    115   if (0 == recycling_list_size_) {
    116     DCHECK(recycling_list_.IsEmpty());
    117     cv_event = new Event();
    118     cv_event->InitListElement();
    119     allocation_counter_++;
    120     CHECK(cv_event->handle());
    121   } else {
    122     cv_event = recycling_list_.PopFront();
    123     recycling_list_size_--;
    124   }
    125   waiting_list_.PushBack(cv_event);
    126   return cv_event;
    127 }
    128 
    129 // RecycleEvent() takes a cv_event that was previously used for Wait()ing, and
    130 // recycles it for use in future Wait() calls for this or other threads.
    131 // Note that there is a tiny chance that the cv_event is still signaled when we
    132 // obtain it, and that can cause spurious signals (if/when we re-use the
    133 // cv_event), but such is quite rare (see FAQ-question-5).
    134 void ConditionVariable::RecycleEvent(Event* used_event) {
    135   // We hold internal_lock, courtesy of Wait().
    136   // If the cv_event timed out, then it is necessary to remove it from
    137   // waiting_list_.  If it was selected by Broadcast() or Signal(), then it is
    138   // already gone.
    139   used_event->Extract();  // Possibly redundant
    140   recycling_list_.PushBack(used_event);
    141   recycling_list_size_++;
    142 }
    143 //------------------------------------------------------------------------------
    144 // The next section provides the implementation for the private Event class.
    145 //------------------------------------------------------------------------------
    146 
    147 // Event provides a doubly-linked-list of events for use exclusively by the
    148 // ConditionVariable class.
    149 
    150 // This custom container was crafted because no simple combination of STL
    151 // classes appeared to support the functionality required.  The specific
    152 // unusual requirement for a linked-list-class is support for the Extract()
    153 // method, which can remove an element from a list, potentially for insertion
    154 // into a second list.  Most critically, the Extract() method is idempotent,
    155 // turning the indicated element into an extracted singleton whether it was
    156 // contained in a list or not.  This functionality allows one (or more) of
    157 // threads to do the extraction.  The iterator that identifies this extractable
    158 // element (in this case, a pointer to the list element) can be used after
    159 // arbitrary manipulation of the (possibly) enclosing list container.  In
    160 // general, STL containers do not provide iterators that can be used across
    161 // modifications (insertions/extractions) of the enclosing containers, and
    162 // certainly don't provide iterators that can be used if the identified
    163 // element is *deleted* (removed) from the container.
    164 
    165 // It is possible to use multiple redundant containers, such as an STL list,
    166 // and an STL map, to achieve similar container semantics.  This container has
    167 // only O(1) methods, while the corresponding (multiple) STL container approach
    168 // would have more complex O(log(N)) methods (yeah... N isn't that large).
    169 // Multiple containers also makes correctness more difficult to assert, as
    170 // data is redundantly stored and maintained, which is generally evil.
    171 
    172 ConditionVariable::Event::Event() : handle_(0) {
    173   next_ = prev_ = this;  // Self referencing circular.
    174 }
    175 
    176 ConditionVariable::Event::~Event() {
    177   if (0 == handle_) {
    178     // This is the list holder
    179     while (!IsEmpty()) {
    180       Event* cv_event = PopFront();
    181       DCHECK(cv_event->ValidateAsItem());
    182       delete cv_event;
    183     }
    184   }
    185   DCHECK(IsSingleton());
    186   if (0 != handle_) {
    187     int ret_val = CloseHandle(handle_);
    188     DCHECK(ret_val);
    189   }
    190 }
    191 
    192 // Change a container instance permanently into an element of a list.
    193 void ConditionVariable::Event::InitListElement() {
    194   DCHECK(!handle_);
    195   handle_ = CreateEvent(NULL, false, false, NULL);
    196   CHECK(handle_);
    197 }
    198 
    199 // Methods for use on lists.
    200 bool ConditionVariable::Event::IsEmpty() const {
    201   DCHECK(ValidateAsList());
    202   return IsSingleton();
    203 }
    204 
    205 void ConditionVariable::Event::PushBack(Event* other) {
    206   DCHECK(ValidateAsList());
    207   DCHECK(other->ValidateAsItem());
    208   DCHECK(other->IsSingleton());
    209   // Prepare other for insertion.
    210   other->prev_ = prev_;
    211   other->next_ = this;
    212   // Cut into list.
    213   prev_->next_ = other;
    214   prev_ = other;
    215   DCHECK(ValidateAsDistinct(other));
    216 }
    217 
    218 ConditionVariable::Event* ConditionVariable::Event::PopFront() {
    219   DCHECK(ValidateAsList());
    220   DCHECK(!IsSingleton());
    221   return next_->Extract();
    222 }
    223 
    224 ConditionVariable::Event* ConditionVariable::Event::PopBack() {
    225   DCHECK(ValidateAsList());
    226   DCHECK(!IsSingleton());
    227   return prev_->Extract();
    228 }
    229 
    230 // Methods for use on list elements.
    231 // Accessor method.
    232 HANDLE ConditionVariable::Event::handle() const {
    233   DCHECK(ValidateAsItem());
    234   return handle_;
    235 }
    236 
    237 // Pull an element from a list (if it's in one).
    238 ConditionVariable::Event* ConditionVariable::Event::Extract() {
    239   DCHECK(ValidateAsItem());
    240   if (!IsSingleton()) {
    241     // Stitch neighbors together.
    242     next_->prev_ = prev_;
    243     prev_->next_ = next_;
    244     // Make extractee into a singleton.
    245     prev_ = next_ = this;
    246   }
    247   DCHECK(IsSingleton());
    248   return this;
    249 }
    250 
    251 // Method for use on a list element or on a list.
    252 bool ConditionVariable::Event::IsSingleton() const {
    253   DCHECK(ValidateLinks());
    254   return next_ == this;
    255 }
    256 
    257 // Provide pre/post conditions to validate correct manipulations.
    258 bool ConditionVariable::Event::ValidateAsDistinct(Event* other) const {
    259   return ValidateLinks() && other->ValidateLinks() && (this != other);
    260 }
    261 
    262 bool ConditionVariable::Event::ValidateAsItem() const {
    263   return (0 != handle_) && ValidateLinks();
    264 }
    265 
    266 bool ConditionVariable::Event::ValidateAsList() const {
    267   return (0 == handle_) && ValidateLinks();
    268 }
    269 
    270 bool ConditionVariable::Event::ValidateLinks() const {
    271   // Make sure both of our neighbors have links that point back to us.
    272   // We don't do the O(n) check and traverse the whole loop, and instead only
    273   // do a local check to (and returning from) our immediate neighbors.
    274   return (next_->prev_ == this) && (prev_->next_ == this);
    275 }
    276 
    277 
    278 /*
    279 FAQ On subtle implementation details:
    280 
    281 1) What makes this problem subtle?  Please take a look at "Strategies
    282 for Implementing POSIX Condition Variables on Win32" by Douglas
    283 C. Schmidt and Irfan Pyarali.
    284 http://www.cs.wustl.edu/~schmidt/win32-cv-1.html It includes
    285 discussions of numerous flawed strategies for implementing this
    286 functionality.  I'm not convinced that even the final proposed
    287 implementation has semantics that are as nice as this implementation
    288 (especially with regard to Broadcast() and the impact on threads that
    289 try to Wait() after a Broadcast() has been called, but before all the
    290 original waiting threads have been signaled).
    291 
    292 2) Why can't you use a single wait_event for all threads that call
    293 Wait()?  See FAQ-question-1, or consider the following: If a single
    294 event were used, then numerous threads calling Wait() could release
    295 their cs locks, and be preempted just before calling
    296 WaitForSingleObject().  If a call to Broadcast() was then presented on
    297 a second thread, it would be impossible to actually signal all
    298 waiting(?) threads.  Some number of SetEvent() calls *could* be made,
    299 but there could be no guarantee that those led to to more than one
    300 signaled thread (SetEvent()'s may be discarded after the first!), and
    301 there could be no guarantee that the SetEvent() calls didn't just
    302 awaken "other" threads that hadn't even started waiting yet (oops).
    303 Without any limit on the number of requisite SetEvent() calls, the
    304 system would be forced to do many such calls, allowing many new waits
    305 to receive spurious signals.
    306 
    307 3) How does this implementation cause spurious signal events?  The
    308 cause in this implementation involves a race between a signal via
    309 time-out and a signal via Signal() or Broadcast().  The series of
    310 actions leading to this are:
    311 
    312 a) Timer fires, and a waiting thread exits the line of code:
    313 
    314     WaitForSingleObject(waiting_event, max_time.InMilliseconds());
    315 
    316 b) That thread (in (a)) is randomly pre-empted after the above line,
    317 leaving the waiting_event reset (unsignaled) and still in the
    318 waiting_list_.
    319 
    320 c) A call to Signal() (or Broadcast()) on a second thread proceeds, and
    321 selects the waiting cv_event (identified in step (b)) as the event to revive
    322 via a call to SetEvent().
    323 
    324 d) The Signal() method (step c) calls SetEvent() on waiting_event (step b).
    325 
    326 e) The waiting cv_event (step b) is now signaled, but no thread is
    327 waiting on it.
    328 
    329 f) When that waiting_event (step b) is reused, it will immediately
    330 be signaled (spuriously).
    331 
    332 
    333 4) Why do you recycle events, and cause spurious signals?  First off,
    334 the spurious events are very rare.  They can only (I think) appear
    335 when the race described in FAQ-question-3 takes place.  This should be
    336 very rare.  Most(?)  uses will involve only timer expiration, or only
    337 Signal/Broadcast() actions.  When both are used, it will be rare that
    338 the race will appear, and it would require MANY Wait() and signaling
    339 activities.  If this implementation did not recycle events, then it
    340 would have to create and destroy events for every call to Wait().
    341 That allocation/deallocation and associated construction/destruction
    342 would be costly (per wait), and would only be a rare benefit (when the
    343 race was "lost" and a spurious signal took place). That would be bad
    344 (IMO) optimization trade-off.  Finally, such spurious events are
    345 allowed by the specification of condition variables (such as
    346 implemented in Vista), and hence it is better if any user accommodates
    347 such spurious events (see usage note in condition_variable.h).
    348 
    349 5) Why don't you reset events when you are about to recycle them, or
    350 about to reuse them, so that the spurious signals don't take place?
    351 The thread described in FAQ-question-3 step c may be pre-empted for an
    352 arbitrary length of time before proceeding to step d.  As a result,
    353 the wait_event may actually be re-used *before* step (e) is reached.
    354 As a result, calling reset would not help significantly.
    355 
    356 6) How is it that the callers lock is released atomically with the
    357 entry into a wait state?  We commit to the wait activity when we
    358 allocate the wait_event for use in a given call to Wait().  This
    359 allocation takes place before the caller's lock is released (and
    360 actually before our internal_lock_ is released).  That allocation is
    361 the defining moment when "the wait state has been entered," as that
    362 thread *can* now be signaled by a call to Broadcast() or Signal().
    363 Hence we actually "commit to wait" before releasing the lock, making
    364 the pair effectively atomic.
    365 
    366 8) Why do you need to lock your data structures during waiting, as the
    367 caller is already in possession of a lock?  We need to Acquire() and
    368 Release() our internal lock during Signal() and Broadcast().  If we tried
    369 to use a callers lock for this purpose, we might conflict with their
    370 external use of the lock.  For example, the caller may use to consistently
    371 hold a lock on one thread while calling Signal() on another, and that would
    372 block Signal().
    373 
    374 9) Couldn't a more efficient implementation be provided if you
    375 preclude using more than one external lock in conjunction with a
    376 single ConditionVariable instance?  Yes, at least it could be viewed
    377 as a simpler API (since you don't have to reiterate the lock argument
    378 in each Wait() call).  One of the constructors now takes a specific
    379 lock as an argument, and a there are corresponding Wait() calls that
    380 don't specify a lock now.  It turns that the resulting implmentation
    381 can't be made more efficient, as the internal lock needs to be used by
    382 Signal() and Broadcast(), to access internal data structures.  As a
    383 result, I was not able to utilize the user supplied lock (which is
    384 being used by the user elsewhere presumably) to protect the private
    385 member access.
    386 
    387 9) Since you have a second lock, how can be be sure that there is no
    388 possible deadlock scenario?  Our internal_lock_ is always the last
    389 lock acquired, and the first one released, and hence a deadlock (due
    390 to critical section problems) is impossible as a consequence of our
    391 lock.
    392 
    393 10) When doing a Broadcast(), why did you copy all the events into
    394 an STL queue, rather than making a linked-loop, and iterating over it?
    395 The iterating during Broadcast() is done so outside the protection
    396 of the internal lock. As a result, other threads, such as the thread
    397 wherein a related event is waiting, could asynchronously manipulate
    398 the links around a cv_event.  As a result, the link structure cannot
    399 be used outside a lock.  Broadcast() could iterate over waiting
    400 events by cycling in-and-out of the protection of the internal_lock,
    401 but that appears more expensive than copying the list into an STL
    402 stack.
    403 
    404 11) Why did the lock.h file need to be modified so much for this
    405 change?  Central to a Condition Variable is the atomic release of a
    406 lock during a Wait().  This places Wait() functionality exactly
    407 mid-way between the two classes, Lock and Condition Variable.  Given
    408 that there can be nested Acquire()'s of locks, and Wait() had to
    409 Release() completely a held lock, it was necessary to augment the Lock
    410 class with a recursion counter. Even more subtle is the fact that the
    411 recursion counter (in a Lock) must be protected, as many threads can
    412 access it asynchronously.  As a positive fallout of this, there are
    413 now some DCHECKS to be sure no one Release()s a Lock more than they
    414 Acquire()ed it, and there is ifdef'ed functionality that can detect
    415 nested locks (legal under windows, but not under Posix).
    416 
    417 12) Why is it that the cv_events removed from list in Broadcast() and Signal()
    418 are not leaked?  How are they recovered??  The cv_events that appear to leak are
    419 taken from the waiting_list_.  For each element in that list, there is currently
    420 a thread in or around the WaitForSingleObject() call of Wait(), and those
    421 threads have references to these otherwise leaked events. They are passed as
    422 arguments to be recycled just aftre returning from WaitForSingleObject().
    423 
    424 13) Why did you use a custom container class (the linked list), when STL has
    425 perfectly good containers, such as an STL list?  The STL list, as with any
    426 container, does not guarantee the utility of an iterator across manipulation
    427 (such as insertions and deletions) of the underlying container.  The custom
    428 double-linked-list container provided that assurance.  I don't believe any
    429 combination of STL containers provided the services that were needed at the same
    430 O(1) efficiency as the custom linked list.  The unusual requirement
    431 for the container class is that a reference to an item within a container (an
    432 iterator) needed to be maintained across an arbitrary manipulation of the
    433 container.  This requirement exposes itself in the Wait() method, where a
    434 waiting_event must be selected prior to the WaitForSingleObject(), and then it
    435 must be used as part of recycling to remove the related instance from the
    436 waiting_list.  A hash table (STL map) could be used, but I was embarrased to
    437 use a complex and relatively low efficiency container when a doubly linked list
    438 provided O(1) performance in all required operations.  Since other operations
    439 to provide performance-and/or-fairness required queue (FIFO) and list (LIFO)
    440 containers, I would also have needed to use an STL list/queue as well as an STL
    441 map.  In the end I decided it would be "fun" to just do it right, and I
    442 put so many assertions (DCHECKs) into the container class that it is trivial to
    443 code review and validate its correctness.
    444 
    445 */
    446 
    447 }  // namespace base
    448