1 // Copyright 2016 The Chromium Authors. All rights reserved.
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_scheduler/scheduler_lock_impl.h"
6
7 #include <algorithm>
8 #include <unordered_map>
9 #include <vector>
10
11 #include "base/lazy_instance.h"
12 #include "base/logging.h"
13 #include "base/synchronization/condition_variable.h"
14 #include "base/threading/platform_thread.h"
15 #include "base/threading/thread_local_storage.h"
16
17 namespace base {
18 namespace internal {
19
20 namespace {
21
22 class SafeAcquisitionTracker {
23 public:
SafeAcquisitionTracker()24 SafeAcquisitionTracker() : tls_acquired_locks_(&OnTLSDestroy) {}
25
RegisterLock(const SchedulerLockImpl * const lock,const SchedulerLockImpl * const predecessor)26 void RegisterLock(
27 const SchedulerLockImpl* const lock,
28 const SchedulerLockImpl* const predecessor) {
29 DCHECK_NE(lock, predecessor) << "Reentrant locks are unsupported.";
30 AutoLock auto_lock(allowed_predecessor_map_lock_);
31 allowed_predecessor_map_[lock] = predecessor;
32 AssertSafePredecessor(lock);
33 }
34
UnregisterLock(const SchedulerLockImpl * const lock)35 void UnregisterLock(const SchedulerLockImpl* const lock) {
36 AutoLock auto_lock(allowed_predecessor_map_lock_);
37 allowed_predecessor_map_.erase(lock);
38 }
39
RecordAcquisition(const SchedulerLockImpl * const lock)40 void RecordAcquisition(const SchedulerLockImpl* const lock) {
41 AssertSafeAcquire(lock);
42 GetAcquiredLocksOnCurrentThread()->push_back(lock);
43 }
44
RecordRelease(const SchedulerLockImpl * const lock)45 void RecordRelease(const SchedulerLockImpl* const lock) {
46 LockVector* acquired_locks = GetAcquiredLocksOnCurrentThread();
47 const auto iter_at_lock =
48 std::find(acquired_locks->begin(), acquired_locks->end(), lock);
49 DCHECK(iter_at_lock != acquired_locks->end());
50 acquired_locks->erase(iter_at_lock);
51 }
52
53 private:
54 using LockVector = std::vector<const SchedulerLockImpl*>;
55 using PredecessorMap = std::unordered_map<
56 const SchedulerLockImpl*, const SchedulerLockImpl*>;
57
58 // This asserts that the lock is safe to acquire. This means that this should
59 // be run before actually recording the acquisition.
AssertSafeAcquire(const SchedulerLockImpl * const lock)60 void AssertSafeAcquire(const SchedulerLockImpl* const lock) {
61 const LockVector* acquired_locks = GetAcquiredLocksOnCurrentThread();
62
63 // If the thread currently holds no locks, this is inherently safe.
64 if (acquired_locks->empty())
65 return;
66
67 // Otherwise, make sure that the previous lock acquired is an allowed
68 // predecessor.
69 AutoLock auto_lock(allowed_predecessor_map_lock_);
70 // Using at() is exception-safe here as |lock| was registered already.
71 const SchedulerLockImpl* allowed_predecessor =
72 allowed_predecessor_map_.at(lock);
73 DCHECK_EQ(acquired_locks->back(), allowed_predecessor);
74 }
75
76 // Asserts that |lock|'s registered predecessor is safe. Because
77 // SchedulerLocks are registered at construction time and any predecessor
78 // specified on a SchedulerLock must already exist, the first registered
79 // SchedulerLock in a potential chain must have a null predecessor and is thus
80 // cycle-free. Any subsequent SchedulerLock with a predecessor must come from
81 // the set of registered SchedulerLocks. Since the registered SchedulerLocks
82 // only contain cycle-free SchedulerLocks, this subsequent SchedulerLock is
83 // itself cycle-free and may be safely added to the registered SchedulerLock
84 // set.
AssertSafePredecessor(const SchedulerLockImpl * lock) const85 void AssertSafePredecessor(const SchedulerLockImpl* lock) const {
86 allowed_predecessor_map_lock_.AssertAcquired();
87 // Using at() is exception-safe here as |lock| was registered already.
88 const SchedulerLockImpl* predecessor = allowed_predecessor_map_.at(lock);
89 if (predecessor) {
90 DCHECK(allowed_predecessor_map_.find(predecessor) !=
91 allowed_predecessor_map_.end())
92 << "SchedulerLock was registered before its predecessor. "
93 << "Potential cycle detected";
94 }
95 }
96
GetAcquiredLocksOnCurrentThread()97 LockVector* GetAcquiredLocksOnCurrentThread() {
98 if (!tls_acquired_locks_.Get())
99 tls_acquired_locks_.Set(new LockVector);
100
101 return reinterpret_cast<LockVector*>(tls_acquired_locks_.Get());
102 }
103
OnTLSDestroy(void * value)104 static void OnTLSDestroy(void* value) {
105 delete reinterpret_cast<LockVector*>(value);
106 }
107
108 // Synchronizes access to |allowed_predecessor_map_|.
109 Lock allowed_predecessor_map_lock_;
110
111 // A map of allowed predecessors.
112 PredecessorMap allowed_predecessor_map_;
113
114 // A thread-local slot holding a vector of locks currently acquired on the
115 // current thread.
116 ThreadLocalStorage::Slot tls_acquired_locks_;
117
118 DISALLOW_COPY_AND_ASSIGN(SafeAcquisitionTracker);
119 };
120
121 LazyInstance<SafeAcquisitionTracker>::Leaky g_safe_acquisition_tracker =
122 LAZY_INSTANCE_INITIALIZER;
123
124 } // namespace
125
SchedulerLockImpl()126 SchedulerLockImpl::SchedulerLockImpl() : SchedulerLockImpl(nullptr) {}
127
SchedulerLockImpl(const SchedulerLockImpl * predecessor)128 SchedulerLockImpl::SchedulerLockImpl(const SchedulerLockImpl* predecessor) {
129 g_safe_acquisition_tracker.Get().RegisterLock(this, predecessor);
130 }
131
~SchedulerLockImpl()132 SchedulerLockImpl::~SchedulerLockImpl() {
133 g_safe_acquisition_tracker.Get().UnregisterLock(this);
134 }
135
Acquire()136 void SchedulerLockImpl::Acquire() {
137 lock_.Acquire();
138 g_safe_acquisition_tracker.Get().RecordAcquisition(this);
139 }
140
Release()141 void SchedulerLockImpl::Release() {
142 lock_.Release();
143 g_safe_acquisition_tracker.Get().RecordRelease(this);
144 }
145
AssertAcquired() const146 void SchedulerLockImpl::AssertAcquired() const {
147 lock_.AssertAcquired();
148 }
149
150 std::unique_ptr<ConditionVariable>
CreateConditionVariable()151 SchedulerLockImpl::CreateConditionVariable() {
152 return std::unique_ptr<ConditionVariable>(new ConditionVariable(&lock_));
153 }
154
155 } // namespace internal
156 } // base
157