1 // Copyright 2016 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 #include "base/task/common/checked_lock_impl.h"
6
7 #include <optional>
8 #include <ostream>
9 #include <unordered_map>
10 #include <vector>
11
12 #include "base/check_op.h"
13 #include "base/lazy_instance.h"
14 #include "base/memory/raw_ptr_exclusion.h"
15 #include "base/ranges/algorithm.h"
16 #include "base/synchronization/condition_variable.h"
17 #include "base/task/common/checked_lock.h"
18 #include "base/threading/platform_thread.h"
19 #include "base/threading/thread_local.h"
20
21 namespace base {
22 namespace internal {
23
24 namespace {
25
26 class SafeAcquisitionTracker {
27 public:
28 SafeAcquisitionTracker() = default;
29
30 SafeAcquisitionTracker(const SafeAcquisitionTracker&) = delete;
31 SafeAcquisitionTracker& operator=(const SafeAcquisitionTracker&) = delete;
32
RegisterLock(const CheckedLockImpl * const lock,const CheckedLockImpl * const predecessor)33 void RegisterLock(const CheckedLockImpl* const lock,
34 const CheckedLockImpl* const predecessor) {
35 DCHECK_NE(lock, predecessor) << "Reentrant locks are unsupported.";
36 AutoLock auto_lock(allowed_predecessor_map_lock_);
37 allowed_predecessor_map_[lock] = predecessor;
38 AssertSafePredecessor(lock);
39 }
40
UnregisterLock(const CheckedLockImpl * const lock)41 void UnregisterLock(const CheckedLockImpl* const lock) {
42 AutoLock auto_lock(allowed_predecessor_map_lock_);
43 allowed_predecessor_map_.erase(lock);
44 }
45
RecordAcquisition(const CheckedLockImpl * const lock)46 void RecordAcquisition(const CheckedLockImpl* const lock) {
47 AssertSafeAcquire(lock);
48 GetAcquiredLocksOnCurrentThread()->push_back(lock);
49 }
50
RecordRelease(const CheckedLockImpl * const lock)51 void RecordRelease(const CheckedLockImpl* const lock) {
52 LockVector* acquired_locks = GetAcquiredLocksOnCurrentThread();
53 const auto iter_at_lock = ranges::find(*acquired_locks, lock);
54 CHECK(iter_at_lock != acquired_locks->end(), base::NotFatalUntil::M125);
55 acquired_locks->erase(iter_at_lock);
56 }
57
AssertNoLockHeldOnCurrentThread()58 void AssertNoLockHeldOnCurrentThread() {
59 DCHECK(GetAcquiredLocksOnCurrentThread()->empty());
60 }
61
62 private:
63 using LockVector = std::vector<const CheckedLockImpl*>;
64 using PredecessorMap =
65 std::unordered_map<const CheckedLockImpl*, const CheckedLockImpl*>;
66
67 // This asserts that the lock is safe to acquire. This means that this should
68 // be run before actually recording the acquisition.
AssertSafeAcquire(const CheckedLockImpl * const lock)69 void AssertSafeAcquire(const CheckedLockImpl* const lock) {
70 const LockVector* acquired_locks = GetAcquiredLocksOnCurrentThread();
71
72 // If the thread currently holds no locks, this is inherently safe.
73 if (acquired_locks->empty())
74 return;
75
76 // A universal predecessor may not be acquired after any other lock.
77 DCHECK(!lock->is_universal_predecessor());
78
79 // Otherwise, make sure that the previous lock acquired is either an
80 // allowed predecessor for this lock or a universal predecessor.
81 const CheckedLockImpl* previous_lock = acquired_locks->back();
82 if (previous_lock->is_universal_predecessor())
83 return;
84
85 AutoLock auto_lock(allowed_predecessor_map_lock_);
86 // Using at() is exception-safe here as |lock| was registered already.
87 const CheckedLockImpl* allowed_predecessor =
88 allowed_predecessor_map_.at(lock);
89 if (lock->is_universal_successor()) {
90 DCHECK(!previous_lock->is_universal_successor());
91 return;
92 } else {
93 DCHECK_EQ(previous_lock, allowed_predecessor);
94 }
95 }
96
97 // Asserts that |lock|'s registered predecessor is safe. Because
98 // CheckedLocks are registered at construction time and any predecessor
99 // specified on a CheckedLock must already exist, the first registered
100 // CheckedLock in a potential chain must have a null predecessor and is thus
101 // cycle-free. Any subsequent CheckedLock with a predecessor must come from
102 // the set of registered CheckedLocks. Since the registered CheckedLocks
103 // only contain cycle-free CheckedLocks, this subsequent CheckedLock is
104 // itself cycle-free and may be safely added to the registered CheckedLock
105 // set.
AssertSafePredecessor(const CheckedLockImpl * lock) const106 void AssertSafePredecessor(const CheckedLockImpl* lock) const {
107 allowed_predecessor_map_lock_.AssertAcquired();
108 // Using at() is exception-safe here as |lock| was registered already.
109 const CheckedLockImpl* predecessor = allowed_predecessor_map_.at(lock);
110 if (predecessor) {
111 DCHECK(allowed_predecessor_map_.find(predecessor) !=
112 allowed_predecessor_map_.end())
113 << "CheckedLock was registered before its predecessor. "
114 << "Potential cycle detected";
115 }
116 }
117
GetAcquiredLocksOnCurrentThread()118 LockVector* GetAcquiredLocksOnCurrentThread() {
119 if (!tls_acquired_locks_.Get())
120 tls_acquired_locks_.Set(std::make_unique<LockVector>());
121
122 return tls_acquired_locks_.Get();
123 }
124
125 // Synchronizes access to |allowed_predecessor_map_|.
126 Lock allowed_predecessor_map_lock_;
127
128 // A map of allowed predecessors.
129 PredecessorMap allowed_predecessor_map_;
130
131 // A thread-local slot holding a vector of locks currently acquired on the
132 // current thread.
133 // LockVector is not a vector<raw_ptr> due to performance regressions detected
134 // in blink_perf.accessibility tests.
135 RAW_PTR_EXCLUSION ThreadLocalOwnedPointer<LockVector> tls_acquired_locks_;
136 };
137
138 LazyInstance<SafeAcquisitionTracker>::Leaky g_safe_acquisition_tracker =
139 LAZY_INSTANCE_INITIALIZER;
140
141 } // namespace
142
CheckedLockImpl()143 CheckedLockImpl::CheckedLockImpl() : CheckedLockImpl(nullptr) {}
144
CheckedLockImpl(const CheckedLockImpl * predecessor)145 CheckedLockImpl::CheckedLockImpl(const CheckedLockImpl* predecessor)
146 : is_universal_predecessor_(false) {
147 DCHECK(predecessor == nullptr || !predecessor->is_universal_successor_);
148 g_safe_acquisition_tracker.Get().RegisterLock(this, predecessor);
149 }
150
CheckedLockImpl(UniversalPredecessor)151 CheckedLockImpl::CheckedLockImpl(UniversalPredecessor)
152 : is_universal_predecessor_(true) {}
153
CheckedLockImpl(UniversalSuccessor)154 CheckedLockImpl::CheckedLockImpl(UniversalSuccessor)
155 : is_universal_successor_(true) {
156 g_safe_acquisition_tracker.Get().RegisterLock(this, nullptr);
157 }
158
~CheckedLockImpl()159 CheckedLockImpl::~CheckedLockImpl() {
160 g_safe_acquisition_tracker.Get().UnregisterLock(this);
161 }
162
AssertNoLockHeldOnCurrentThread()163 void CheckedLockImpl::AssertNoLockHeldOnCurrentThread() {
164 g_safe_acquisition_tracker.Get().AssertNoLockHeldOnCurrentThread();
165 }
166
Acquire()167 void CheckedLockImpl::Acquire() {
168 lock_.Acquire();
169 g_safe_acquisition_tracker.Get().RecordAcquisition(this);
170 }
171
Release()172 void CheckedLockImpl::Release() {
173 lock_.Release();
174 g_safe_acquisition_tracker.Get().RecordRelease(this);
175 }
176
AssertAcquired() const177 void CheckedLockImpl::AssertAcquired() const {
178 lock_.AssertAcquired();
179 }
180
AssertNotHeld() const181 void CheckedLockImpl::AssertNotHeld() const {
182 lock_.AssertNotHeld();
183 }
184
CreateConditionVariable()185 ConditionVariable CheckedLockImpl::CreateConditionVariable() {
186 return ConditionVariable(&lock_);
187 }
188
CreateConditionVariableAndEmplace(std::optional<ConditionVariable> & opt)189 void CheckedLockImpl::CreateConditionVariableAndEmplace(
190 std::optional<ConditionVariable>& opt) {
191 opt.emplace(&lock_);
192 }
193
194 } // namespace internal
195 } // namespace base
196