Home | History | Annotate | Download | only in task_scheduler
      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:
     24   SafeAcquisitionTracker() : tls_acquired_locks_(&OnTLSDestroy) {}
     25 
     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 
     35   void UnregisterLock(const SchedulerLockImpl* const lock) {
     36     AutoLock auto_lock(allowed_predecessor_map_lock_);
     37     allowed_predecessor_map_.erase(lock);
     38   }
     39 
     40   void RecordAcquisition(const SchedulerLockImpl* const lock) {
     41     AssertSafeAcquire(lock);
     42     GetAcquiredLocksOnCurrentThread()->push_back(lock);
     43   }
     44 
     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.
     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.
     85   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 
     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 
    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 
    126 SchedulerLockImpl::SchedulerLockImpl() : SchedulerLockImpl(nullptr) {}
    127 
    128 SchedulerLockImpl::SchedulerLockImpl(const SchedulerLockImpl* predecessor) {
    129   g_safe_acquisition_tracker.Get().RegisterLock(this, predecessor);
    130 }
    131 
    132 SchedulerLockImpl::~SchedulerLockImpl() {
    133   g_safe_acquisition_tracker.Get().UnregisterLock(this);
    134 }
    135 
    136 void SchedulerLockImpl::Acquire() {
    137   lock_.Acquire();
    138   g_safe_acquisition_tracker.Get().RecordAcquisition(this);
    139 }
    140 
    141 void SchedulerLockImpl::Release() {
    142   lock_.Release();
    143   g_safe_acquisition_tracker.Get().RecordRelease(this);
    144 }
    145 
    146 void SchedulerLockImpl::AssertAcquired() const {
    147   lock_.AssertAcquired();
    148 }
    149 
    150 std::unique_ptr<ConditionVariable>
    151 SchedulerLockImpl::CreateConditionVariable() {
    152   return std::unique_ptr<ConditionVariable>(new ConditionVariable(&lock_));
    153 }
    154 
    155 }  // namespace internal
    156 }  // base
    157