ipc/chromium/src/base/condition_variable_win.cc

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/ipc/chromium/src/base/condition_variable_win.cc	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,446 @@
     1.4 +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
     1.5 +// Use of this source code is governed by a BSD-style license that can be
     1.6 +// found in the LICENSE file.
     1.7 +
     1.8 +#include "base/condition_variable.h"
     1.9 +
    1.10 +#include <stack>
    1.11 +
    1.12 +#include "base/lock.h"
    1.13 +#include "base/logging.h"
    1.14 +#include "base/time.h"
    1.15 +
    1.16 +using base::TimeDelta;
    1.17 +
    1.18 +ConditionVariable::ConditionVariable(Lock* user_lock)
    1.19 +  : user_lock_(*user_lock),
    1.20 +    run_state_(RUNNING),
    1.21 +    allocation_counter_(0),
    1.22 +    recycling_list_size_(0) {
    1.23 +  DCHECK(user_lock);
    1.24 +}
    1.25 +
    1.26 +ConditionVariable::~ConditionVariable() {
    1.27 +  AutoLock auto_lock(internal_lock_);
    1.28 +  run_state_ = SHUTDOWN;  // Prevent any more waiting.
    1.29 +
    1.30 +  DCHECK_EQ(recycling_list_size_, allocation_counter_);
    1.31 +  if (recycling_list_size_ != allocation_counter_) {  // Rare shutdown problem.
    1.32 +    // There are threads of execution still in this->TimedWait() and yet the
    1.33 +    // caller has instigated the destruction of this instance :-/.
    1.34 +    // A common reason for such "overly hasty" destruction is that the caller
    1.35 +    // was not willing to wait for all the threads to terminate.  Such hasty
    1.36 +    // actions are a violation of our usage contract, but we'll give the
    1.37 +    // waiting thread(s) one last chance to exit gracefully (prior to our
    1.38 +    // destruction).
    1.39 +    // Note: waiting_list_ *might* be empty, but recycling is still pending.
    1.40 +    AutoUnlock auto_unlock(internal_lock_);
    1.41 +    Broadcast();  // Make sure all waiting threads have been signaled.
    1.42 +    Sleep(10);  // Give threads a chance to grab internal_lock_.
    1.43 +    // All contained threads should be blocked on user_lock_ by now :-).
    1.44 +  }  // Reacquire internal_lock_.
    1.45 +
    1.46 +  DCHECK_EQ(recycling_list_size_, allocation_counter_);
    1.47 +}
    1.48 +
    1.49 +void ConditionVariable::Wait() {
    1.50 +  // Default to "wait forever" timing, which means have to get a Signal()
    1.51 +  // or Broadcast() to come out of this wait state.
    1.52 +  TimedWait(TimeDelta::FromMilliseconds(INFINITE));
    1.53 +}
    1.54 +
    1.55 +void ConditionVariable::TimedWait(const TimeDelta& max_time) {
    1.56 +  Event* waiting_event;
    1.57 +  HANDLE handle;
    1.58 +  {
    1.59 +    AutoLock auto_lock(internal_lock_);
    1.60 +    if (RUNNING != run_state_) return;  // Destruction in progress.
    1.61 +    waiting_event = GetEventForWaiting();
    1.62 +    handle = waiting_event->handle();
    1.63 +    DCHECK(handle);
    1.64 +  }  // Release internal_lock.
    1.65 +
    1.66 +  {
    1.67 +    AutoUnlock unlock(user_lock_);  // Release caller's lock
    1.68 +    WaitForSingleObject(handle, static_cast<DWORD>(max_time.InMilliseconds()));
    1.69 +    // Minimize spurious signal creation window by recycling asap.
    1.70 +    AutoLock auto_lock(internal_lock_);
    1.71 +    RecycleEvent(waiting_event);
    1.72 +    // Release internal_lock_
    1.73 +  }  // Reacquire callers lock to depth at entry.
    1.74 +}
    1.75 +
    1.76 +// Broadcast() is guaranteed to signal all threads that were waiting (i.e., had
    1.77 +// a cv_event internally allocated for them) before Broadcast() was called.
    1.78 +void ConditionVariable::Broadcast() {
    1.79 +  std::stack<HANDLE> handles;  // See FAQ-question-10.
    1.80 +  {
    1.81 +    AutoLock auto_lock(internal_lock_);
    1.82 +    if (waiting_list_.IsEmpty())
    1.83 +      return;
    1.84 +    while (!waiting_list_.IsEmpty())
    1.85 +      // This is not a leak from waiting_list_.  See FAQ-question 12.
    1.86 +      handles.push(waiting_list_.PopBack()->handle());
    1.87 +  }  // Release internal_lock_.
    1.88 +  while (!handles.empty()) {
    1.89 +    SetEvent(handles.top());
    1.90 +    handles.pop();
    1.91 +  }
    1.92 +}
    1.93 +
    1.94 +// Signal() will select one of the waiting threads, and signal it (signal its
    1.95 +// cv_event).  For better performance we signal the thread that went to sleep
    1.96 +// most recently (LIFO).  If we want fairness, then we wake the thread that has
    1.97 +// been sleeping the longest (FIFO).
    1.98 +void ConditionVariable::Signal() {
    1.99 +  HANDLE handle;
   1.100 +  {
   1.101 +    AutoLock auto_lock(internal_lock_);
   1.102 +    if (waiting_list_.IsEmpty())
   1.103 +      return;  // No one to signal.
   1.104 +    // Only performance option should be used.
   1.105 +    // This is not a leak from waiting_list.  See FAQ-question 12.
   1.106 +     handle = waiting_list_.PopBack()->handle();  // LIFO.
   1.107 +  }  // Release internal_lock_.
   1.108 +  SetEvent(handle);
   1.109 +}
   1.110 +
   1.111 +// GetEventForWaiting() provides a unique cv_event for any caller that needs to
   1.112 +// wait.  This means that (worst case) we may over time create as many cv_event
   1.113 +// objects as there are threads simultaneously using this instance's Wait()
   1.114 +// functionality.
   1.115 +ConditionVariable::Event* ConditionVariable::GetEventForWaiting() {
   1.116 +  // We hold internal_lock, courtesy of Wait().
   1.117 +  Event* cv_event;
   1.118 +  if (0 == recycling_list_size_) {
   1.119 +    DCHECK(recycling_list_.IsEmpty());
   1.120 +    cv_event = new Event();
   1.121 +    cv_event->InitListElement();
   1.122 +    allocation_counter_++;
   1.123 +    // CHECK_NE is not defined in our codebase, so we have to use CHECK
   1.124 +    CHECK(cv_event->handle());
   1.125 +  } else {
   1.126 +    cv_event = recycling_list_.PopFront();
   1.127 +    recycling_list_size_--;
   1.128 +  }
   1.129 +  waiting_list_.PushBack(cv_event);
   1.130 +  return cv_event;
   1.131 +}
   1.132 +
   1.133 +// RecycleEvent() takes a cv_event that was previously used for Wait()ing, and
   1.134 +// recycles it for use in future Wait() calls for this or other threads.
   1.135 +// Note that there is a tiny chance that the cv_event is still signaled when we
   1.136 +// obtain it, and that can cause spurious signals (if/when we re-use the
   1.137 +// cv_event), but such is quite rare (see FAQ-question-5).
   1.138 +void ConditionVariable::RecycleEvent(Event* used_event) {
   1.139 +  // We hold internal_lock, courtesy of Wait().
   1.140 +  // If the cv_event timed out, then it is necessary to remove it from
   1.141 +  // waiting_list_.  If it was selected by Broadcast() or Signal(), then it is
   1.142 +  // already gone.
   1.143 +  used_event->Extract();  // Possibly redundant
   1.144 +  recycling_list_.PushBack(used_event);
   1.145 +  recycling_list_size_++;
   1.146 +}
   1.147 +//------------------------------------------------------------------------------
   1.148 +// The next section provides the implementation for the private Event class.
   1.149 +//------------------------------------------------------------------------------
   1.150 +
   1.151 +// Event provides a doubly-linked-list of events for use exclusively by the
   1.152 +// ConditionVariable class.
   1.153 +
   1.154 +// This custom container was crafted because no simple combination of STL
   1.155 +// classes appeared to support the functionality required.  The specific
   1.156 +// unusual requirement for a linked-list-class is support for the Extract()
   1.157 +// method, which can remove an element from a list, potentially for insertion
   1.158 +// into a second list.  Most critically, the Extract() method is idempotent,
   1.159 +// turning the indicated element into an extracted singleton whether it was
   1.160 +// contained in a list or not.  This functionality allows one (or more) of
   1.161 +// threads to do the extraction.  The iterator that identifies this extractable
   1.162 +// element (in this case, a pointer to the list element) can be used after
   1.163 +// arbitrary manipulation of the (possibly) enclosing list container.  In
   1.164 +// general, STL containers do not provide iterators that can be used across
   1.165 +// modifications (insertions/extractions) of the enclosing containers, and
   1.166 +// certainly don't provide iterators that can be used if the identified
   1.167 +// element is *deleted* (removed) from the container.
   1.168 +
   1.169 +// It is possible to use multiple redundant containers, such as an STL list,
   1.170 +// and an STL map, to achieve similar container semantics.  This container has
   1.171 +// only O(1) methods, while the corresponding (multiple) STL container approach
   1.172 +// would have more complex O(log(N)) methods (yeah... N isn't that large).
   1.173 +// Multiple containers also makes correctness more difficult to assert, as
   1.174 +// data is redundantly stored and maintained, which is generally evil.
   1.175 +
   1.176 +ConditionVariable::Event::Event() : handle_(0) {
   1.177 +  next_ = prev_ = this;  // Self referencing circular.
   1.178 +}
   1.179 +
   1.180 +ConditionVariable::Event::~Event() {
   1.181 +  if (0 == handle_) {
   1.182 +    // This is the list holder
   1.183 +    while (!IsEmpty()) {
   1.184 +      Event* cv_event = PopFront();
   1.185 +      DCHECK(cv_event->ValidateAsItem());
   1.186 +      delete cv_event;
   1.187 +    }
   1.188 +  }
   1.189 +  DCHECK(IsSingleton());
   1.190 +  if (0 != handle_) {
   1.191 +    int ret_val = CloseHandle(handle_);
   1.192 +    DCHECK(ret_val);
   1.193 +  }
   1.194 +}
   1.195 +
   1.196 +// Change a container instance permanently into an element of a list.
   1.197 +void ConditionVariable::Event::InitListElement() {
   1.198 +  DCHECK(!handle_);
   1.199 +  handle_ = CreateEvent(NULL, false, false, NULL);
   1.200 +  CHECK(handle_);
   1.201 +}
   1.202 +
   1.203 +// Methods for use on lists.
   1.204 +bool ConditionVariable::Event::IsEmpty() const {
   1.205 +  DCHECK(ValidateAsList());
   1.206 +  return IsSingleton();
   1.207 +}
   1.208 +
   1.209 +void ConditionVariable::Event::PushBack(Event* other) {
   1.210 +  DCHECK(ValidateAsList());
   1.211 +  DCHECK(other->ValidateAsItem());
   1.212 +  DCHECK(other->IsSingleton());
   1.213 +  // Prepare other for insertion.
   1.214 +  other->prev_ = prev_;
   1.215 +  other->next_ = this;
   1.216 +  // Cut into list.
   1.217 +  prev_->next_ = other;
   1.218 +  prev_ = other;
   1.219 +  DCHECK(ValidateAsDistinct(other));
   1.220 +}
   1.221 +
   1.222 +ConditionVariable::Event* ConditionVariable::Event::PopFront() {
   1.223 +  DCHECK(ValidateAsList());
   1.224 +  DCHECK(!IsSingleton());
   1.225 +  return next_->Extract();
   1.226 +}
   1.227 +
   1.228 +ConditionVariable::Event* ConditionVariable::Event::PopBack() {
   1.229 +  DCHECK(ValidateAsList());
   1.230 +  DCHECK(!IsSingleton());
   1.231 +  return prev_->Extract();
   1.232 +}
   1.233 +
   1.234 +// Methods for use on list elements.
   1.235 +// Accessor method.
   1.236 +HANDLE ConditionVariable::Event::handle() const {
   1.237 +  DCHECK(ValidateAsItem());
   1.238 +  return handle_;
   1.239 +}
   1.240 +
   1.241 +// Pull an element from a list (if it's in one).
   1.242 +ConditionVariable::Event* ConditionVariable::Event::Extract() {
   1.243 +  DCHECK(ValidateAsItem());
   1.244 +  if (!IsSingleton()) {
   1.245 +    // Stitch neighbors together.
   1.246 +    next_->prev_ = prev_;
   1.247 +    prev_->next_ = next_;
   1.248 +    // Make extractee into a singleton.
   1.249 +    prev_ = next_ = this;
   1.250 +  }
   1.251 +  DCHECK(IsSingleton());
   1.252 +  return this;
   1.253 +}
   1.254 +
   1.255 +// Method for use on a list element or on a list.
   1.256 +bool ConditionVariable::Event::IsSingleton() const {
   1.257 +  DCHECK(ValidateLinks());
   1.258 +  return next_ == this;
   1.259 +}
   1.260 +
   1.261 +// Provide pre/post conditions to validate correct manipulations.
   1.262 +bool ConditionVariable::Event::ValidateAsDistinct(Event* other) const {
   1.263 +  return ValidateLinks() && other->ValidateLinks() && (this != other);
   1.264 +}
   1.265 +
   1.266 +bool ConditionVariable::Event::ValidateAsItem() const {
   1.267 +  return (0 != handle_) && ValidateLinks();
   1.268 +}
   1.269 +
   1.270 +bool ConditionVariable::Event::ValidateAsList() const {
   1.271 +  return (0 == handle_) && ValidateLinks();
   1.272 +}
   1.273 +
   1.274 +bool ConditionVariable::Event::ValidateLinks() const {
   1.275 +  // Make sure both of our neighbors have links that point back to us.
   1.276 +  // We don't do the O(n) check and traverse the whole loop, and instead only
   1.277 +  // do a local check to (and returning from) our immediate neighbors.
   1.278 +  return (next_->prev_ == this) && (prev_->next_ == this);
   1.279 +}
   1.280 +
   1.281 +
   1.282 +/*
   1.283 +FAQ On subtle implementation details:
   1.284 +
   1.285 +1) What makes this problem subtle?  Please take a look at "Strategies
   1.286 +for Implementing POSIX Condition Variables on Win32" by Douglas
   1.287 +C. Schmidt and Irfan Pyarali.
   1.288 +http://www.cs.wustl.edu/~schmidt/win32-cv-1.html It includes
   1.289 +discussions of numerous flawed strategies for implementing this
   1.290 +functionality.  I'm not convinced that even the final proposed
   1.291 +implementation has semantics that are as nice as this implementation
   1.292 +(especially with regard to Broadcast() and the impact on threads that
   1.293 +try to Wait() after a Broadcast() has been called, but before all the
   1.294 +original waiting threads have been signaled).
   1.295 +
   1.296 +2) Why can't you use a single wait_event for all threads that call
   1.297 +Wait()?  See FAQ-question-1, or consider the following: If a single
   1.298 +event were used, then numerous threads calling Wait() could release
   1.299 +their cs locks, and be preempted just before calling
   1.300 +WaitForSingleObject().  If a call to Broadcast() was then presented on
   1.301 +a second thread, it would be impossible to actually signal all
   1.302 +waiting(?) threads.  Some number of SetEvent() calls *could* be made,
   1.303 +but there could be no guarantee that those led to to more than one
   1.304 +signaled thread (SetEvent()'s may be discarded after the first!), and
   1.305 +there could be no guarantee that the SetEvent() calls didn't just
   1.306 +awaken "other" threads that hadn't even started waiting yet (oops).
   1.307 +Without any limit on the number of requisite SetEvent() calls, the
   1.308 +system would be forced to do many such calls, allowing many new waits
   1.309 +to receive spurious signals.
   1.310 +
   1.311 +3) How does this implementation cause spurious signal events?  The
   1.312 +cause in this implementation involves a race between a signal via
   1.313 +time-out and a signal via Signal() or Broadcast().  The series of
   1.314 +actions leading to this are:
   1.315 +
   1.316 +a) Timer fires, and a waiting thread exits the line of code:
   1.317 +
   1.318 +    WaitForSingleObject(waiting_event, max_time.InMilliseconds());
   1.319 +
   1.320 +b) That thread (in (a)) is randomly pre-empted after the above line,
   1.321 +leaving the waiting_event reset (unsignaled) and still in the
   1.322 +waiting_list_.
   1.323 +
   1.324 +c) A call to Signal() (or Broadcast()) on a second thread proceeds, and
   1.325 +selects the waiting cv_event (identified in step (b)) as the event to revive
   1.326 +via a call to SetEvent().
   1.327 +
   1.328 +d) The Signal() method (step c) calls SetEvent() on waiting_event (step b).
   1.329 +
   1.330 +e) The waiting cv_event (step b) is now signaled, but no thread is
   1.331 +waiting on it.
   1.332 +
   1.333 +f) When that waiting_event (step b) is reused, it will immediately
   1.334 +be signaled (spuriously).
   1.335 +
   1.336 +
   1.337 +4) Why do you recycle events, and cause spurious signals?  First off,
   1.338 +the spurious events are very rare.  They can only (I think) appear
   1.339 +when the race described in FAQ-question-3 takes place.  This should be
   1.340 +very rare.  Most(?)  uses will involve only timer expiration, or only
   1.341 +Signal/Broadcast() actions.  When both are used, it will be rare that
   1.342 +the race will appear, and it would require MANY Wait() and signaling
   1.343 +activities.  If this implementation did not recycle events, then it
   1.344 +would have to create and destroy events for every call to Wait().
   1.345 +That allocation/deallocation and associated construction/destruction
   1.346 +would be costly (per wait), and would only be a rare benefit (when the
   1.347 +race was "lost" and a spurious signal took place). That would be bad
   1.348 +(IMO) optimization trade-off.  Finally, such spurious events are
   1.349 +allowed by the specification of condition variables (such as
   1.350 +implemented in Vista), and hence it is better if any user accommodates
   1.351 +such spurious events (see usage note in condition_variable.h).
   1.352 +
   1.353 +5) Why don't you reset events when you are about to recycle them, or
   1.354 +about to reuse them, so that the spurious signals don't take place?
   1.355 +The thread described in FAQ-question-3 step c may be pre-empted for an
   1.356 +arbitrary length of time before proceeding to step d.  As a result,
   1.357 +the wait_event may actually be re-used *before* step (e) is reached.
   1.358 +As a result, calling reset would not help significantly.
   1.359 +
   1.360 +6) How is it that the callers lock is released atomically with the
   1.361 +entry into a wait state?  We commit to the wait activity when we
   1.362 +allocate the wait_event for use in a given call to Wait().  This
   1.363 +allocation takes place before the caller's lock is released (and
   1.364 +actually before our internal_lock_ is released).  That allocation is
   1.365 +the defining moment when "the wait state has been entered," as that
   1.366 +thread *can* now be signaled by a call to Broadcast() or Signal().
   1.367 +Hence we actually "commit to wait" before releasing the lock, making
   1.368 +the pair effectively atomic.
   1.369 +
   1.370 +8) Why do you need to lock your data structures during waiting, as the
   1.371 +caller is already in possession of a lock?  We need to Acquire() and
   1.372 +Release() our internal lock during Signal() and Broadcast().  If we tried
   1.373 +to use a callers lock for this purpose, we might conflict with their
   1.374 +external use of the lock.  For example, the caller may use to consistently
   1.375 +hold a lock on one thread while calling Signal() on another, and that would
   1.376 +block Signal().
   1.377 +
   1.378 +9) Couldn't a more efficient implementation be provided if you
   1.379 +preclude using more than one external lock in conjunction with a
   1.380 +single ConditionVariable instance?  Yes, at least it could be viewed
   1.381 +as a simpler API (since you don't have to reiterate the lock argument
   1.382 +in each Wait() call).  One of the constructors now takes a specific
   1.383 +lock as an argument, and a there are corresponding Wait() calls that
   1.384 +don't specify a lock now.  It turns that the resulting implmentation
   1.385 +can't be made more efficient, as the internal lock needs to be used by
   1.386 +Signal() and Broadcast(), to access internal data structures.  As a
   1.387 +result, I was not able to utilize the user supplied lock (which is
   1.388 +being used by the user elsewhere presumably) to protect the private
   1.389 +member access.
   1.390 +
   1.391 +9) Since you have a second lock, how can be be sure that there is no
   1.392 +possible deadlock scenario?  Our internal_lock_ is always the last
   1.393 +lock acquired, and the first one released, and hence a deadlock (due
   1.394 +to critical section problems) is impossible as a consequence of our
   1.395 +lock.
   1.396 +
   1.397 +10) When doing a Broadcast(), why did you copy all the events into
   1.398 +an STL queue, rather than making a linked-loop, and iterating over it?
   1.399 +The iterating during Broadcast() is done so outside the protection
   1.400 +of the internal lock. As a result, other threads, such as the thread
   1.401 +wherein a related event is waiting, could asynchronously manipulate
   1.402 +the links around a cv_event.  As a result, the link structure cannot
   1.403 +be used outside a lock.  Broadcast() could iterate over waiting
   1.404 +events by cycling in-and-out of the protection of the internal_lock,
   1.405 +but that appears more expensive than copying the list into an STL
   1.406 +stack.
   1.407 +
   1.408 +11) Why did the lock.h file need to be modified so much for this
   1.409 +change?  Central to a Condition Variable is the atomic release of a
   1.410 +lock during a Wait().  This places Wait() functionality exactly
   1.411 +mid-way between the two classes, Lock and Condition Variable.  Given
   1.412 +that there can be nested Acquire()'s of locks, and Wait() had to
   1.413 +Release() completely a held lock, it was necessary to augment the Lock
   1.414 +class with a recursion counter. Even more subtle is the fact that the
   1.415 +recursion counter (in a Lock) must be protected, as many threads can
   1.416 +access it asynchronously.  As a positive fallout of this, there are
   1.417 +now some DCHECKS to be sure no one Release()s a Lock more than they
   1.418 +Acquire()ed it, and there is ifdef'ed functionality that can detect
   1.419 +nested locks (legal under windows, but not under Posix).
   1.420 +
   1.421 +12) Why is it that the cv_events removed from list in Broadcast() and Signal()
   1.422 +are not leaked?  How are they recovered??  The cv_events that appear to leak are
   1.423 +taken from the waiting_list_.  For each element in that list, there is currently
   1.424 +a thread in or around the WaitForSingleObject() call of Wait(), and those
   1.425 +threads have references to these otherwise leaked events. They are passed as
   1.426 +arguments to be recycled just aftre returning from WaitForSingleObject().
   1.427 +
   1.428 +13) Why did you use a custom container class (the linked list), when STL has
   1.429 +perfectly good containers, such as an STL list?  The STL list, as with any
   1.430 +container, does not guarantee the utility of an iterator across manipulation
   1.431 +(such as insertions and deletions) of the underlying container.  The custom
   1.432 +double-linked-list container provided that assurance.  I don't believe any
   1.433 +combination of STL containers provided the services that were needed at the same
   1.434 +O(1) efficiency as the custom linked list.  The unusual requirement
   1.435 +for the container class is that a reference to an item within a container (an
   1.436 +iterator) needed to be maintained across an arbitrary manipulation of the
   1.437 +container.  This requirement exposes itself in the Wait() method, where a
   1.438 +waiting_event must be selected prior to the WaitForSingleObject(), and then it
   1.439 +must be used as part of recycling to remove the related instance from the
   1.440 +waiting_list.  A hash table (STL map) could be used, but I was embarrased to
   1.441 +use a complex and relatively low efficiency container when a doubly linked list
   1.442 +provided O(1) performance in all required operations.  Since other operations
   1.443 +to provide performance-and/or-fairness required queue (FIFO) and list (LIFO)
   1.444 +containers, I would also have needed to use an STL list/queue as well as an STL
   1.445 +map.  In the end I decided it would be "fun" to just do it right, and I
   1.446 +put so many assertions (DCHECKs) into the container class that it is trivial to
   1.447 +code review and validate its correctness.
   1.448 +
   1.449 +*/

mercurial