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