Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "absl/base/internal/spinlock.h"
00016
00017 #include <algorithm>
00018 #include <atomic>
00019 #include <limits>
00020
00021 #include "absl/base/attributes.h"
00022 #include "absl/base/internal/atomic_hook.h"
00023 #include "absl/base/internal/cycleclock.h"
00024 #include "absl/base/internal/spinlock_wait.h"
00025 #include "absl/base/internal/sysinfo.h"
00026 #include "absl/base/call_once.h"
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056 namespace absl {
00057 namespace base_internal {
00058
00059 ABSL_CONST_INIT static base_internal::AtomicHook<void (*)(const void *lock,
00060 int64_t wait_cycles)>
00061 submit_profile_data;
00062
00063 void RegisterSpinLockProfiler(void (*fn)(const void *contendedlock,
00064 int64_t wait_cycles)) {
00065 submit_profile_data.Store(fn);
00066 }
00067
00068
00069 SpinLock::SpinLock(base_internal::SchedulingMode mode)
00070 : lockword_(IsCooperative(mode) ? kSpinLockCooperative : 0) {
00071 ABSL_TSAN_MUTEX_CREATE(this, __tsan_mutex_not_static);
00072 }
00073
00074 SpinLock::SpinLock(base_internal::LinkerInitialized,
00075 base_internal::SchedulingMode mode) {
00076 ABSL_TSAN_MUTEX_CREATE(this, 0);
00077 if (IsCooperative(mode)) {
00078 InitLinkerInitializedAndCooperative();
00079 }
00080
00081 }
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091 void SpinLock::InitLinkerInitializedAndCooperative() {
00092 Lock();
00093 lockword_.fetch_or(kSpinLockCooperative, std::memory_order_relaxed);
00094 Unlock();
00095 }
00096
00097
00098
00099
00100 uint32_t SpinLock::SpinLoop() {
00101
00102
00103 ABSL_CONST_INIT static absl::once_flag init_adaptive_spin_count;
00104 ABSL_CONST_INIT static int adaptive_spin_count = 0;
00105 base_internal::LowLevelCallOnce(&init_adaptive_spin_count, []() {
00106 adaptive_spin_count = base_internal::NumCPUs() > 1 ? 1000 : 1;
00107 });
00108
00109 int c = adaptive_spin_count;
00110 uint32_t lock_value;
00111 do {
00112 lock_value = lockword_.load(std::memory_order_relaxed);
00113 } while ((lock_value & kSpinLockHeld) != 0 && --c > 0);
00114 return lock_value;
00115 }
00116
00117 void SpinLock::SlowLock() {
00118 uint32_t lock_value = SpinLoop();
00119 lock_value = TryLockInternal(lock_value, 0);
00120 if ((lock_value & kSpinLockHeld) == 0) {
00121 return;
00122 }
00123
00124
00125
00126
00127 int64_t wait_start_time = CycleClock::Now();
00128 uint32_t wait_cycles = 0;
00129 int lock_wait_call_count = 0;
00130 while ((lock_value & kSpinLockHeld) != 0) {
00131
00132
00133 if ((lock_value & kWaitTimeMask) == 0) {
00134
00135
00136
00137 if (lockword_.compare_exchange_strong(
00138 lock_value, lock_value | kSpinLockSleeper,
00139 std::memory_order_relaxed, std::memory_order_relaxed)) {
00140
00141
00142
00143 lock_value |= kSpinLockSleeper;
00144 } else if ((lock_value & kSpinLockHeld) == 0) {
00145
00146
00147
00148 lock_value = TryLockInternal(lock_value, wait_cycles);
00149 continue;
00150 }
00151 }
00152
00153 base_internal::SchedulingMode scheduling_mode;
00154 if ((lock_value & kSpinLockCooperative) != 0) {
00155 scheduling_mode = base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL;
00156 } else {
00157 scheduling_mode = base_internal::SCHEDULE_KERNEL_ONLY;
00158 }
00159
00160
00161 ABSL_TSAN_MUTEX_PRE_DIVERT(this, 0);
00162
00163 base_internal::SpinLockDelay(&lockword_, lock_value, ++lock_wait_call_count,
00164 scheduling_mode);
00165 ABSL_TSAN_MUTEX_POST_DIVERT(this, 0);
00166
00167
00168 lock_value = SpinLoop();
00169 wait_cycles = EncodeWaitCycles(wait_start_time, CycleClock::Now());
00170 lock_value = TryLockInternal(lock_value, wait_cycles);
00171 }
00172 }
00173
00174 void SpinLock::SlowUnlock(uint32_t lock_value) {
00175 base_internal::SpinLockWake(&lockword_,
00176 false);
00177
00178
00179
00180
00181 if ((lock_value & kWaitTimeMask) != kSpinLockSleeper) {
00182 const uint64_t wait_cycles = DecodeWaitCycles(lock_value);
00183 ABSL_TSAN_MUTEX_PRE_DIVERT(this, 0);
00184 submit_profile_data(this, wait_cycles);
00185 ABSL_TSAN_MUTEX_POST_DIVERT(this, 0);
00186 }
00187 }
00188
00189
00190
00191
00192
00193
00194
00195 enum { PROFILE_TIMESTAMP_SHIFT = 7 };
00196 enum { LOCKWORD_RESERVED_SHIFT = 3 };
00197
00198 uint32_t SpinLock::EncodeWaitCycles(int64_t wait_start_time,
00199 int64_t wait_end_time) {
00200 static const int64_t kMaxWaitTime =
00201 std::numeric_limits<uint32_t>::max() >> LOCKWORD_RESERVED_SHIFT;
00202 int64_t scaled_wait_time =
00203 (wait_end_time - wait_start_time) >> PROFILE_TIMESTAMP_SHIFT;
00204
00205
00206
00207 uint32_t clamped = static_cast<uint32_t>(
00208 std::min(scaled_wait_time, kMaxWaitTime) << LOCKWORD_RESERVED_SHIFT);
00209
00210 if (clamped == 0) {
00211 return kSpinLockSleeper;
00212 }
00213
00214 const uint32_t kMinWaitTime =
00215 kSpinLockSleeper + (1 << LOCKWORD_RESERVED_SHIFT);
00216 if (clamped == kSpinLockSleeper) {
00217 return kMinWaitTime;
00218 }
00219 return clamped;
00220 }
00221
00222 uint64_t SpinLock::DecodeWaitCycles(uint32_t lock_value) {
00223
00224 const uint64_t scaled_wait_time =
00225 static_cast<uint32_t>(lock_value & kWaitTimeMask);
00226 return scaled_wait_time
00227 << (PROFILE_TIMESTAMP_SHIFT - LOCKWORD_RESERVED_SHIFT);
00228 }
00229
00230 }
00231 }