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 const SchedulerLockImpl* allowed_predecessor =
71 allowed_predecessor_map_.at(lock);
72 DCHECK_EQ(acquired_locks->back(), allowed_predecessor);
73 }
74
AssertSafePredecessor(const SchedulerLockImpl * lock) const75 void AssertSafePredecessor(const SchedulerLockImpl* lock) const {
76 allowed_predecessor_map_lock_.AssertAcquired();
77 for (const SchedulerLockImpl* predecessor =
78 allowed_predecessor_map_.at(lock);
79 predecessor != nullptr;
80 predecessor = allowed_predecessor_map_.at(predecessor)) {
81 DCHECK_NE(predecessor, lock) <<
82 "Scheduler lock predecessor cycle detected.";
83 }
84 }
85
GetAcquiredLocksOnCurrentThread()86 LockVector* GetAcquiredLocksOnCurrentThread() {
87 if (!tls_acquired_locks_.Get())
88 tls_acquired_locks_.Set(new LockVector);
89
90 return reinterpret_cast<LockVector*>(tls_acquired_locks_.Get());
91 }
92
OnTLSDestroy(void * value)93 static void OnTLSDestroy(void* value) {
94 delete reinterpret_cast<LockVector*>(value);
95 }
96
97 // Synchronizes access to |allowed_predecessor_map_|.
98 Lock allowed_predecessor_map_lock_;
99
100 // A map of allowed predecessors.
101 PredecessorMap allowed_predecessor_map_;
102
103 // A thread-local slot holding a vector of locks currently acquired on the
104 // current thread.
105 ThreadLocalStorage::Slot tls_acquired_locks_;
106
107 DISALLOW_COPY_AND_ASSIGN(SafeAcquisitionTracker);
108 };
109
110 LazyInstance<SafeAcquisitionTracker>::Leaky g_safe_acquisition_tracker =
111 LAZY_INSTANCE_INITIALIZER;
112
113 } // namespace
114
SchedulerLockImpl()115 SchedulerLockImpl::SchedulerLockImpl() : SchedulerLockImpl(nullptr) {}
116
SchedulerLockImpl(const SchedulerLockImpl * predecessor)117 SchedulerLockImpl::SchedulerLockImpl(const SchedulerLockImpl* predecessor) {
118 g_safe_acquisition_tracker.Get().RegisterLock(this, predecessor);
119 }
120
~SchedulerLockImpl()121 SchedulerLockImpl::~SchedulerLockImpl() {
122 g_safe_acquisition_tracker.Get().UnregisterLock(this);
123 }
124
Acquire()125 void SchedulerLockImpl::Acquire() {
126 lock_.Acquire();
127 g_safe_acquisition_tracker.Get().RecordAcquisition(this);
128 }
129
Release()130 void SchedulerLockImpl::Release() {
131 lock_.Release();
132 g_safe_acquisition_tracker.Get().RecordRelease(this);
133 }
134
AssertAcquired() const135 void SchedulerLockImpl::AssertAcquired() const {
136 lock_.AssertAcquired();
137 }
138
139 std::unique_ptr<ConditionVariable>
CreateConditionVariable()140 SchedulerLockImpl::CreateConditionVariable() {
141 return std::unique_ptr<ConditionVariable>(new ConditionVariable(&lock_));
142 }
143
144 } // namespace internal
145 } // base
146