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