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