spinlock.cc 9.3 KB

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