spinlock.cc (9202B)
1 // Copyright 2017 The Abseil Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "absl/base/internal/spinlock.h" 16 17 #include <algorithm> 18 #include <atomic> 19 #include <limits> 20 21 #include "absl/base/attributes.h" 22 #include "absl/base/config.h" 23 #include "absl/base/internal/atomic_hook.h" 24 #include "absl/base/internal/cycleclock.h" 25 #include "absl/base/internal/spinlock_wait.h" 26 #include "absl/base/internal/sysinfo.h" /* For NumCPUs() */ 27 #include "absl/base/call_once.h" 28 29 // Description of lock-word: 30 // 31..00: [............................3][2][1][0] 31 // 32 // [0]: kSpinLockHeld 33 // [1]: kSpinLockCooperative 34 // [2]: kSpinLockDisabledScheduling 35 // [31..3]: ONLY kSpinLockSleeper OR 36 // Wait time in cycles >> PROFILE_TIMESTAMP_SHIFT 37 // 38 // Detailed descriptions: 39 // 40 // Bit [0]: The lock is considered held iff kSpinLockHeld is set. 41 // 42 // Bit [1]: Eligible waiters (e.g. Fibers) may co-operatively reschedule when 43 // contended iff kSpinLockCooperative is set. 44 // 45 // Bit [2]: This bit is exclusive from bit [1]. It is used only by a 46 // non-cooperative lock. When set, indicates that scheduling was 47 // successfully disabled when the lock was acquired. May be unset, 48 // even if non-cooperative, if a ThreadIdentity did not yet exist at 49 // time of acquisition. 50 // 51 // Bit [3]: If this is the only upper bit ([31..3]) set then this lock was 52 // acquired without contention, however, at least one waiter exists. 53 // 54 // Otherwise, bits [31..3] represent the time spent by the current lock 55 // holder to acquire the lock. There may be outstanding waiter(s). 56 57 namespace absl { 58 ABSL_NAMESPACE_BEGIN 59 namespace base_internal { 60 61 ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES static base_internal::AtomicHook<void (*)( 62 const void *lock, int64_t wait_cycles)> 63 submit_profile_data; 64 65 void RegisterSpinLockProfiler(void (*fn)(const void *contendedlock, 66 int64_t wait_cycles)) { 67 submit_profile_data.Store(fn); 68 } 69 70 // Uncommon constructors. 71 SpinLock::SpinLock(base_internal::SchedulingMode mode) 72 : lockword_(IsCooperative(mode) ? kSpinLockCooperative : 0) { 73 ABSL_TSAN_MUTEX_CREATE(this, __tsan_mutex_not_static); 74 } 75 76 // Monitor the lock to see if its value changes within some time period 77 // (adaptive_spin_count loop iterations). The last value read from the lock 78 // is returned from the method. 79 uint32_t SpinLock::SpinLoop() { 80 // We are already in the slow path of SpinLock, initialize the 81 // adaptive_spin_count here. 82 ABSL_CONST_INIT static absl::once_flag init_adaptive_spin_count; 83 ABSL_CONST_INIT static int adaptive_spin_count = 0; 84 base_internal::LowLevelCallOnce(&init_adaptive_spin_count, []() { 85 adaptive_spin_count = base_internal::NumCPUs() > 1 ? 1000 : 1; 86 }); 87 88 int c = adaptive_spin_count; 89 uint32_t lock_value; 90 do { 91 lock_value = lockword_.load(std::memory_order_relaxed); 92 } while ((lock_value & kSpinLockHeld) != 0 && --c > 0); 93 return lock_value; 94 } 95 96 void SpinLock::SlowLock() { 97 uint32_t lock_value = SpinLoop(); 98 lock_value = TryLockInternal(lock_value, 0); 99 if ((lock_value & kSpinLockHeld) == 0) { 100 return; 101 } 102 103 base_internal::SchedulingMode scheduling_mode; 104 if ((lock_value & kSpinLockCooperative) != 0) { 105 scheduling_mode = base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL; 106 } else { 107 scheduling_mode = base_internal::SCHEDULE_KERNEL_ONLY; 108 } 109 110 // The lock was not obtained initially, so this thread needs to wait for 111 // it. Record the current timestamp in the local variable wait_start_time 112 // so the total wait time can be stored in the lockword once this thread 113 // obtains the lock. 114 int64_t wait_start_time = CycleClock::Now(); 115 uint32_t wait_cycles = 0; 116 int lock_wait_call_count = 0; 117 while ((lock_value & kSpinLockHeld) != 0) { 118 // If the lock is currently held, but not marked as having a sleeper, mark 119 // it as having a sleeper. 120 if ((lock_value & kWaitTimeMask) == 0) { 121 // Here, just "mark" that the thread is going to sleep. Don't store the 122 // lock wait time in the lock -- the lock word stores the amount of time 123 // that the current holder waited before acquiring the lock, not the wait 124 // time of any thread currently waiting to acquire it. 125 if (lockword_.compare_exchange_strong( 126 lock_value, lock_value | kSpinLockSleeper, 127 std::memory_order_relaxed, std::memory_order_relaxed)) { 128 // Successfully transitioned to kSpinLockSleeper. Pass 129 // kSpinLockSleeper to the SpinLockWait routine to properly indicate 130 // the last lock_value observed. 131 lock_value |= kSpinLockSleeper; 132 } else if ((lock_value & kSpinLockHeld) == 0) { 133 // Lock is free again, so try and acquire it before sleeping. The 134 // new lock state will be the number of cycles this thread waited if 135 // this thread obtains the lock. 136 lock_value = TryLockInternal(lock_value, wait_cycles); 137 continue; // Skip the delay at the end of the loop. 138 } else if ((lock_value & kWaitTimeMask) == 0) { 139 // The lock is still held, without a waiter being marked, but something 140 // else about the lock word changed, causing our CAS to fail. For 141 // example, a new lock holder may have acquired the lock with 142 // kSpinLockDisabledScheduling set, whereas the previous holder had not 143 // set that flag. In this case, attempt again to mark ourselves as a 144 // waiter. 145 continue; 146 } 147 } 148 149 // SpinLockDelay() calls into fiber scheduler, we need to see 150 // synchronization there to avoid false positives. 151 ABSL_TSAN_MUTEX_PRE_DIVERT(this, 0); 152 // Wait for an OS specific delay. 153 base_internal::SpinLockDelay(&lockword_, lock_value, ++lock_wait_call_count, 154 scheduling_mode); 155 ABSL_TSAN_MUTEX_POST_DIVERT(this, 0); 156 // Spin again after returning from the wait routine to give this thread 157 // some chance of obtaining the lock. 158 lock_value = SpinLoop(); 159 wait_cycles = EncodeWaitCycles(wait_start_time, CycleClock::Now()); 160 lock_value = TryLockInternal(lock_value, wait_cycles); 161 } 162 } 163 164 void SpinLock::SlowUnlock(uint32_t lock_value) { 165 base_internal::SpinLockWake(&lockword_, 166 false); // wake waiter if necessary 167 168 // If our acquisition was contended, collect contentionz profile info. We 169 // reserve a unitary wait time to represent that a waiter exists without our 170 // own acquisition having been contended. 171 if ((lock_value & kWaitTimeMask) != kSpinLockSleeper) { 172 const int64_t wait_cycles = DecodeWaitCycles(lock_value); 173 ABSL_TSAN_MUTEX_PRE_DIVERT(this, 0); 174 submit_profile_data(this, wait_cycles); 175 ABSL_TSAN_MUTEX_POST_DIVERT(this, 0); 176 } 177 } 178 179 // We use the upper 29 bits of the lock word to store the time spent waiting to 180 // acquire this lock. This is reported by contentionz profiling. Since the 181 // lower bits of the cycle counter wrap very quickly on high-frequency 182 // processors we divide to reduce the granularity to 2^kProfileTimestampShift 183 // sized units. On a 4Ghz machine this will lose track of wait times greater 184 // than (2^29/4 Ghz)*128 =~ 17.2 seconds. Such waits should be extremely rare. 185 static constexpr int kProfileTimestampShift = 7; 186 187 // We currently reserve the lower 3 bits. 188 static constexpr int kLockwordReservedShift = 3; 189 190 uint32_t SpinLock::EncodeWaitCycles(int64_t wait_start_time, 191 int64_t wait_end_time) { 192 static const int64_t kMaxWaitTime = 193 std::numeric_limits<uint32_t>::max() >> kLockwordReservedShift; 194 int64_t scaled_wait_time = 195 (wait_end_time - wait_start_time) >> kProfileTimestampShift; 196 197 // Return a representation of the time spent waiting that can be stored in 198 // the lock word's upper bits. 199 uint32_t clamped = static_cast<uint32_t>( 200 std::min(scaled_wait_time, kMaxWaitTime) << kLockwordReservedShift); 201 202 if (clamped == 0) { 203 return kSpinLockSleeper; // Just wake waiters, but don't record contention. 204 } 205 // Bump up value if necessary to avoid returning kSpinLockSleeper. 206 const uint32_t kMinWaitTime = 207 kSpinLockSleeper + (1 << kLockwordReservedShift); 208 if (clamped == kSpinLockSleeper) { 209 return kMinWaitTime; 210 } 211 return clamped; 212 } 213 214 int64_t SpinLock::DecodeWaitCycles(uint32_t lock_value) { 215 // Cast to uint32_t first to ensure bits [63:32] are cleared. 216 const int64_t scaled_wait_time = 217 static_cast<uint32_t>(lock_value & kWaitTimeMask); 218 return scaled_wait_time << (kProfileTimestampShift - kLockwordReservedShift); 219 } 220 221 } // namespace base_internal 222 ABSL_NAMESPACE_END 223 } // namespace absl