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