| // Copyright 2016 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // A component is a large, long-lived set of functionality that may be enabled |
| // or disabled at runtime. Some examples include: the multizone features, |
| // MetricsRecorder, or OpencastController. Components may depend on each other |
| // (ie, a component may call the public methods of other components); the |
| // Component infrastructure ensures that when a component is disabled, nothing |
| // that depends on it will call any of its methods until it is enabled again. |
| // |
| // Components may be used without a dependency relationship via a weak |
| // reference. A weak reference does not allow direct access to the component; |
| // instead, it must be either used to create a strict dependency (see below), or |
| // be converted to a scoped reference via Try(). Scoped references must be |
| // checked for validity before use (they are convertible to bool); an invalid |
| // scoped reference must not be used. Scoped references should be short-lived; |
| // to encourage this, they are only move-constructible and cannot be copied or |
| // assigned. |
| // |
| // If component Y depends on Component X, then Y has a Dependency reference |
| // to X. This causes Y to be disabled as X is being disabled (before X's |
| // OnDisable() method is called). Similarly, this dependency will cause X to be |
| // enabled when Y is being enabled (X will be enabled before Y's OnEnable() |
| // method is called). A component may freely access any of its dependencies |
| // as long as it is enabled. When a component is disabled, it must ensure that |
| // none of its dependencies will be used again until it is enabled. It is |
| // recommended to set up dependencies in your component's constructor; it is an |
| // error to add a dependency to a component that is not disabled. |
| // |
| // When a component is disabled, it will first recursively disable any other |
| // components that depend on it. It will also disable the creation of |
| // new scoped references. It then waits for all scoped references to be |
| // destroyed before calling OnDisable() to actually disable the component. |
| // |
| // Components MUST be disabled before they are deleted. For ease of use, a |
| // Destroy() method is provided. When Destroy() is called, it prevents the |
| // component from being enabled ever again, and then disables it, deleting it |
| // once it is disabled. |
| // |
| // Example usage: |
| // |
| // class MetricsRecorder : public Component<MetricsRecorder> { |
| // public: |
| // virtual ~MetricsRecorder() {} |
| // virtual void RecordEvent(const std::string& event) = 0; |
| // }; |
| // |
| // class SetupManager : public Component<SetupManager> { |
| // public: |
| // virtual ~SetupManager() {} |
| // virtual int GetMultizoneDelay() = 0; |
| // }; |
| // |
| // class Multizone : public Component<Multizone> { |
| // public: |
| // virtual ~Multizone() {} |
| // virtual void DoMultizoneStuff() = 0; |
| // }; |
| // |
| // class MetricsRecorderImpl : public MetricsRecorder { |
| // public: |
| // void OnEnable() override { |
| // // ... Enable metrics reporting ... |
| // OnEnableComplete(true); |
| // } |
| // |
| // // Release all resources; public methods will not be called after this. |
| // void OnDisable() override { |
| // OnDisableComplete(); |
| // } |
| // |
| // void RecordEvent(const std::string& event) override { |
| // // ... Record an event ... |
| // } |
| // }; |
| // |
| // class SetupManagerImpl : public SetupManager { |
| // public: |
| // void OnEnable() override { |
| // // ... Enable setup manager ... |
| // // OnEnableComplete() may be called asynchronously. |
| // base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| // FROM_HERE, base::BindOnce(&SetupManagerImpl::CompleteEnable, |
| // base::Unretained(this))); |
| // } |
| // |
| // void CompleteEnable() { |
| // OnEnableComplete(true); |
| // } |
| // |
| // void OnDisable() override { |
| // OnDisableComplete(); |
| // } |
| // |
| // int GetMultizoneDelay() override { return 0; } |
| // }; |
| // |
| // class MultizoneImpl : public Multizone { |
| // public: |
| // MultizoneImpl(const MetricsRecorder::WeakRef& metrics_recorder, |
| // const SetupManager::WeakRef& setup_manager) |
| // : metrics_recorder_(metrics_recorder, this), |
| // setup_manager_(setup_manager) { |
| // // We can try to use weak deps even before this component is enabled. |
| // // However, we MUST NOT attempt to use any strong dependencies. |
| // if (auto setup = setup_manager_.Try()) { |
| // int delay = setup->GetMultizoneDelay(); |
| // // ... Do something with delay ... |
| // } |
| // } |
| // |
| // void OnEnable() override { |
| // // ... Enable multizone ... |
| // // Can use strong dependencies directly |
| // metrics_recorder_->RecordEvent("enable multizone"); |
| // OnEnableComplete(); |
| // } |
| // |
| // void OnDisable() override { |
| // // Can still use strong dependencies here. However, this method MUST |
| // // ensure that strong dependencies will NOT be used after it returns. |
| // metrics_recorder_->RecordEvent("disable multizone"); |
| // OnDisableComplete(); |
| // } |
| // |
| // void DoMultizoneStuff() { |
| // metrics_recorder_->RecordEvent("multizone stuff"); |
| // // You have to Try() every time you use a weak dependency. |
| // if (auto setup = setup_manager_.Try()) { |
| // int delay = setup->GetMultizoneDelay(); |
| // // ... Do something with delay ... |
| // } |
| // } |
| // |
| // private: |
| // MetricsRecorder::Dependency metrics_recorder_; |
| // SetupManager::WeakRef setup_manager_; |
| // }; |
| |
| #ifndef CHROMECAST_BASE_COMPONENT_COMPONENT_H_ |
| #define CHROMECAST_BASE_COMPONENT_COMPONENT_H_ |
| |
| #include <vector> |
| |
| #include "base/check.h" |
| #include "base/functional/callback.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/observer_list_threadsafe.h" |
| #include "base/threading/thread_checker.h" |
| #include "chromecast/base/component/component_internal.h" |
| |
| namespace base { |
| class SingleThreadTaskRunner; |
| } // namespace base |
| |
| namespace chromecast { |
| |
| class ComponentBase { |
| public: |
| class Observer { |
| public: |
| // Called when a component finishes being enabled. If the component was |
| // enabled successfully, |success| will be |true|. Note that access to |
| // |component| is not guaranteed to be safe; since the observers are |
| // notified asynchronously, |component| may have been already deleted. |
| virtual void OnComponentEnabled(ComponentBase* component, bool success) {} |
| // Called when a component has been disabled. Access to |component| is not |
| // guaranteed to be safe. |
| virtual void OnComponentDisabled(ComponentBase* component) {} |
| |
| protected: |
| virtual ~Observer() {} |
| }; |
| |
| ComponentBase(const ComponentBase&) = delete; |
| ComponentBase& operator=(const ComponentBase&) = delete; |
| |
| virtual ~ComponentBase(); |
| |
| // Enables this component if possible. Attempts to enable all strong |
| // dependencies first. It is OK to call Disable() while the component is in |
| // the process of being enabled. All components MUST be created/enabled/ |
| // disabled/destroyed on the same thread. |
| // Note that enabling a component may occur asynchronously; components must |
| // always be accessed through a Dependency or WeakReference to ensure safety. |
| // TODO(kmackay) Consider allowing components to be used on any thread. |
| void Enable(); |
| |
| // Disables this component; disabling may complete asynchronously. It is OK to |
| // call Enable() again while the component is being disabled. Note that a |
| // component MUST be disabled (or never enabled) before it is deleted. |
| void Disable(); |
| |
| // Deletes this component, disabling it first if necessary. |
| void Destroy(); |
| |
| void AddObserver(Observer* observer); |
| void RemoveObserver(Observer* observer); |
| |
| protected: |
| ComponentBase(); |
| |
| // Enables the component implementation. This method must set things up so |
| // that any public method calls are valid, and then call OnEnableComplete(), |
| // passing in |true| if the enable was successful, |false| otherwise. |
| // OnEnableComplete() may be called from any thread. OnEnable() will not be |
| // called again until after the component has been disabled, and will not be |
| // called during an ongoing OnDisable() call (so if OnDisable() is called, |
| // then OnEnable() will not be called until OnDisableComplete() has been |
| // called). This method is called only on the thread that the component was |
| // created on. |
| virtual void OnEnable() = 0; |
| |
| // Disables the component implementation. This is not called until there are |
| // no more live dependencies, so there will be no more public method calls |
| // to the component until after OnEnable() is called again. This method must |
| // do whatever is necessary to ensure that no more calls to dependencies of |
| // this component will be made, and then call the |disabled_cb|. The |
| // |disabled_cb| may be called from any thread. This method is called only on |
| // the thread that the component was created on. |
| virtual void OnDisable() = 0; |
| |
| // Handles the success/failure of a call to OnEnable(). When OnEnable() is |
| // called, it must eventually call OnEnableComplete() (after the component is |
| // ready to be used by dependents), passing in |true| if the component was |
| // enabled successfully. If |success| is false, then OnDisable() will be |
| // called immediately to return the component to a consistent disabled state. |
| // May be called on any thread. |
| void OnEnableComplete(bool success); |
| |
| // Handles the completion of a call to OnDisable(). When OnDisable() is |
| // called, it must eventually call OnDisableComplete() (after ensuring that |
| // none of the component's strong dependencies will be used anymore). May be |
| // called on any thread. |
| void OnDisableComplete(); |
| |
| private: |
| friend class subtle::DependencyCount; |
| friend class subtle::DependencyBase; |
| friend class subtle::WeakReferenceBase; |
| |
| enum State { |
| kStateDisabled, |
| kStateDisabling, |
| kStateEnabled, |
| kStateEnabling, |
| kStateDestroying |
| }; |
| |
| void DependencyReady(); |
| void TryOnEnable(); |
| void OnEnableCompleteInternal(bool success); |
| void DependencyCountDisableComplete(); |
| void TryOnDisable(); |
| void OnDisableCompleteInternal(); |
| void AddDependency(subtle::DependencyBase* dependency); |
| void StopUsingDependencies(); |
| // Returns |true| if |component| is a transitive dependency of this component. |
| bool DependsOn(ComponentBase* component); |
| |
| const scoped_refptr<base::SingleThreadTaskRunner> task_runner_; |
| scoped_refptr<subtle::DependencyCount> counter_; |
| std::vector<subtle::DependencyBase*> strong_dependencies_; |
| State state_; |
| // |true| when a call to OnEnable()/OnDisable() is in progress. |
| bool async_call_in_progress_; |
| int pending_dependency_count_; |
| const scoped_refptr<base::ObserverListThreadSafe<Observer>> observers_; |
| }; |
| |
| template <typename C> |
| class StrongDependency : public subtle::DependencyBase { |
| public: |
| StrongDependency(const WeakReference<C>& dependency, ComponentBase* dependent) |
| : subtle::DependencyBase(dependency, dependent) {} |
| |
| StrongDependency(const StrongDependency&) = delete; |
| StrongDependency& operator=(const StrongDependency&) = delete; |
| |
| C* operator->() const { |
| DCHECK(dependency_); |
| return static_cast<C*>(dependency_); |
| } |
| }; |
| |
| template <typename C> |
| class WeakReference : public subtle::WeakReferenceBase { |
| public: |
| explicit WeakReference(const C& dependency) : WeakReferenceBase(dependency) {} |
| explicit WeakReference(const StrongDependency<C>& dependency) |
| : subtle::WeakReferenceBase(dependency) {} |
| |
| // Explicitly allow copy. |
| WeakReference(const WeakReference& other) = default; |
| WeakReference(WeakReference&& other) = default; |
| |
| // Disallow assignment. |
| void operator=(const WeakReference&) = delete; |
| |
| // Try to get a scoped reference. Expected usage: |
| // if (auto ref = weak.Try()) { |
| // // ... use ref ... |
| // } |
| subtle::Ref_DO_NOT_DECLARE<C> Try() const { |
| return subtle::Ref_DO_NOT_DECLARE<C>(counter_); |
| } |
| }; |
| |
| template <typename C> |
| class Component : public ComponentBase { |
| public: |
| using WeakRef = WeakReference<C>; |
| using Dependency = StrongDependency<C>; |
| |
| Component() = default; |
| |
| Component(const Component&) = delete; |
| Component& operator=(const Component&) = delete; |
| |
| WeakRef GetRef() { return WeakRef(*static_cast<C*>(this)); } |
| }; |
| |
| } // namespace chromecast |
| |
| #endif // CHROMECAST_BASE_COMPONENT_COMPONENT_H_ |