• 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 <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