• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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