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