1 // Copyright 2011 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef BASE_SYNCHRONIZATION_LOCK_IMPL_H_
6 #define BASE_SYNCHRONIZATION_LOCK_IMPL_H_
7
8 #include <utility>
9
10 #include "base/base_export.h"
11 #include "base/check.h"
12 #include "base/dcheck_is_on.h"
13 #include "base/memory/raw_ptr_exclusion.h"
14 #include "base/memory/stack_allocated.h"
15 #include "base/synchronization/lock_subtle.h"
16 #include "base/thread_annotations.h"
17 #include "build/build_config.h"
18
19 #if BUILDFLAG(IS_WIN)
20 #include "base/win/windows_types.h"
21 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
22 #include <errno.h>
23 #include <pthread.h>
24 #include <string.h>
25 #endif
26
27 namespace base {
28 class Lock;
29 class ConditionVariable;
30
31 namespace win {
32 namespace internal {
33 class AutoNativeLock;
34 class ScopedHandleVerifier;
35 } // namespace internal
36 } // namespace win
37
38 namespace internal {
39
40 // This class implements the underlying platform-specific spin-lock mechanism
41 // used for the Lock class. Do not use, use Lock instead.
42 class BASE_EXPORT LockImpl {
43 public:
44 LockImpl(const LockImpl&) = delete;
45 LockImpl& operator=(const LockImpl&) = delete;
46
47 private:
48 friend class base::Lock;
49 friend class base::ConditionVariable;
50 friend class base::win::internal::AutoNativeLock;
51 friend class base::win::internal::ScopedHandleVerifier;
52
53 #if BUILDFLAG(IS_WIN)
54 using NativeHandle = CHROME_SRWLOCK;
55 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
56 using NativeHandle = pthread_mutex_t;
57 #endif
58
59 LockImpl();
60 ~LockImpl();
61
62 // If the lock is not held, take it and return true. If the lock is already
63 // held by something else, immediately return false.
64 inline bool Try();
65
66 // Take the lock, blocking until it is available if necessary.
67 inline void Lock();
68
69 // Release the lock. This must only be called by the lock's holder: after
70 // a successful call to Try, or a call to Lock.
71 inline void Unlock();
72
73 // Return the native underlying lock.
74 // TODO(awalker): refactor lock and condition variables so that this is
75 // unnecessary.
native_handle()76 NativeHandle* native_handle() { return &native_handle_; }
77
78 #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
79 // Whether this lock will attempt to use priority inheritance.
80 static bool PriorityInheritanceAvailable();
81 #endif
82
83 void LockInternal();
84 NativeHandle native_handle_;
85 };
86
Lock()87 void LockImpl::Lock() {
88 // Try the lock first to acquire it cheaply if it's not contended. Try() is
89 // cheap on platforms with futex-type locks, as it doesn't call into the
90 // kernel. Not marked `[[likely]]`, as:
91 // 1. We don't know how much contention the lock would experience
92 // 2. This may lead to weird-looking code layout when inlined into a caller
93 // with `[[(un)likely]]` attributes.
94 if (Try()) {
95 return;
96 }
97
98 LockInternal();
99 }
100
101 #if BUILDFLAG(IS_WIN)
Try()102 bool LockImpl::Try() {
103 return !!::TryAcquireSRWLockExclusive(
104 reinterpret_cast<PSRWLOCK>(&native_handle_));
105 }
106
Unlock()107 void LockImpl::Unlock() {
108 ::ReleaseSRWLockExclusive(reinterpret_cast<PSRWLOCK>(&native_handle_));
109 }
110
111 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
112
113 #if DCHECK_IS_ON()
114 BASE_EXPORT void dcheck_trylock_result(int rv);
115 BASE_EXPORT void dcheck_unlock_result(int rv);
116 #endif
117
Try()118 bool LockImpl::Try() {
119 int rv = pthread_mutex_trylock(&native_handle_);
120 #if DCHECK_IS_ON()
121 dcheck_trylock_result(rv);
122 #endif
123 return rv == 0;
124 }
125
Unlock()126 void LockImpl::Unlock() {
127 [[maybe_unused]] int rv = pthread_mutex_unlock(&native_handle_);
128 #if DCHECK_IS_ON()
129 dcheck_unlock_result(rv);
130 #endif
131 }
132 #endif
133
134 // This is an implementation used for AutoLock templated on the lock type.
135 template <class LockType>
136 class SCOPED_LOCKABLE BasicAutoLock {
137 STACK_ALLOCATED();
138
139 public:
140 struct AlreadyAcquired {};
141
142 explicit BasicAutoLock(
143 LockType& lock,
144 subtle::LockTracking tracking = subtle::LockTracking::kDisabled)
EXCLUSIVE_LOCK_FUNCTION(lock)145 EXCLUSIVE_LOCK_FUNCTION(lock)
146 : lock_(lock) {
147 lock_.Acquire(tracking);
148 }
149
BasicAutoLock(LockType & lock,const AlreadyAcquired &)150 BasicAutoLock(LockType& lock, const AlreadyAcquired&)
151 EXCLUSIVE_LOCKS_REQUIRED(lock)
152 : lock_(lock) {
153 lock_.AssertAcquired();
154 }
155
156 BasicAutoLock(const BasicAutoLock&) = delete;
157 BasicAutoLock& operator=(const BasicAutoLock&) = delete;
158
UNLOCK_FUNCTION()159 ~BasicAutoLock() UNLOCK_FUNCTION() {
160 lock_.AssertAcquired();
161 lock_.Release();
162 }
163
164 private:
165 LockType& lock_;
166 };
167
168 // This is an implementation used for MovableAutoLock templated on the lock
169 // type.
170 template <class LockType>
171 class SCOPED_LOCKABLE BasicMovableAutoLock {
172 public:
173 explicit BasicMovableAutoLock(
174 LockType& lock,
175 subtle::LockTracking tracking = subtle::LockTracking::kDisabled)
EXCLUSIVE_LOCK_FUNCTION(lock)176 EXCLUSIVE_LOCK_FUNCTION(lock)
177 : lock_(&lock) {
178 lock_->Acquire(tracking);
179 }
180
181 BasicMovableAutoLock(const BasicMovableAutoLock&) = delete;
182 BasicMovableAutoLock& operator=(const BasicMovableAutoLock&) = delete;
BasicMovableAutoLock(BasicMovableAutoLock && other)183 BasicMovableAutoLock(BasicMovableAutoLock&& other) :
184 lock_(std::exchange(other.lock_, nullptr)) {}
185 BasicMovableAutoLock& operator=(BasicMovableAutoLock&& other) = delete;
186
UNLOCK_FUNCTION()187 ~BasicMovableAutoLock() UNLOCK_FUNCTION() {
188 // The lock may have been moved out.
189 if (lock_) {
190 lock_->AssertAcquired();
191 lock_->Release();
192 }
193 }
194
195 private:
196 // RAW_PTR_EXCLUSION: Stack-scoped.
197 RAW_PTR_EXCLUSION LockType* lock_;
198 };
199
200 // This is an implementation used for AutoTryLock templated on the lock type.
201 template <class LockType>
202 class SCOPED_LOCKABLE BasicAutoTryLock {
203 STACK_ALLOCATED();
204
205 public:
206 // The `LOCKS_EXCLUDED(lock)` annotation requires that the caller has not
207 // acquired `lock`. Without the annotation, Clang's Thread Safety Analysis
208 // would generate a false positive despite correct usage. For instance, a
209 // caller that checks `is_acquired()` before writing to guarded data would be
210 // flagged with "writing variable 'foo' requires holding 'lock' exclusively."
211 // See <https://crbug.com/340196356>.
212 explicit BasicAutoTryLock(
213 LockType& lock,
214 subtle::LockTracking tracking = subtle::LockTracking::kDisabled)
LOCKS_EXCLUDED(lock)215 LOCKS_EXCLUDED(lock)
216 : lock_(lock), is_acquired_(lock_.Try(tracking)) {}
217
218 BasicAutoTryLock(const BasicAutoTryLock&) = delete;
219 BasicAutoTryLock& operator=(const BasicAutoTryLock&) = delete;
220
UNLOCK_FUNCTION()221 ~BasicAutoTryLock() UNLOCK_FUNCTION() {
222 if (is_acquired_) {
223 lock_.AssertAcquired();
224 lock_.Release();
225 }
226 }
227
is_acquired()228 bool is_acquired() const EXCLUSIVE_TRYLOCK_FUNCTION(true) {
229 return is_acquired_;
230 }
231
232 private:
233 LockType& lock_;
234 const bool is_acquired_;
235 };
236
237 // This is an implementation used for AutoUnlock templated on the lock type.
238 template <class LockType>
239 class BasicAutoUnlock {
240 STACK_ALLOCATED();
241
242 public:
BasicAutoUnlock(LockType & lock)243 explicit BasicAutoUnlock(LockType& lock) : lock_(lock) {
244 // We require our caller to have the lock.
245 lock_.AssertAcquired();
246 lock_.Release();
247 }
248
249 BasicAutoUnlock(const BasicAutoUnlock&) = delete;
250 BasicAutoUnlock& operator=(const BasicAutoUnlock&) = delete;
251
~BasicAutoUnlock()252 ~BasicAutoUnlock() { lock_.Acquire(); }
253
254 private:
255 LockType& lock_;
256 };
257
258 // This is an implementation used for AutoLockMaybe templated on the lock type.
259 template <class LockType>
260 class SCOPED_LOCKABLE BasicAutoLockMaybe {
261 STACK_ALLOCATED();
262
263 public:
264 explicit BasicAutoLockMaybe(
265 LockType* lock,
266 subtle::LockTracking tracking = subtle::LockTracking::kDisabled)
EXCLUSIVE_LOCK_FUNCTION(lock)267 EXCLUSIVE_LOCK_FUNCTION(lock)
268 : lock_(lock) {
269 if (lock_)
270 lock_->Acquire(tracking);
271 }
272
273 BasicAutoLockMaybe(const BasicAutoLockMaybe&) = delete;
274 BasicAutoLockMaybe& operator=(const BasicAutoLockMaybe&) = delete;
275
UNLOCK_FUNCTION()276 ~BasicAutoLockMaybe() UNLOCK_FUNCTION() {
277 if (lock_) {
278 lock_->AssertAcquired();
279 lock_->Release();
280 }
281 }
282
283 private:
284 LockType* const lock_;
285 };
286
287 // This is an implementation used for ReleasableAutoLock templated on the lock
288 // type.
289 template <class LockType>
290 class SCOPED_LOCKABLE BasicReleasableAutoLock {
291 STACK_ALLOCATED();
292
293 public:
294 explicit BasicReleasableAutoLock(
295 LockType* lock,
296 subtle::LockTracking tracking = subtle::LockTracking::kDisabled)
EXCLUSIVE_LOCK_FUNCTION(lock)297 EXCLUSIVE_LOCK_FUNCTION(lock)
298 : lock_(lock) {
299 DCHECK(lock_);
300 lock_->Acquire(tracking);
301 }
302
303 BasicReleasableAutoLock(const BasicReleasableAutoLock&) = delete;
304 BasicReleasableAutoLock& operator=(const BasicReleasableAutoLock&) = delete;
305
UNLOCK_FUNCTION()306 ~BasicReleasableAutoLock() UNLOCK_FUNCTION() {
307 if (lock_) {
308 lock_->AssertAcquired();
309 lock_->Release();
310 }
311 }
312
Release()313 void Release() UNLOCK_FUNCTION() {
314 DCHECK(lock_);
315 lock_->AssertAcquired();
316 lock_->Release();
317 lock_ = nullptr;
318 }
319
320 private:
321 LockType* lock_;
322 };
323
324 } // namespace internal
325 } // namespace base
326
327 #endif // BASE_SYNCHRONIZATION_LOCK_IMPL_H_
328