• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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