// 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 #include #include #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; using PredecessorMap = std::unordered_map; // 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(tls_acquired_locks_.Get()); } static void OnTLSDestroy(void* value) { delete reinterpret_cast(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::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 SchedulerLockImpl::CreateConditionVariable() { return std::make_unique(&lock_); } } // namespace internal } // namespace base