Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(35)

Side by Side Diff: base/task_scheduler/scheduler_lock_impl.cc

Issue 1706123002: TaskScheduler [2/9] Scheduler Lock (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: CR Feedback Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698