| // Copyright 2017 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. |
| |
| // Safe Observer/Observable implementation. |
| // |
| // When using ObserverListThreadSafe, we were running into issues since there |
| // was no synchronization between getting the existing value, and registering |
| // as an observer. See go/cast-platform-design-synchronized-value for more |
| // details. |
| // |
| // To fix this issue, and to make observing values safer and simpler in general, |
| // use the Observable/Observer pattern in this file. When you have a value that |
| // other code wants to observe (ie, get the value of and receive any changes), |
| // wrap that value in an Observable<T>. The value type T must be copyable and |
| // copy-assignable. The Observable must be constructed with the initial observed |
| // value, and the value may be updated at any time from any thread by calling |
| // SetValue(). You can also get the value using GetValue(); but note that this |
| // is not threadsafe (the value is returned without locking), so the caller must |
| // ensure safety by other means. Calling GetValueThreadSafe() is threadsafe but |
| // involves a mutex lock. |
| // |
| // Code that wants to observe the value calls Observe() on it at any point when |
| // the value is alive. Note that Observe() may be called safely from any thread. |
| // Observe() returns an Observer<T> instance, which MUST be used and destroyed |
| // only on the thread that called Observe(). The Observer initially contains the |
| // value that the Observable had when Observe() was called, and that value will |
| // be updated asynchronously whenever the Observable's SetValue() method is |
| // is called. NOTE: the initial value of the Observer is the value known to the |
| // thread that created the Observer at the time; there may be an updated value |
| // from the Observable that hasn't been handled by the Observer's thread yet. |
| // |
| // The Observer's view of the observed value is returned by GetValue(); this is |
| // a low-cost call since there is no locking (the value is updated on the thread |
| // that constructed the Observer). Note that Observers are always updated |
| // asynchronously with PostTask(), even if they belong to the same thread that |
| // calls SetValue(). All Observers on the same thread have the same consistent |
| // view of the observed value. |
| // |
| // Observers may be copied freely; the copy also observes the original |
| // Observable, and belongs to the thread that created the copy. Copying is safe |
| // even when the original Observable has been destroyed. |
| // |
| // Code may register a callback that is called whenever an Observer's value is |
| // updated, by calling SetOnUpdateCallback(). If you get an Observer by calling |
| // Observe() and then immediately call SetOnUpdateCallback() to register a |
| // a callback, you are guaranteed to get every value of the Observable starting |
| // from when you called Observe() - you get the initial value by calling |
| // GetValue() on the returned Observer, and any subsequent updates will trigger |
| // the callback so you can call GetValue() to get the new value. You will not |
| // receive any extra callbacks (exactly one callback per value update). |
| // |
| // Note that Observers are not default-constructible, since there is no way to |
| // construct it in a default state. In cases where you need to instantiate an |
| // Observer after your constructor, you can use a std::unique_ptr<Observer> |
| // instead, and initialize it when needed. |
| // |
| // Example usage: |
| // |
| // class MediaManager { |
| // public: |
| // MediaManager() : volume_(0.0f) {} |
| // |
| // Observer<float> ObserveVolume() { return volume_.Observe(); } |
| // |
| // // ... other methods ... |
| // |
| // private: |
| // // Assume this is called from some other internal code when the volume is |
| // // updated. |
| // void OnUpdateVolume(float new_volume) { |
| // volume_.SetValue(new_volume); // All observers will get the new value. |
| // } |
| // |
| // Observable<float> volume_; |
| // } |
| // |
| // class VolumeFeedbackManager { |
| // public: |
| // VolumeFeedbackManager(MediaManager* media_manager) |
| // : volume_observer_(media_manager->ObserveVolume()) { |
| // volume_observer_.SetOnUpdateCallback( |
| // base::BindRepeating(&VolumeFeedbackManager::OnVolumeChanged, |
| // base::Unretained(this))); |
| // } |
| // |
| // private: |
| // void OnVolumeChanged() { |
| // ShowVolumeFeedback(volume_observer_.GetValue()); |
| // } |
| // |
| // void ShowVolumeFeedback(float volume) { |
| // // ... some implementation ... |
| // } |
| // }; |
| // |
| |
| #ifndef CHROMECAST_BASE_OBSERVER_H_ |
| #define CHROMECAST_BASE_OBSERVER_H_ |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <memory> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/callback.h" |
| #include "base/location.h" |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/sequence_checker.h" |
| #include "base/sequenced_task_runner.h" |
| #include "base/stl_util.h" |
| #include "base/synchronization/lock.h" |
| #include "base/threading/sequenced_task_runner_handle.h" |
| |
| namespace chromecast { |
| |
| namespace subtle { |
| template <typename T> |
| class ObservableInternals; |
| } // namespace subtle |
| |
| template <typename T> |
| class Observable; |
| |
| template <typename T> |
| class Observer { |
| public: |
| Observer(const Observer& other); |
| |
| ~Observer(); |
| |
| void SetOnUpdateCallback(base::RepeatingClosure callback) { |
| on_update_callback_ = std::move(callback); |
| } |
| |
| const T& GetValue() const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return value_; |
| } |
| |
| private: |
| friend class subtle::ObservableInternals<T>; |
| friend class Observable<T>; |
| |
| explicit Observer(scoped_refptr<subtle::ObservableInternals<T>> internals); |
| |
| void OnUpdate(); |
| |
| const scoped_refptr<subtle::ObservableInternals<T>> internals_; |
| // Note: value_ is a const ref to the value copy for this sequence, stored in |
| // SequenceOwnedInfo. |
| const T& value_; |
| base::RepeatingClosure on_update_callback_; |
| SEQUENCE_CHECKER(sequence_checker_); |
| |
| DISALLOW_ASSIGN(Observer); |
| }; |
| |
| template <typename T> |
| class Observable { |
| static_assert(std::is_copy_constructible<T>::value, |
| "Observable values must be copyable"); |
| static_assert(std::is_copy_assignable<T>::value, |
| "Observable values must be copy-assignable"); |
| |
| public: |
| explicit Observable(const T& initial_value); |
| Observer<T> Observe(); |
| |
| void SetValue(const T& new_value); |
| const T& GetValue() const; // NOT threadsafe! |
| T GetValueThreadSafe() const; |
| |
| private: |
| // By using a refcounted object to store the value and observer list, we can |
| // avoid tying the lifetime of Observable to its Observers or vice versa. |
| const scoped_refptr<subtle::ObservableInternals<T>> internals_; |
| |
| DISALLOW_COPY_AND_ASSIGN(Observable); |
| }; |
| |
| namespace subtle { |
| |
| template <typename T> |
| class ObservableInternals |
| : public base::RefCountedThreadSafe<ObservableInternals<T>> { |
| public: |
| explicit ObservableInternals(const T& initial_value) |
| : value_(initial_value) {} |
| |
| void SetValue(const T& new_value) { |
| base::AutoLock lock(lock_); |
| value_ = new_value; |
| |
| for (auto& item : per_sequence_) { |
| item.SetValue(new_value); |
| } |
| } |
| |
| const T& GetValue() const { return value_; } |
| |
| T GetValueThreadSafe() const { |
| base::AutoLock lock(lock_); |
| return value_; |
| } |
| |
| const T& AddObserver(Observer<T>* observer) { |
| DCHECK(observer); |
| DCHECK(base::SequencedTaskRunnerHandle::IsSet()); |
| auto task_runner = base::SequencedTaskRunnerHandle::Get(); |
| |
| base::AutoLock lock(lock_); |
| auto it = per_sequence_.begin(); |
| while (it != per_sequence_.end() && it->task_runner() != task_runner) { |
| ++it; |
| } |
| if (it == per_sequence_.end()) { |
| per_sequence_.emplace_back(std::move(task_runner), value_); |
| it = --per_sequence_.end(); |
| } |
| it->AddObserver(observer); |
| return it->value(); |
| } |
| |
| void RemoveObserver(Observer<T>* observer) { |
| DCHECK(observer); |
| DCHECK(base::SequencedTaskRunnerHandle::IsSet()); |
| auto task_runner = base::SequencedTaskRunnerHandle::Get(); |
| |
| base::AutoLock lock(lock_); |
| for (size_t i = 0; i < per_sequence_.size(); ++i) { |
| if (per_sequence_[i].task_runner() == task_runner) { |
| per_sequence_[i].RemoveObserver(observer); |
| |
| if (per_sequence_[i].Empty()) { |
| per_sequence_[i].Swap(per_sequence_.back()); |
| per_sequence_.pop_back(); |
| } |
| return; |
| } |
| } |
| |
| NOTREACHED() << "Tried to remove observer from unknown task runner"; |
| } |
| |
| private: |
| // Information owned by a particular sequence. Must be only accessed on that |
| // sequence, and must be deleted by posting a task to that sequence. |
| // This class MUST NOT contain a scoped_refptr to the task_runner, since if it |
| // did, there would be a reference cycle during cleanup, when the task to |
| // Destroy() is posted. |
| class SequenceOwnedInfo { |
| public: |
| explicit SequenceOwnedInfo(const T& value) : value_(value) {} |
| |
| const T& value() const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return value_; |
| } |
| |
| void AddObserver(Observer<T>* observer) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK(observer); |
| DCHECK(!base::ContainsValue(observers_, observer)); |
| observers_.push_back(observer); |
| } |
| |
| void RemoveObserver(Observer<T>* observer) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK(observer); |
| DCHECK(base::ContainsValue(observers_, observer)); |
| observers_.erase( |
| std::remove(observers_.begin(), observers_.end(), observer), |
| observers_.end()); |
| } |
| |
| bool Empty() const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return observers_.empty(); |
| } |
| |
| void SetValue(const T& value) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| value_ = value; |
| for (auto* obs : observers_) { |
| obs->OnUpdate(); |
| } |
| } |
| |
| static void Destroy(std::unique_ptr<SequenceOwnedInfo> self) { |
| // The unique_ptr deletes automatically. |
| } |
| |
| private: |
| std::vector<Observer<T>*> observers_; |
| T value_; |
| SEQUENCE_CHECKER(sequence_checker_); |
| |
| DISALLOW_COPY_AND_ASSIGN(SequenceOwnedInfo); |
| }; |
| |
| class PerSequenceInfo { |
| public: |
| PerSequenceInfo(scoped_refptr<base::SequencedTaskRunner> task_runner, |
| const T& value) |
| : task_runner_(std::move(task_runner)), |
| owned_info_(std::make_unique<SequenceOwnedInfo>(value)) {} |
| |
| PerSequenceInfo(PerSequenceInfo&& other) = default; |
| |
| ~PerSequenceInfo() { |
| if (!owned_info_) { |
| // Members have been moved out via move constructor. |
| return; |
| } |
| |
| DCHECK(Empty()); |
| // Must post a task to delete the owned info, since there may still be a |
| // pending task to call SequenceOwnedInfo::SetValue(). |
| // Use manual PostNonNestableTask(), since DeleteSoon() does not |
| // guarantee deletion. |
| task_runner_->PostNonNestableTask( |
| FROM_HERE, |
| base::BindOnce(&SequenceOwnedInfo::Destroy, std::move(owned_info_))); |
| } |
| |
| const T& value() const { return owned_info_->value(); } |
| |
| const base::SequencedTaskRunner* task_runner() const { |
| return task_runner_.get(); |
| } |
| |
| void AddObserver(Observer<T>* observer) { |
| owned_info_->AddObserver(observer); |
| } |
| |
| void RemoveObserver(Observer<T>* observer) { |
| owned_info_->RemoveObserver(observer); |
| } |
| |
| bool Empty() const { return owned_info_->Empty(); } |
| |
| void Swap(PerSequenceInfo& other) { |
| std::swap(task_runner_, other.task_runner_); |
| std::swap(owned_info_, other.owned_info_); |
| } |
| |
| void SetValue(const T& value) { |
| task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&SequenceOwnedInfo::SetValue, |
| base::Unretained(owned_info_.get()), value)); |
| } |
| |
| private: |
| // Operations on |owned_info| do not need to be synchronized with a lock, |
| // since all operations must occur on |task_runner|. |
| scoped_refptr<base::SequencedTaskRunner> task_runner_; |
| std::unique_ptr<SequenceOwnedInfo> owned_info_; |
| }; |
| |
| friend class base::RefCountedThreadSafe<ObservableInternals>; |
| ~ObservableInternals() {} |
| |
| mutable base::Lock lock_; |
| T value_; |
| std::vector<PerSequenceInfo> per_sequence_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ObservableInternals); |
| }; |
| |
| } // namespace subtle |
| |
| template <typename T> |
| Observer<T>::Observer(scoped_refptr<subtle::ObservableInternals<T>> internals) |
| : internals_(std::move(internals)), value_(internals_->AddObserver(this)) {} |
| |
| template <typename T> |
| Observer<T>::Observer(const Observer& other) : Observer(other.internals_) {} |
| |
| template <typename T> |
| Observer<T>::~Observer() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| internals_->RemoveObserver(this); |
| } |
| |
| template <typename T> |
| void Observer<T>::OnUpdate() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (on_update_callback_) { |
| on_update_callback_.Run(); |
| } |
| } |
| |
| template <typename T> |
| Observable<T>::Observable(const T& initial_value) |
| : internals_(base::WrapRefCounted( |
| new subtle::ObservableInternals<T>(initial_value))) {} |
| |
| template <typename T> |
| Observer<T> Observable<T>::Observe() { |
| return Observer<T>(internals_); |
| } |
| |
| template <typename T> |
| void Observable<T>::SetValue(const T& new_value) { |
| internals_->SetValue(new_value); |
| } |
| |
| template <typename T> |
| const T& Observable<T>::GetValue() const { |
| return internals_->GetValue(); |
| } |
| |
| template <typename T> |
| T Observable<T>::GetValueThreadSafe() const { |
| return internals_->GetValueThreadSafe(); |
| } |
| |
| } // namespace chromecast |
| |
| #endif // CHROMECAST_BASE_OBSERVER_H_ |