OLD | NEW |
---|---|
(Empty) | |
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 | |
16 namespace base { | |
17 namespace internal { | |
18 | |
19 namespace { | |
20 | |
21 class SafeAcquisitionTracker { | |
22 public: | |
23 SafeAcquisitionTracker() = default; | |
24 | |
25 void RegisterLock( | |
26 const SchedulerLockImpl* const lock, | |
27 const SchedulerLockImpl* const predecessor) { | |
28 // Reentrant locks are unsupported. | |
29 DCHECK(lock != predecessor); | |
30 AutoLock auto_lock(metadata_lock_); | |
31 allowed_predecessor_map[lock] = predecessor; | |
32 AssertSafePredecessor(lock); | |
33 } | |
34 | |
35 void UnregisterLock(const SchedulerLockImpl* const lock) { | |
36 AutoLock auto_lock(metadata_lock_); | |
37 allowed_predecessor_map.erase(lock); | |
38 } | |
39 | |
40 void RecordAcquisition(const SchedulerLockImpl* const lock) { | |
41 AutoLock auto_lock(metadata_lock_); | |
42 const PlatformThreadId id = PlatformThread::CurrentId(); | |
43 AssertSafeAcquire(id, lock); | |
44 acquired_locks_[id].push_back(lock); | |
45 } | |
46 | |
47 void RecordRelease(const SchedulerLockImpl* const lock) { | |
48 AutoLock auto_lock(metadata_lock_); | |
49 const PlatformThreadId id = PlatformThread::CurrentId(); | |
50 LockVector& thread_locks = acquired_locks_[id]; | |
51 const auto iter = | |
52 std::find(thread_locks.begin(), thread_locks.end(), lock); | |
53 DCHECK(iter != thread_locks.end()); | |
54 thread_locks.erase(iter); | |
55 if (thread_locks.empty()) | |
56 acquired_locks_.erase(id); | |
57 } | |
58 | |
59 private: | |
60 using LockVector = std::vector<const SchedulerLockImpl*>; | |
61 using PredecessorMap = std::unordered_map< | |
62 const SchedulerLockImpl*, const SchedulerLockImpl*>; | |
63 using AcquisitionMap = | |
64 std::unordered_map<base::PlatformThreadId, LockVector>; | |
65 | |
66 // This asserts that the lock is safe to acquire. This means that this should | |
67 // be run before actually recording the acquisition. | |
68 void AssertSafeAcquire(PlatformThreadId id, | |
69 const SchedulerLockImpl* const lock) const { | |
70 metadata_lock_.AssertAcquired(); | |
71 const auto& thread_lock_pair = acquired_locks_.find(id); | |
72 | |
73 // If the thread currently holds no locks, this is inherently safe. | |
74 if (thread_lock_pair == acquired_locks_.end()) | |
75 return; | |
76 | |
77 // Otherwise, make sure that the previous lock acquired is an allowed | |
78 // predecessor. | |
79 const SchedulerLockImpl* allowed_predecessor = | |
80 allowed_predecessor_map.at(lock); | |
gab
2016/02/19 16:08:03
at() throws an exception if |lock| isn't in |allow
robliao
2016/02/19 21:25:06
I do actually want this to crash since it's akin t
gab
2016/02/19 22:05:24
But we could DCHECK with better information for th
| |
81 DCHECK(thread_lock_pair->second.back() == allowed_predecessor); | |
82 } | |
83 | |
84 void AssertSafePredecessor(const SchedulerLockImpl* lock) const { | |
85 metadata_lock_.AssertAcquired(); | |
86 for (const SchedulerLockImpl* predecessor = | |
87 allowed_predecessor_map.at(lock); | |
88 predecessor != nullptr; | |
89 predecessor = allowed_predecessor_map.at(predecessor)) { | |
gab
2016/02/19 16:08:03
Also don't use at() here.
robliao
2016/02/19 21:25:06
See above.
| |
90 if (predecessor == lock) | |
91 DCHECK(false); | |
gab
2016/02/19 16:08:03
Why the change to DCHECK(false)? This is what NOTR
robliao
2016/02/19 21:25:06
I got feedback in a separate review that NOTREACHE
gab
2016/02/19 22:05:24
Meh, this code is only for !DCHECK_IS_ON() where t
robliao
2016/02/19 23:51:11
No strong opinion either way. Switched back to NOT
| |
92 } | |
93 } | |
94 | |
95 // Synchronizes everything in SafeAcquisitionTracker. | |
96 base::Lock metadata_lock_; | |
97 | |
98 PredecessorMap allowed_predecessor_map; | |
99 AcquisitionMap acquired_locks_; | |
100 | |
101 DISALLOW_COPY_AND_ASSIGN(SafeAcquisitionTracker); | |
102 }; | |
103 | |
104 LazyInstance<SafeAcquisitionTracker> g_safe_acquisition_tracker = | |
105 LAZY_INSTANCE_INITIALIZER; | |
106 | |
107 } // namespace | |
108 | |
109 SchedulerLockImpl::SchedulerLockImpl() : SchedulerLockImpl(nullptr) {} | |
110 | |
111 SchedulerLockImpl::SchedulerLockImpl(const SchedulerLockImpl* predecessor) { | |
112 g_safe_acquisition_tracker.Get().RegisterLock(this, predecessor); | |
113 } | |
114 | |
115 SchedulerLockImpl::~SchedulerLockImpl() { | |
116 g_safe_acquisition_tracker.Get().UnregisterLock(this); | |
117 } | |
118 | |
119 void SchedulerLockImpl::Acquire() { | |
120 lock_.Acquire(); | |
121 g_safe_acquisition_tracker.Get().RecordAcquisition(this); | |
gab
2016/02/19 16:08:03
Should this go first to avoid locking in the death
robliao
2016/02/19 21:25:06
See discussion in the unit tests file:
Summarized
| |
122 } | |
123 | |
124 void SchedulerLockImpl::Release() { | |
125 lock_.Release(); | |
126 g_safe_acquisition_tracker.Get().RecordRelease(this); | |
127 } | |
128 | |
129 void SchedulerLockImpl::AssertAcquired() const { | |
130 lock_.AssertAcquired(); | |
131 } | |
132 | |
133 scoped_ptr<ConditionVariable> SchedulerLockImpl::CreateConditionVariable() { | |
134 return scoped_ptr<ConditionVariable>(new ConditionVariable(&lock_)); | |
135 } | |
136 | |
137 } // namespace internal | |
138 } // base | |
OLD | NEW |