1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
base / synchronization / lock.h [blame]
// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_SYNCHRONIZATION_LOCK_H_
#define BASE_SYNCHRONIZATION_LOCK_H_
#include <type_traits>
#include "base/base_export.h"
#include "base/dcheck_is_on.h"
#include "base/synchronization/lock_impl.h"
#include "base/synchronization/lock_subtle.h"
#include "base/thread_annotations.h"
#include "build/build_config.h"
#if DCHECK_IS_ON()
#include <memory>
#include "base/compiler_specific.h"
#include "base/threading/platform_thread_ref.h"
#endif
namespace base {
// Foward-declare to avoid circular #includes.
template <typename T>
class FunctionRef;
// A convenient wrapper for an OS specific critical section. The only real
// intelligence in this class is in debug mode for the support for the
// AssertAcquired() method and invariant debugging.
class LOCKABLE BASE_EXPORT Lock {
public:
Lock(const Lock&) = delete;
Lock& operator=(const Lock&) = delete;
#if !DCHECK_IS_ON()
// Optimized wrapper implementation
Lock() = default;
// The provided `check_invariants` will be ignored. Using a templated method
// here instead of `explicit Lock(FunctionRef<void()>)` avoids a compile error
// about instantiation of an undefined template when code that neither
// #includes function_ref.h nor calls this constructor #includes this header.
template <typename T,
typename =
std::enable_if_t<std::is_convertible_v<T, FunctionRef<void()>>>>
explicit Lock(T check_invariants) : Lock() {}
~Lock() = default;
void Acquire(subtle::LockTracking tracking = subtle::LockTracking::kDisabled)
EXCLUSIVE_LOCK_FUNCTION() {
lock_.Lock();
}
void Release() UNLOCK_FUNCTION() { lock_.Unlock(); }
// If the lock is not held, take it and return true. If the lock is already
// held by another thread, immediately return false. This must not be called
// by a thread already holding the lock (what happens is undefined and an
// assertion may fail).
bool Try(subtle::LockTracking tracking = subtle::LockTracking::kDisabled)
EXCLUSIVE_TRYLOCK_FUNCTION(true) {
return lock_.Try();
}
// Null implementation if not debug.
void AssertAcquired() const ASSERT_EXCLUSIVE_LOCK() {}
void AssertNotHeld() const {}
#else
Lock();
// The provided `check_invariants` will be invoked just after the mutex is
// acquired and just before the mutex is released. It should have no
// side-effects and should DCHECK whatever holder-specific invariants
// regarding this lock may exist.
explicit Lock(FunctionRef<void()> check_invariants LIFETIME_BOUND);
~Lock();
// Note: Acquiring a lock that is already held by the calling thread is not
// supported and results in a CHECK() failure.
void Acquire(subtle::LockTracking tracking = subtle::LockTracking::kDisabled)
EXCLUSIVE_LOCK_FUNCTION();
void Release() UNLOCK_FUNCTION();
bool Try(subtle::LockTracking tracking = subtle::LockTracking::kDisabled)
EXCLUSIVE_TRYLOCK_FUNCTION(true);
void AssertAcquired() const ASSERT_EXCLUSIVE_LOCK();
void AssertNotHeld() const;
#endif // DCHECK_IS_ON()
// Whether Lock mitigates priority inversion when used from different thread
// priorities.
static bool HandlesMultipleThreadPriorities() {
#if BUILDFLAG(IS_WIN)
// Windows mitigates priority inversion by randomly boosting the priority of
// ready threads.
// https://msdn.microsoft.com/library/windows/desktop/ms684831.aspx
return true;
#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
// POSIX mitigates priority inversion by setting the priority of a thread
// holding a Lock to the maximum priority of any other thread waiting on it.
return internal::LockImpl::PriorityInheritanceAvailable();
#else
#error Unsupported platform
#endif
}
// Both Windows and POSIX implementations of ConditionVariable need to be
// able to see our lock and tweak our debugging counters, as they release and
// acquire locks inside of their condition variable APIs.
friend class ConditionVariable;
private:
#if DCHECK_IS_ON()
// Check that `owning_thread_ref_` refers to the current thread and unset it.
void CheckHeldAndUnmark();
// Check that `owning_thread_ref_` is null and set it to the current thread.
void CheckUnheldAndMark();
// Adds/removes this lock to/from the thread-local list returned by
// `subtle::GetLocksHeldByCurrentThread()`, unless tracking is disabled.
void AddToLocksHeldOnCurrentThread();
void RemoveFromLocksHeldOnCurrentThread();
// Reference to the thread holding the lock. Protected by `lock_`.
base::PlatformThreadRef owning_thread_ref_;
// Whether the lock is currently in the list of locks held by a thread. When
// true, the lock is removed from the list upon `Release()`.
bool in_tracked_locks_held_by_current_thread_ = false;
std::unique_ptr<FunctionRef<void()>> check_invariants_;
#endif // DCHECK_IS_ON()
// Platform specific underlying lock implementation.
internal::LockImpl lock_;
};
// A helper class that acquires the given Lock while the AutoLock is in scope.
using AutoLock = internal::BasicAutoLock<Lock>;
// A helper class that acquires the given Lock while the MovableAutoLock is in
// scope. Unlike AutoLock, the lock can be moved out of MovableAutoLock. Unlike
// AutoLockMaybe, the passed in lock is always valid, so need to check only on
// destruction.
using MovableAutoLock = internal::BasicMovableAutoLock<Lock>;
// A helper class that tries to acquire the given Lock while the AutoTryLock is
// in scope.
using AutoTryLock = internal::BasicAutoTryLock<Lock>;
// AutoUnlock is a helper that will Release() the |lock| argument in the
// constructor, and re-Acquire() it in the destructor.
using AutoUnlock = internal::BasicAutoUnlock<Lock>;
// Like AutoLock but is a no-op when the provided Lock* is null. Inspired from
// absl::MutexLockMaybe. Use this instead of std::optional<AutoLock> to get
// around -Wthread-safety-analysis warnings for conditional locking.
using AutoLockMaybe = internal::BasicAutoLockMaybe<Lock>;
// Like AutoLock but permits Release() of its mutex before destruction.
// Release() may be called at most once. Inspired from
// absl::ReleasableMutexLock. Use this instead of std::optional<AutoLock> to get
// around -Wthread-safety-analysis warnings for AutoLocks that are explicitly
// released early (prefer proper scoping to this).
using ReleasableAutoLock = internal::BasicReleasableAutoLock<Lock>;
} // namespace base
#endif // BASE_SYNCHRONIZATION_LOCK_H_