• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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