xref: /aosp_15_r20/external/cronet/base/task/common/checked_lock_impl.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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