// 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. // 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 { // public: // virtual ~MetricsRecorder() {} // virtual void RecordEvent(const std::string& event) = 0; // }; // // class SetupManager : public Component { // public: // virtual ~SetupManager() {} // virtual int GetMultizoneDelay() = 0; // }; // // class Multizone : public Component { // 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::ThreadTaskRunnerHandle::Get()->PostTask( // FROM_HERE, base::Bind(&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 #include "base/callback.h" #include "base/logging.h" #include "base/macros.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() {} }; 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 task_runner_; scoped_refptr counter_; std::vector 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> observers_; DISALLOW_COPY_AND_ASSIGN(ComponentBase); }; template class StrongDependency : public subtle::DependencyBase { public: StrongDependency(const WeakReference& dependency, ComponentBase* dependent) : subtle::DependencyBase(dependency, dependent) {} C* operator->() const { DCHECK(dependency_); return static_cast(dependency_); } private: DISALLOW_COPY_AND_ASSIGN(StrongDependency); }; template class WeakReference : public subtle::WeakReferenceBase { public: explicit WeakReference(const C& dependency) : WeakReferenceBase(dependency) {} explicit WeakReference(const StrongDependency& 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 Try() const { return subtle::Ref_DO_NOT_DECLARE(counter_); } }; template class Component : public ComponentBase { public: using WeakRef = WeakReference; using Dependency = StrongDependency; Component() = default; WeakRef GetRef() { return WeakRef(*static_cast(this)); } private: DISALLOW_COPY_AND_ASSIGN(Component); }; } // namespace chromecast #endif // CHROMECAST_BASE_COMPONENT_COMPONENT_H_