// 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 "chromecast/base/component/component.h" #include #include "base/atomicops.h" #include "base/bind.h" #include "base/bind_helpers.h" #include "base/callback_helpers.h" #include "base/location.h" #include "base/single_thread_task_runner.h" #include "base/thread_task_runner_handle.h" namespace chromecast { namespace { const base::subtle::AtomicWord kEnabledBit = 0x40000000; } // namespace namespace subtle { class DependencyCount : public base::RefCountedThreadSafe { public: explicit DependencyCount(ComponentBase* component) : component_(component), task_runner_(base::ThreadTaskRunnerHandle::Get()), dep_count_(0), disabling_(false) { DCHECK(component_); } void Detach() { DCHECK(task_runner_->BelongsToCurrentThread()); component_ = nullptr; } void Disable() { DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK(!disabling_); disabling_ = true; std::set dependents(strong_dependents_); for (DependencyBase* dependent : dependents) dependent->Disable(); while (true) { AtomicWord deps = base::subtle::NoBarrier_Load(&dep_count_); AtomicWord old_deps = base::subtle::Acquire_CompareAndSwap( &dep_count_, deps, deps & ~kEnabledBit); if (old_deps == deps) { if ((deps & ~kEnabledBit) == 0) DisableComplete(); return; } } } void Enable() { DCHECK(task_runner_->BelongsToCurrentThread()); disabling_ = false; while (true) { AtomicWord deps = base::subtle::NoBarrier_Load(&dep_count_); DCHECK(!(deps & kEnabledBit)); AtomicWord old_deps = base::subtle::Release_CompareAndSwap( &dep_count_, deps, deps | kEnabledBit); if (old_deps == deps) break; } for (DependencyBase* dependent : strong_dependents_) dependent->Ready(component_); } ComponentBase* WeakAcquireDep() { while (true) { AtomicWord deps = base::subtle::NoBarrier_Load(&dep_count_); if (!(deps & kEnabledBit)) return nullptr; AtomicWord old_deps = base::subtle::Acquire_CompareAndSwap(&dep_count_, deps, deps + 1); // We depend on the fact that a component must be disabled (meaning that // we will never reach this point) before it is destroyed. Therefore if // we do reach this point, it is safe to return the raw pointer. if (old_deps == deps) return component_; } } void StrongAcquireDep(DependencyBase* dependent) { DCHECK(dependent); DCHECK(task_runner_->BelongsToCurrentThread()); if (!component_) { dependent->Disable(); return; } strong_dependents_.insert(dependent); AtomicWord count = base::subtle::NoBarrier_AtomicIncrement(&dep_count_, 1); DCHECK_GT(count, 0); if (count & kEnabledBit) { dependent->Ready(component_); } else { component_->Enable(); } } void StrongReleaseDep(DependencyBase* dependent) { DCHECK(dependent); DCHECK(task_runner_->BelongsToCurrentThread()); strong_dependents_.erase(dependent); ReleaseDep(); } void ReleaseDep() { AtomicWord after = base::subtle::Barrier_AtomicIncrement(&dep_count_, -1); DCHECK_GE(after, 0); if (after == 0) DisableComplete(); } bool DependsOn(ComponentBase* component) { DCHECK(task_runner_->BelongsToCurrentThread()); if (!component_) return false; if (component_ == component) return true; return component_->DependsOn(component); } private: friend class base::RefCountedThreadSafe; using AtomicWord = base::subtle::AtomicWord; ~DependencyCount() {} void DisableComplete() { if (!task_runner_->BelongsToCurrentThread()) { task_runner_->PostTask( FROM_HERE, base::Bind(&DependencyCount::DisableComplete, this)); return; } // Need to make sure that Enable() was not called in the meantime. if (base::subtle::NoBarrier_Load(&dep_count_) != 0 || !disabling_) return; // Ensure that we don't call DisableComplete() more than once per Disable(). disabling_ = false; DCHECK(component_); DCHECK(strong_dependents_.empty()); component_->DependencyCountDisableComplete(); } ComponentBase* component_; const scoped_refptr task_runner_; AtomicWord dep_count_; bool disabling_; std::set strong_dependents_; DISALLOW_COPY_AND_ASSIGN(DependencyCount); }; DependencyBase::DependencyBase(const WeakReferenceBase& dependency, ComponentBase* dependent) : dependent_(dependent), dependency_(nullptr), counter_(dependency.counter_) { DCHECK(dependent_); dependent_->AddDependency(this); } DependencyBase::~DependencyBase() {} void DependencyBase::StartUsing() { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(!dependency_); counter_->StrongAcquireDep(this); } void DependencyBase::StopUsing() { DCHECK(thread_checker_.CalledOnValidThread()); if (!dependency_) return; dependency_ = nullptr; counter_->StrongReleaseDep(this); } void DependencyBase::Ready(ComponentBase* dependency) { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(!dependency_); DCHECK(dependency); dependency_ = dependency; dependent_->DependencyReady(); } void DependencyBase::Disable() { DCHECK(thread_checker_.CalledOnValidThread()); dependent_->Disable(); } bool DependencyBase::DependsOn(ComponentBase* component) { return counter_->DependsOn(component); } WeakReferenceBase::WeakReferenceBase(const ComponentBase& dependency) : counter_(dependency.counter_) { DCHECK(counter_); } WeakReferenceBase::WeakReferenceBase(const DependencyBase& dependency) : counter_(dependency.counter_) { DCHECK(counter_); } WeakReferenceBase::WeakReferenceBase(const WeakReferenceBase& other) : counter_(other.counter_) { DCHECK(counter_); } WeakReferenceBase::WeakReferenceBase(WeakReferenceBase&& other) : counter_(std::move(other.counter_)) { DCHECK(counter_); } WeakReferenceBase::~WeakReferenceBase() {} ScopedReferenceBase::ScopedReferenceBase( const scoped_refptr& counter) : counter_(counter) { DCHECK(counter_); dependency_ = counter_->WeakAcquireDep(); } ScopedReferenceBase::ScopedReferenceBase(ScopedReferenceBase&& other) : counter_(std::move(other.counter_)), dependency_(other.dependency_) { DCHECK(counter_); other.dependency_ = nullptr; } ScopedReferenceBase::~ScopedReferenceBase() { if (dependency_) counter_->ReleaseDep(); } } // namespace subtle ComponentBase::ComponentBase() : task_runner_(base::ThreadTaskRunnerHandle::Get()), state_(kStateDisabled), async_call_in_progress_(false), pending_dependency_count_(0), observers_(new base::ObserverListThreadSafe()) { counter_ = new subtle::DependencyCount(this); } ComponentBase::~ComponentBase() { DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK_EQ(kStateDisabled, state_) << "Components must be disabled " << "before being destroyed"; counter_->Detach(); } void ComponentBase::Enable() { DCHECK(task_runner_->BelongsToCurrentThread()); if (state_ == kStateEnabling || state_ == kStateEnabled || state_ == kStateDestroying) { return; } state_ = kStateEnabling; if (strong_dependencies_.empty()) { TryOnEnable(); } else { // Enable all strong dependencies first. pending_dependency_count_ = strong_dependencies_.size(); for (subtle::DependencyBase* dependency : strong_dependencies_) dependency->StartUsing(); } } void ComponentBase::DependencyReady() { DCHECK(task_runner_->BelongsToCurrentThread()); if (state_ != kStateEnabling) return; DCHECK_GT(pending_dependency_count_, 0); --pending_dependency_count_; if (pending_dependency_count_ == 0) TryOnEnable(); } void ComponentBase::TryOnEnable() { DCHECK_EQ(kStateEnabling, state_); if (async_call_in_progress_) return; async_call_in_progress_ = true; OnEnable(); } void ComponentBase::OnEnableComplete(bool success) { // Always post a task, to prevent the stack from getting too deep. task_runner_->PostTask(FROM_HERE, base::Bind(&ComponentBase::OnEnableCompleteInternal, base::Unretained(this), success)); } void ComponentBase::OnEnableCompleteInternal(bool success) { async_call_in_progress_ = false; DCHECK(state_ == kStateEnabling || state_ == kStateDisabling || state_ == kStateDestroying); if (state_ != kStateEnabling) { if (success) { TryOnDisable(); } else { OnDisableCompleteInternal(); } return; } if (success) { state_ = kStateEnabled; counter_->Enable(); } else { Disable(); } observers_->Notify(FROM_HERE, &Observer::OnComponentEnabled, this, success); } void ComponentBase::Destroy() { DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK_NE(kStateDestroying, state_); if (state_ == kStateDisabled) { delete this; } else { bool should_disable = (state_ != kStateDisabling); state_ = kStateDestroying; if (should_disable) counter_->Disable(); } } void ComponentBase::AddObserver(Observer* observer) { DCHECK(observer); observers_->AddObserver(observer); } void ComponentBase::RemoveObserver(Observer* observer) { observers_->RemoveObserver(observer); } void ComponentBase::Disable() { DCHECK(task_runner_->BelongsToCurrentThread()); if (state_ == kStateDisabling || state_ == kStateDisabled || state_ == kStateDestroying) { return; } state_ = kStateDisabling; counter_->Disable(); } void ComponentBase::DependencyCountDisableComplete() { DCHECK(task_runner_->BelongsToCurrentThread()); if (state_ == kStateDisabling || state_ == kStateDestroying) TryOnDisable(); } void ComponentBase::TryOnDisable() { DCHECK(state_ == kStateDisabling || state_ == kStateDestroying); if (async_call_in_progress_) return; async_call_in_progress_ = true; OnDisable(); } void ComponentBase::OnDisableComplete() { // Always post a task, to prevent calls to Disable() from within Enable(). task_runner_->PostTask(FROM_HERE, base::Bind(&ComponentBase::OnDisableCompleteInternal, base::Unretained(this))); } void ComponentBase::OnDisableCompleteInternal() { async_call_in_progress_ = false; DCHECK(state_ == kStateEnabling || state_ == kStateDisabling || state_ == kStateDestroying); if (state_ == kStateEnabling) { TryOnEnable(); return; } if (state_ == kStateDestroying) { StopUsingDependencies(); observers_->Notify(FROM_HERE, &Observer::OnComponentDisabled, this); state_ = kStateDisabled; delete this; } else { state_ = kStateDisabled; StopUsingDependencies(); observers_->Notify(FROM_HERE, &Observer::OnComponentDisabled, this); } } void ComponentBase::AddDependency(subtle::DependencyBase* dependency) { DCHECK_EQ(kStateDisabled, state_); DCHECK(!dependency->DependsOn(this)) << "Circular dependency detected"; strong_dependencies_.push_back(dependency); } void ComponentBase::StopUsingDependencies() { for (subtle::DependencyBase* dependency : strong_dependencies_) dependency->StopUsing(); } bool ComponentBase::DependsOn(ComponentBase* component) { for (subtle::DependencyBase* dependency : strong_dependencies_) { if (dependency->DependsOn(component)) return true; } return false; } } // namespace chromecast