xref: /aosp_15_r20/external/cronet/base/synchronization/lock_impl.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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 "base/base_export.h"
9 #include "base/check.h"
10 #include "base/dcheck_is_on.h"
11 #include "base/memory/raw_ptr_exclusion.h"
12 #include "base/memory/stack_allocated.h"
13 #include "base/thread_annotations.h"
14 #include "build/build_config.h"
15 
16 #if BUILDFLAG(IS_WIN)
17 #include "base/win/windows_types.h"
18 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
19 #include <errno.h>
20 #include <pthread.h>
21 #include <string.h>
22 #endif
23 
24 namespace base {
25 class Lock;
26 class ConditionVariable;
27 
28 namespace win {
29 namespace internal {
30 class AutoNativeLock;
31 class ScopedHandleVerifier;
32 }  // namespace internal
33 }  // namespace win
34 
35 namespace internal {
36 
37 // This class implements the underlying platform-specific spin-lock mechanism
38 // used for the Lock class. Do not use, use Lock instead.
39 class BASE_EXPORT LockImpl {
40  public:
41   LockImpl(const LockImpl&) = delete;
42   LockImpl& operator=(const LockImpl&) = delete;
43 
44  private:
45   friend class base::Lock;
46   friend class base::ConditionVariable;
47   friend class base::win::internal::AutoNativeLock;
48   friend class base::win::internal::ScopedHandleVerifier;
49 
50 #if BUILDFLAG(IS_WIN)
51   using NativeHandle = CHROME_SRWLOCK;
52 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
53   using NativeHandle = pthread_mutex_t;
54 #endif
55 
56   LockImpl();
57   ~LockImpl();
58 
59   // If the lock is not held, take it and return true.  If the lock is already
60   // held by something else, immediately return false.
61   inline bool Try();
62 
63   // Take the lock, blocking until it is available if necessary.
64   inline void Lock();
65 
66   // Release the lock.  This must only be called by the lock's holder: after
67   // a successful call to Try, or a call to Lock.
68   inline void Unlock();
69 
70   // Return the native underlying lock.
71   // TODO(awalker): refactor lock and condition variables so that this is
72   // unnecessary.
native_handle()73   NativeHandle* native_handle() { return &native_handle_; }
74 
75 #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
76   // Whether this lock will attempt to use priority inheritance.
77   static bool PriorityInheritanceAvailable();
78 #endif
79 
80   void LockInternal();
81   NativeHandle native_handle_;
82 };
83 
Lock()84 void LockImpl::Lock() {
85   // Try the lock first to acquire it cheaply if it's not contended. Try() is
86   // cheap on platforms with futex-type locks, as it doesn't call into the
87   // kernel. Not marked LIKELY(), as:
88   // 1. We don't know how much contention the lock would experience
89   // 2. This may lead to weird-looking code layout when inlined into a caller
90   // with (UN)LIKELY() annotations.
91   if (Try()) {
92     return;
93   }
94 
95   LockInternal();
96 }
97 
98 #if BUILDFLAG(IS_WIN)
Try()99 bool LockImpl::Try() {
100   return !!::TryAcquireSRWLockExclusive(
101       reinterpret_cast<PSRWLOCK>(&native_handle_));
102 }
103 
Unlock()104 void LockImpl::Unlock() {
105   ::ReleaseSRWLockExclusive(reinterpret_cast<PSRWLOCK>(&native_handle_));
106 }
107 
108 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
109 
110 #if DCHECK_IS_ON()
111 BASE_EXPORT void dcheck_trylock_result(int rv);
112 BASE_EXPORT void dcheck_unlock_result(int rv);
113 #endif
114 
Try()115 bool LockImpl::Try() {
116   int rv = pthread_mutex_trylock(&native_handle_);
117 #if DCHECK_IS_ON()
118   dcheck_trylock_result(rv);
119 #endif
120   return rv == 0;
121 }
122 
Unlock()123 void LockImpl::Unlock() {
124   [[maybe_unused]] int rv = pthread_mutex_unlock(&native_handle_);
125 #if DCHECK_IS_ON()
126   dcheck_unlock_result(rv);
127 #endif
128 }
129 #endif
130 
131 // This is an implementation used for AutoLock templated on the lock type.
132 template <class LockType>
133 class SCOPED_LOCKABLE BasicAutoLock {
134  public:
135   struct AlreadyAcquired {};
136 
BasicAutoLock(LockType & lock)137   explicit BasicAutoLock(LockType& lock) EXCLUSIVE_LOCK_FUNCTION(lock)
138       : lock_(lock) {
139     lock_.Acquire();
140   }
141 
BasicAutoLock(LockType & lock,const AlreadyAcquired &)142   BasicAutoLock(LockType& lock, const AlreadyAcquired&)
143       EXCLUSIVE_LOCKS_REQUIRED(lock)
144       : lock_(lock) {
145     lock_.AssertAcquired();
146   }
147 
148   BasicAutoLock(const BasicAutoLock&) = delete;
149   BasicAutoLock& operator=(const BasicAutoLock&) = delete;
150 
UNLOCK_FUNCTION()151   ~BasicAutoLock() UNLOCK_FUNCTION() {
152     lock_.AssertAcquired();
153     lock_.Release();
154   }
155 
156  private:
157   // RAW_PTR_EXCLUSION: crbug.com/1521343 crbug.com/1520734 crbug.com/1519816
158   RAW_PTR_EXCLUSION LockType& lock_;
159 };
160 
161 // This is an implementation used for AutoTryLock templated on the lock type.
162 template <class LockType>
163 class SCOPED_LOCKABLE BasicAutoTryLock {
164   STACK_ALLOCATED();
165 
166  public:
BasicAutoTryLock(LockType & lock)167   explicit BasicAutoTryLock(LockType& lock) EXCLUSIVE_LOCK_FUNCTION(lock)
168       : lock_(lock), is_acquired_(lock_.Try()) {}
169 
170   BasicAutoTryLock(const BasicAutoTryLock&) = delete;
171   BasicAutoTryLock& operator=(const BasicAutoTryLock&) = delete;
172 
UNLOCK_FUNCTION()173   ~BasicAutoTryLock() UNLOCK_FUNCTION() {
174     if (is_acquired_) {
175       lock_.AssertAcquired();
176       lock_.Release();
177     }
178   }
179 
is_acquired()180   bool is_acquired() const { return is_acquired_; }
181 
182  private:
183   LockType& lock_;
184   const bool is_acquired_;
185 };
186 
187 // This is an implementation used for AutoUnlock templated on the lock type.
188 template <class LockType>
189 class BasicAutoUnlock {
190   STACK_ALLOCATED();
191 
192  public:
BasicAutoUnlock(LockType & lock)193   explicit BasicAutoUnlock(LockType& lock) : lock_(lock) {
194     // We require our caller to have the lock.
195     lock_.AssertAcquired();
196     lock_.Release();
197   }
198 
199   BasicAutoUnlock(const BasicAutoUnlock&) = delete;
200   BasicAutoUnlock& operator=(const BasicAutoUnlock&) = delete;
201 
~BasicAutoUnlock()202   ~BasicAutoUnlock() { lock_.Acquire(); }
203 
204  private:
205   LockType& lock_;
206 };
207 
208 // This is an implementation used for AutoLockMaybe templated on the lock type.
209 template <class LockType>
210 class SCOPED_LOCKABLE BasicAutoLockMaybe {
211   STACK_ALLOCATED();
212 
213  public:
BasicAutoLockMaybe(LockType * lock)214   explicit BasicAutoLockMaybe(LockType* lock) EXCLUSIVE_LOCK_FUNCTION(lock)
215       : lock_(lock) {
216     if (lock_)
217       lock_->Acquire();
218   }
219 
220   BasicAutoLockMaybe(const BasicAutoLockMaybe&) = delete;
221   BasicAutoLockMaybe& operator=(const BasicAutoLockMaybe&) = delete;
222 
UNLOCK_FUNCTION()223   ~BasicAutoLockMaybe() UNLOCK_FUNCTION() {
224     if (lock_) {
225       lock_->AssertAcquired();
226       lock_->Release();
227     }
228   }
229 
230  private:
231   LockType* const lock_;
232 };
233 
234 // This is an implementation used for ReleasableAutoLock templated on the lock
235 // type.
236 template <class LockType>
237 class SCOPED_LOCKABLE BasicReleasableAutoLock {
238   STACK_ALLOCATED();
239 
240  public:
BasicReleasableAutoLock(LockType * lock)241   explicit BasicReleasableAutoLock(LockType* lock) EXCLUSIVE_LOCK_FUNCTION(lock)
242       : lock_(lock) {
243     DCHECK(lock_);
244     lock_->Acquire();
245   }
246 
247   BasicReleasableAutoLock(const BasicReleasableAutoLock&) = delete;
248   BasicReleasableAutoLock& operator=(const BasicReleasableAutoLock&) = delete;
249 
UNLOCK_FUNCTION()250   ~BasicReleasableAutoLock() UNLOCK_FUNCTION() {
251     if (lock_) {
252       lock_->AssertAcquired();
253       lock_->Release();
254     }
255   }
256 
Release()257   void Release() UNLOCK_FUNCTION() {
258     DCHECK(lock_);
259     lock_->AssertAcquired();
260     lock_->Release();
261     lock_ = nullptr;
262   }
263 
264  private:
265   LockType* lock_;
266 };
267 
268 }  // namespace internal
269 }  // namespace base
270 
271 #endif  // BASE_SYNCHRONIZATION_LOCK_IMPL_H_
272