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