| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/task/task_scheduler/scheduler_lock_impl.h" |
| |
| #include <algorithm> |
| #include <unordered_map> |
| #include <vector> |
| |
| #include "base/lazy_instance.h" |
| #include "base/logging.h" |
| #include "base/synchronization/condition_variable.h" |
| #include "base/threading/platform_thread.h" |
| #include "base/threading/thread_local_storage.h" |
| |
| namespace base { |
| namespace internal { |
| |
| namespace { |
| |
| class SafeAcquisitionTracker { |
| public: |
| SafeAcquisitionTracker() : tls_acquired_locks_(&OnTLSDestroy) {} |
| |
| void RegisterLock(const SchedulerLockImpl* const lock, |
| const SchedulerLockImpl* const predecessor) { |
| DCHECK_NE(lock, predecessor) << "Reentrant locks are unsupported."; |
| AutoLock auto_lock(allowed_predecessor_map_lock_); |
| allowed_predecessor_map_[lock] = predecessor; |
| AssertSafePredecessor(lock); |
| } |
| |
| void UnregisterLock(const SchedulerLockImpl* const lock) { |
| AutoLock auto_lock(allowed_predecessor_map_lock_); |
| allowed_predecessor_map_.erase(lock); |
| } |
| |
| void RecordAcquisition(const SchedulerLockImpl* const lock) { |
| AssertSafeAcquire(lock); |
| GetAcquiredLocksOnCurrentThread()->push_back(lock); |
| } |
| |
| void RecordRelease(const SchedulerLockImpl* const lock) { |
| LockVector* acquired_locks = GetAcquiredLocksOnCurrentThread(); |
| const auto iter_at_lock = |
| std::find(acquired_locks->begin(), acquired_locks->end(), lock); |
| DCHECK(iter_at_lock != acquired_locks->end()); |
| acquired_locks->erase(iter_at_lock); |
| } |
| |
| private: |
| using LockVector = std::vector<const SchedulerLockImpl*>; |
| using PredecessorMap = |
| std::unordered_map<const SchedulerLockImpl*, const SchedulerLockImpl*>; |
| |
| // This asserts that the lock is safe to acquire. This means that this should |
| // be run before actually recording the acquisition. |
| void AssertSafeAcquire(const SchedulerLockImpl* const lock) { |
| const LockVector* acquired_locks = GetAcquiredLocksOnCurrentThread(); |
| |
| // If the thread currently holds no locks, this is inherently safe. |
| if (acquired_locks->empty()) |
| return; |
| |
| // Otherwise, make sure that the previous lock acquired is an allowed |
| // predecessor. |
| AutoLock auto_lock(allowed_predecessor_map_lock_); |
| // Using at() is exception-safe here as |lock| was registered already. |
| const SchedulerLockImpl* allowed_predecessor = |
| allowed_predecessor_map_.at(lock); |
| DCHECK_EQ(acquired_locks->back(), allowed_predecessor); |
| } |
| |
| // Asserts that |lock|'s registered predecessor is safe. Because |
| // SchedulerLocks are registered at construction time and any predecessor |
| // specified on a SchedulerLock must already exist, the first registered |
| // SchedulerLock in a potential chain must have a null predecessor and is thus |
| // cycle-free. Any subsequent SchedulerLock with a predecessor must come from |
| // the set of registered SchedulerLocks. Since the registered SchedulerLocks |
| // only contain cycle-free SchedulerLocks, this subsequent SchedulerLock is |
| // itself cycle-free and may be safely added to the registered SchedulerLock |
| // set. |
| void AssertSafePredecessor(const SchedulerLockImpl* lock) const { |
| allowed_predecessor_map_lock_.AssertAcquired(); |
| // Using at() is exception-safe here as |lock| was registered already. |
| const SchedulerLockImpl* predecessor = allowed_predecessor_map_.at(lock); |
| if (predecessor) { |
| DCHECK(allowed_predecessor_map_.find(predecessor) != |
| allowed_predecessor_map_.end()) |
| << "SchedulerLock was registered before its predecessor. " |
| << "Potential cycle detected"; |
| } |
| } |
| |
| LockVector* GetAcquiredLocksOnCurrentThread() { |
| if (!tls_acquired_locks_.Get()) |
| tls_acquired_locks_.Set(new LockVector); |
| |
| return reinterpret_cast<LockVector*>(tls_acquired_locks_.Get()); |
| } |
| |
| static void OnTLSDestroy(void* value) { |
| delete reinterpret_cast<LockVector*>(value); |
| } |
| |
| // Synchronizes access to |allowed_predecessor_map_|. |
| Lock allowed_predecessor_map_lock_; |
| |
| // A map of allowed predecessors. |
| PredecessorMap allowed_predecessor_map_; |
| |
| // A thread-local slot holding a vector of locks currently acquired on the |
| // current thread. |
| ThreadLocalStorage::Slot tls_acquired_locks_; |
| |
| DISALLOW_COPY_AND_ASSIGN(SafeAcquisitionTracker); |
| }; |
| |
| LazyInstance<SafeAcquisitionTracker>::Leaky g_safe_acquisition_tracker = |
| LAZY_INSTANCE_INITIALIZER; |
| |
| } // namespace |
| |
| SchedulerLockImpl::SchedulerLockImpl() : SchedulerLockImpl(nullptr) {} |
| |
| SchedulerLockImpl::SchedulerLockImpl(const SchedulerLockImpl* predecessor) { |
| g_safe_acquisition_tracker.Get().RegisterLock(this, predecessor); |
| } |
| |
| SchedulerLockImpl::~SchedulerLockImpl() { |
| g_safe_acquisition_tracker.Get().UnregisterLock(this); |
| } |
| |
| void SchedulerLockImpl::Acquire() { |
| lock_.Acquire(); |
| g_safe_acquisition_tracker.Get().RecordAcquisition(this); |
| } |
| |
| void SchedulerLockImpl::Release() { |
| lock_.Release(); |
| g_safe_acquisition_tracker.Get().RecordRelease(this); |
| } |
| |
| void SchedulerLockImpl::AssertAcquired() const { |
| lock_.AssertAcquired(); |
| } |
| |
| std::unique_ptr<ConditionVariable> |
| SchedulerLockImpl::CreateConditionVariable() { |
| return std::make_unique<ConditionVariable>(&lock_); |
| } |
| |
| } // namespace internal |
| } // namespace base |