| // Copyright (c) 2012 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. | 
 |  | 
 | #ifndef BASE_OBSERVER_LIST_THREADSAFE_H_ | 
 | #define BASE_OBSERVER_LIST_THREADSAFE_H_ | 
 |  | 
 | #include <unordered_map> | 
 |  | 
 | #include "base/base_export.h" | 
 | #include "base/bind.h" | 
 | #include "base/lazy_instance.h" | 
 | #include "base/location.h" | 
 | #include "base/logging.h" | 
 | #include "base/macros.h" | 
 | #include "base/memory/ref_counted.h" | 
 | #include "base/observer_list.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" | 
 | #include "base/threading/thread_local.h" | 
 | #include "build_config.h" | 
 |  | 
 | // TODO(fdoray): Removing these includes causes IWYU failures in other headers, | 
 | // remove them in a follow- up CL. | 
 | #include "base/memory/ptr_util.h" | 
 | #include "base/single_thread_task_runner.h" | 
 | #include "base/threading/thread_task_runner_handle.h" | 
 |  | 
 | /////////////////////////////////////////////////////////////////////////////// | 
 | // | 
 | // OVERVIEW: | 
 | // | 
 | //   A thread-safe container for a list of observers. This is similar to the | 
 | //   observer_list (see observer_list.h), but it is more robust for multi- | 
 | //   threaded situations. | 
 | // | 
 | //   The following use cases are supported: | 
 | //    * Observers can register for notifications from any sequence. They are | 
 | //      always notified on the sequence from which they were registered. | 
 | //    * Any sequence may trigger a notification via Notify(). | 
 | //    * Observers can remove themselves from the observer list inside of a | 
 | //      callback. | 
 | //    * If one sequence is notifying observers concurrently with an observer | 
 | //      removing itself from the observer list, the notifications will be | 
 | //      silently dropped. | 
 | // | 
 | //   The drawback of the threadsafe observer list is that notifications are not | 
 | //   as real-time as the non-threadsafe version of this class. Notifications | 
 | //   will always be done via PostTask() to another sequence, whereas with the | 
 | //   non-thread-safe observer_list, notifications happen synchronously. | 
 | // | 
 | /////////////////////////////////////////////////////////////////////////////// | 
 |  | 
 | namespace base { | 
 | namespace internal { | 
 |  | 
 | class BASE_EXPORT ObserverListThreadSafeBase | 
 |     : public RefCountedThreadSafe<ObserverListThreadSafeBase> { | 
 |  public: | 
 |   ObserverListThreadSafeBase() = default; | 
 |  | 
 |  protected: | 
 |   template <typename ObserverType, typename Method> | 
 |   struct Dispatcher; | 
 |  | 
 |   template <typename ObserverType, typename ReceiverType, typename... Params> | 
 |   struct Dispatcher<ObserverType, void (ReceiverType::*)(Params...)> { | 
 |     static void Run(void (ReceiverType::*m)(Params...), | 
 |                     Params... params, | 
 |                     ObserverType* obj) { | 
 |       (obj->*m)(std::forward<Params>(params)...); | 
 |     } | 
 |   }; | 
 |  | 
 |   struct NotificationDataBase { | 
 |     NotificationDataBase(void* observer_list_in, const Location& from_here_in) | 
 |         : observer_list(observer_list_in), from_here(from_here_in) {} | 
 |  | 
 |     void* observer_list; | 
 |     Location from_here; | 
 |   }; | 
 |  | 
 |   virtual ~ObserverListThreadSafeBase() = default; | 
 |  | 
 |   static LazyInstance<ThreadLocalPointer<const NotificationDataBase>>::Leaky | 
 |       tls_current_notification_; | 
 |  | 
 |  private: | 
 |   friend class RefCountedThreadSafe<ObserverListThreadSafeBase>; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(ObserverListThreadSafeBase); | 
 | }; | 
 |  | 
 | }  // namespace internal | 
 |  | 
 | template <class ObserverType> | 
 | class ObserverListThreadSafe : public internal::ObserverListThreadSafeBase { | 
 |  public: | 
 |   ObserverListThreadSafe() = default; | 
 |   explicit ObserverListThreadSafe(ObserverListPolicy policy) | 
 |       : policy_(policy) {} | 
 |  | 
 |   // Adds |observer| to the list. |observer| must not already be in the list. | 
 |   void AddObserver(ObserverType* observer) { | 
 |     // TODO(fdoray): Change this to a DCHECK once all call sites have a | 
 |     // SequencedTaskRunnerHandle. | 
 |     if (!SequencedTaskRunnerHandle::IsSet()) | 
 |       return; | 
 |  | 
 |     AutoLock auto_lock(lock_); | 
 |  | 
 |     // Add |observer| to the list of observers. | 
 |     DCHECK(!ContainsKey(observers_, observer)); | 
 |     const scoped_refptr<SequencedTaskRunner> task_runner = | 
 |         SequencedTaskRunnerHandle::Get(); | 
 |     observers_[observer] = task_runner; | 
 |  | 
 |     // If this is called while a notification is being dispatched on this thread | 
 |     // and |policy_| is ALL, |observer| must be notified (if a notification is | 
 |     // being dispatched on another thread in parallel, the notification may or | 
 |     // may not make it to |observer| depending on the outcome of the race to | 
 |     // |lock_|). | 
 |     if (policy_ == ObserverListPolicy::ALL) { | 
 |       const NotificationDataBase* current_notification = | 
 |           tls_current_notification_.Get().Get(); | 
 |       if (current_notification && current_notification->observer_list == this) { | 
 |         task_runner->PostTask( | 
 |             current_notification->from_here, | 
 |             BindOnce( | 
 |                 &ObserverListThreadSafe<ObserverType>::NotifyWrapper, this, | 
 |                 observer, | 
 |                 *static_cast<const NotificationData*>(current_notification))); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   // Remove an observer from the list if it is in the list. | 
 |   // | 
 |   // If a notification was sent to the observer but hasn't started to run yet, | 
 |   // it will be aborted. If a notification has started to run, removing the | 
 |   // observer won't stop it. | 
 |   void RemoveObserver(ObserverType* observer) { | 
 |     AutoLock auto_lock(lock_); | 
 |     observers_.erase(observer); | 
 |   } | 
 |  | 
 |   // Verifies that the list is currently empty (i.e. there are no observers). | 
 |   void AssertEmpty() const { | 
 | #if DCHECK_IS_ON() | 
 |     AutoLock auto_lock(lock_); | 
 |     DCHECK(observers_.empty()); | 
 | #endif | 
 |   } | 
 |  | 
 |   // Asynchronously invokes a callback on all observers, on their registration | 
 |   // sequence. You cannot assume that at the completion of the Notify call that | 
 |   // all Observers have been Notified. The notification may still be pending | 
 |   // delivery. | 
 |   template <typename Method, typename... Params> | 
 |   void Notify(const Location& from_here, Method m, Params&&... params) { | 
 |     Callback<void(ObserverType*)> method = | 
 |         Bind(&Dispatcher<ObserverType, Method>::Run, m, | 
 |              std::forward<Params>(params)...); | 
 |  | 
 |     AutoLock lock(lock_); | 
 |     for (const auto& observer : observers_) { | 
 |       observer.second->PostTask( | 
 |           from_here, | 
 |           BindOnce(&ObserverListThreadSafe<ObserverType>::NotifyWrapper, this, | 
 |                    observer.first, NotificationData(this, from_here, method))); | 
 |     } | 
 |   } | 
 |  | 
 |  private: | 
 |   friend class RefCountedThreadSafe<ObserverListThreadSafeBase>; | 
 |  | 
 |   struct NotificationData : public NotificationDataBase { | 
 |     NotificationData(ObserverListThreadSafe* observer_list_in, | 
 |                      const Location& from_here_in, | 
 |                      const Callback<void(ObserverType*)>& method_in) | 
 |         : NotificationDataBase(observer_list_in, from_here_in), | 
 |           method(method_in) {} | 
 |  | 
 |     Callback<void(ObserverType*)> method; | 
 |   }; | 
 |  | 
 |   ~ObserverListThreadSafe() override = default; | 
 |  | 
 |   void NotifyWrapper(ObserverType* observer, | 
 |                      const NotificationData& notification) { | 
 |     { | 
 |       AutoLock auto_lock(lock_); | 
 |  | 
 |       // Check whether the observer still needs a notification. | 
 |       auto it = observers_.find(observer); | 
 |       if (it == observers_.end()) | 
 |         return; | 
 |       DCHECK(it->second->RunsTasksInCurrentSequence()); | 
 |     } | 
 |  | 
 |     // Keep track of the notification being dispatched on the current thread. | 
 |     // This will be used if the callback below calls AddObserver(). | 
 |     // | 
 |     // Note: |tls_current_notification_| may not be nullptr if this runs in a | 
 |     // nested loop started by a notification callback. In that case, it is | 
 |     // important to save the previous value to restore it later. | 
 |     auto& tls_current_notification = tls_current_notification_.Get(); | 
 |     const NotificationDataBase* const previous_notification = | 
 |         tls_current_notification.Get(); | 
 |     tls_current_notification.Set(¬ification); | 
 |  | 
 |     // Invoke the callback. | 
 |     notification.method.Run(observer); | 
 |  | 
 |     // Reset the notification being dispatched on the current thread to its | 
 |     // previous value. | 
 |     tls_current_notification.Set(previous_notification); | 
 |   } | 
 |  | 
 |   const ObserverListPolicy policy_ = ObserverListPolicy::ALL; | 
 |  | 
 |   // Synchronizes access to |observers_|. | 
 |   mutable Lock lock_; | 
 |  | 
 |   // Keys are observers. Values are the SequencedTaskRunners on which they must | 
 |   // be notified. | 
 |   std::unordered_map<ObserverType*, scoped_refptr<SequencedTaskRunner>> | 
 |       observers_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(ObserverListThreadSafe); | 
 | }; | 
 |  | 
 | }  // namespace base | 
 |  | 
 | #endif  // BASE_OBSERVER_LIST_THREADSAFE_H_ |