Home | History | Annotate | Download | only in base
      1 /* Copyright (c) 2006, Google Inc.
      2  * All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  *
     30  * ---
     31  * Author: Sanjay Ghemawat
     32  */
     33 
     34 //
     35 // Fast spinlocks (at least on x86, a lock/unlock pair is approximately
     36 // half the cost of a Mutex because the unlock just does a store instead
     37 // of a compare-and-swap which is expensive).
     38 
     39 // SpinLock is async signal safe.
     40 // If used within a signal handler, all lock holders
     41 // should block the signal even outside the signal handler.
     42 
     43 #ifndef BASE_SPINLOCK_H_
     44 #define BASE_SPINLOCK_H_
     45 
     46 #include <config.h>
     47 #include "base/atomicops.h"
     48 #include "base/basictypes.h"
     49 #include "base/dynamic_annotations.h"
     50 #include "base/thread_annotations.h"
     51 
     52 class LOCKABLE SpinLock {
     53  public:
     54   SpinLock() : lockword_(kSpinLockFree) { }
     55 
     56   // Special constructor for use with static SpinLock objects.  E.g.,
     57   //
     58   //    static SpinLock lock(base::LINKER_INITIALIZED);
     59   //
     60   // When intialized using this constructor, we depend on the fact
     61   // that the linker has already initialized the memory appropriately.
     62   // A SpinLock constructed like this can be freely used from global
     63   // initializers without worrying about the order in which global
     64   // initializers run.
     65   explicit SpinLock(base::LinkerInitialized /*x*/) {
     66     // Does nothing; lockword_ is already initialized
     67   }
     68 
     69   // Acquire this SpinLock.
     70   // TODO(csilvers): uncomment the annotation when we figure out how to
     71   //                 support this macro with 0 args (see thread_annotations.h)
     72   inline void Lock() /*EXCLUSIVE_LOCK_FUNCTION()*/ {
     73     if (base::subtle::Acquire_CompareAndSwap(&lockword_, kSpinLockFree,
     74                                              kSpinLockHeld) != kSpinLockFree) {
     75       SlowLock();
     76     }
     77     ANNOTATE_RWLOCK_ACQUIRED(this, 1);
     78   }
     79 
     80   // Try to acquire this SpinLock without blocking and return true if the
     81   // acquisition was successful.  If the lock was not acquired, false is
     82   // returned.  If this SpinLock is free at the time of the call, TryLock
     83   // will return true with high probability.
     84   inline bool TryLock() EXCLUSIVE_TRYLOCK_FUNCTION(true) {
     85     bool res =
     86         (base::subtle::Acquire_CompareAndSwap(&lockword_, kSpinLockFree,
     87                                               kSpinLockHeld) == kSpinLockFree);
     88     if (res) {
     89       ANNOTATE_RWLOCK_ACQUIRED(this, 1);
     90     }
     91     return res;
     92   }
     93 
     94   // Release this SpinLock, which must be held by the calling thread.
     95   // TODO(csilvers): uncomment the annotation when we figure out how to
     96   //                 support this macro with 0 args (see thread_annotations.h)
     97   inline void Unlock() /*UNLOCK_FUNCTION()*/ {
     98     uint64 wait_cycles =
     99         static_cast<uint64>(base::subtle::NoBarrier_Load(&lockword_));
    100     ANNOTATE_RWLOCK_RELEASED(this, 1);
    101     base::subtle::Release_Store(&lockword_, kSpinLockFree);
    102     if (wait_cycles != kSpinLockHeld) {
    103       // Collect contentionz profile info, and speed the wakeup of any waiter.
    104       // The wait_cycles value indicates how long this thread spent waiting
    105       // for the lock.
    106       SlowUnlock(wait_cycles);
    107     }
    108   }
    109 
    110   // Determine if the lock is held.  When the lock is held by the invoking
    111   // thread, true will always be returned. Intended to be used as
    112   // CHECK(lock.IsHeld()).
    113   inline bool IsHeld() const {
    114     return base::subtle::NoBarrier_Load(&lockword_) != kSpinLockFree;
    115   }
    116 
    117   static const base::LinkerInitialized LINKER_INITIALIZED;  // backwards compat
    118  private:
    119   enum { kSpinLockFree = 0 };
    120   enum { kSpinLockHeld = 1 };
    121   enum { kSpinLockSleeper = 2 };
    122 
    123   volatile Atomic32 lockword_;
    124 
    125   void SlowLock();
    126   void SlowUnlock(uint64 wait_cycles);
    127   Atomic32 SpinLoop(int64 initial_wait_timestamp, Atomic32* wait_cycles);
    128   inline int32 CalculateWaitCycles(int64 wait_start_time);
    129 
    130   DISALLOW_COPY_AND_ASSIGN(SpinLock);
    131 };
    132 
    133 // Corresponding locker object that arranges to acquire a spinlock for
    134 // the duration of a C++ scope.
    135 class SCOPED_LOCKABLE SpinLockHolder {
    136  private:
    137   SpinLock* lock_;
    138  public:
    139   inline explicit SpinLockHolder(SpinLock* l) EXCLUSIVE_LOCK_FUNCTION(l)
    140       : lock_(l) {
    141     l->Lock();
    142   }
    143   // TODO(csilvers): uncomment the annotation when we figure out how to
    144   //                 support this macro with 0 args (see thread_annotations.h)
    145   inline ~SpinLockHolder() /*UNLOCK_FUNCTION()*/ { lock_->Unlock(); }
    146 };
    147 // Catch bug where variable name is omitted, e.g. SpinLockHolder (&lock);
    148 #define SpinLockHolder(x) COMPILE_ASSERT(0, spin_lock_decl_missing_var_name)
    149 
    150 
    151 #endif  // BASE_SPINLOCK_H_
    152