Home | History | Annotate | Download | only in base
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #ifndef BASE_OBSERVER_LIST_THREADSAFE_H_
      6 #define BASE_OBSERVER_LIST_THREADSAFE_H_
      7 
      8 #include <algorithm>
      9 #include <map>
     10 
     11 #include "base/basictypes.h"
     12 #include "base/bind.h"
     13 #include "base/location.h"
     14 #include "base/logging.h"
     15 #include "base/memory/ref_counted.h"
     16 #include "base/message_loop/message_loop.h"
     17 #include "base/message_loop/message_loop_proxy.h"
     18 #include "base/observer_list.h"
     19 #include "base/stl_util.h"
     20 #include "base/threading/platform_thread.h"
     21 
     22 ///////////////////////////////////////////////////////////////////////////////
     23 //
     24 // OVERVIEW:
     25 //
     26 //   A thread-safe container for a list of observers.
     27 //   This is similar to the observer_list (see observer_list.h), but it
     28 //   is more robust for multi-threaded situations.
     29 //
     30 //   The following use cases are supported:
     31 //    * Observers can register for notifications from any thread.
     32 //      Callbacks to the observer will occur on the same thread where
     33 //      the observer initially called AddObserver() from.
     34 //    * Any thread may trigger a notification via Notify().
     35 //    * Observers can remove themselves from the observer list inside
     36 //      of a callback.
     37 //    * If one thread is notifying observers concurrently with an observer
     38 //      removing itself from the observer list, the notifications will
     39 //      be silently dropped.
     40 //
     41 //   The drawback of the threadsafe observer list is that notifications
     42 //   are not as real-time as the non-threadsafe version of this class.
     43 //   Notifications will always be done via PostTask() to another thread,
     44 //   whereas with the non-thread-safe observer_list, notifications happen
     45 //   synchronously and immediately.
     46 //
     47 //   IMPLEMENTATION NOTES
     48 //   The ObserverListThreadSafe maintains an ObserverList for each thread
     49 //   which uses the ThreadSafeObserver.  When Notifying the observers,
     50 //   we simply call PostTask to each registered thread, and then each thread
     51 //   will notify its regular ObserverList.
     52 //
     53 ///////////////////////////////////////////////////////////////////////////////
     54 
     55 // Forward declaration for ObserverListThreadSafeTraits.
     56 template <class ObserverType>
     57 class ObserverListThreadSafe;
     58 
     59 // An UnboundMethod is a wrapper for a method where the actual object is
     60 // provided at Run dispatch time.
     61 template <class T, class Method, class Params>
     62 class UnboundMethod {
     63  public:
     64   UnboundMethod(Method m, const Params& p) : m_(m), p_(p) {
     65     COMPILE_ASSERT(
     66         (base::internal::ParamsUseScopedRefptrCorrectly<Params>::value),
     67         badunboundmethodparams);
     68   }
     69   void Run(T* obj) const {
     70     DispatchToMethod(obj, m_, p_);
     71   }
     72  private:
     73   Method m_;
     74   Params p_;
     75 };
     76 
     77 // This class is used to work around VS2005 not accepting:
     78 //
     79 // friend class
     80 //     base::RefCountedThreadSafe<ObserverListThreadSafe<ObserverType> >;
     81 //
     82 // Instead of friending the class, we could friend the actual function
     83 // which calls delete.  However, this ends up being
     84 // RefCountedThreadSafe::DeleteInternal(), which is private.  So we
     85 // define our own templated traits class so we can friend it.
     86 template <class T>
     87 struct ObserverListThreadSafeTraits {
     88   static void Destruct(const ObserverListThreadSafe<T>* x) {
     89     delete x;
     90   }
     91 };
     92 
     93 template <class ObserverType>
     94 class ObserverListThreadSafe
     95     : public base::RefCountedThreadSafe<
     96         ObserverListThreadSafe<ObserverType>,
     97         ObserverListThreadSafeTraits<ObserverType> > {
     98  public:
     99   typedef typename ObserverList<ObserverType>::NotificationType
    100       NotificationType;
    101 
    102   ObserverListThreadSafe()
    103       : type_(ObserverListBase<ObserverType>::NOTIFY_ALL) {}
    104   explicit ObserverListThreadSafe(NotificationType type) : type_(type) {}
    105 
    106   // Add an observer to the list.  An observer should not be added to
    107   // the same list more than once.
    108   void AddObserver(ObserverType* obs) {
    109     // If there is not a current MessageLoop, it is impossible to notify on it,
    110     // so do not add the observer.
    111     if (!base::MessageLoop::current())
    112       return;
    113 
    114     ObserverList<ObserverType>* list = NULL;
    115     base::PlatformThreadId thread_id = base::PlatformThread::CurrentId();
    116     {
    117       base::AutoLock lock(list_lock_);
    118       if (observer_lists_.find(thread_id) == observer_lists_.end())
    119         observer_lists_[thread_id] = new ObserverListContext(type_);
    120       list = &(observer_lists_[thread_id]->list);
    121     }
    122     list->AddObserver(obs);
    123   }
    124 
    125   // Remove an observer from the list if it is in the list.
    126   // If there are pending notifications in-transit to the observer, they will
    127   // be aborted.
    128   // If the observer to be removed is in the list, RemoveObserver MUST
    129   // be called from the same thread which called AddObserver.
    130   void RemoveObserver(ObserverType* obs) {
    131     ObserverListContext* context = NULL;
    132     ObserverList<ObserverType>* list = NULL;
    133     base::PlatformThreadId thread_id = base::PlatformThread::CurrentId();
    134     {
    135       base::AutoLock lock(list_lock_);
    136       typename ObserversListMap::iterator it = observer_lists_.find(thread_id);
    137       if (it == observer_lists_.end()) {
    138         // This will happen if we try to remove an observer on a thread
    139         // we never added an observer for.
    140         return;
    141       }
    142       context = it->second;
    143       list = &context->list;
    144 
    145       // If we're about to remove the last observer from the list,
    146       // then we can remove this observer_list entirely.
    147       if (list->HasObserver(obs) && list->size() == 1)
    148         observer_lists_.erase(it);
    149     }
    150     list->RemoveObserver(obs);
    151 
    152     // If RemoveObserver is called from a notification, the size will be
    153     // nonzero.  Instead of deleting here, the NotifyWrapper will delete
    154     // when it finishes iterating.
    155     if (list->size() == 0)
    156       delete context;
    157   }
    158 
    159   // Verifies that the list is currently empty (i.e. there are no observers).
    160   void AssertEmpty() const {
    161     base::AutoLock lock(list_lock_);
    162     DCHECK(observer_lists_.empty());
    163   }
    164 
    165   // Notify methods.
    166   // Make a thread-safe callback to each Observer in the list.
    167   // Note, these calls are effectively asynchronous.  You cannot assume
    168   // that at the completion of the Notify call that all Observers have
    169   // been Notified.  The notification may still be pending delivery.
    170   template <class Method>
    171   void Notify(Method m) {
    172     UnboundMethod<ObserverType, Method, Tuple0> method(m, MakeTuple());
    173     Notify<Method, Tuple0>(method);
    174   }
    175 
    176   template <class Method, class A>
    177   void Notify(Method m, const A& a) {
    178     UnboundMethod<ObserverType, Method, Tuple1<A> > method(m, MakeTuple(a));
    179     Notify<Method, Tuple1<A> >(method);
    180   }
    181 
    182   template <class Method, class A, class B>
    183   void Notify(Method m, const A& a, const B& b) {
    184     UnboundMethod<ObserverType, Method, Tuple2<A, B> > method(
    185         m, MakeTuple(a, b));
    186     Notify<Method, Tuple2<A, B> >(method);
    187   }
    188 
    189   template <class Method, class A, class B, class C>
    190   void Notify(Method m, const A& a, const B& b, const C& c) {
    191     UnboundMethod<ObserverType, Method, Tuple3<A, B, C> > method(
    192         m, MakeTuple(a, b, c));
    193     Notify<Method, Tuple3<A, B, C> >(method);
    194   }
    195 
    196   template <class Method, class A, class B, class C, class D>
    197   void Notify(Method m, const A& a, const B& b, const C& c, const D& d) {
    198     UnboundMethod<ObserverType, Method, Tuple4<A, B, C, D> > method(
    199         m, MakeTuple(a, b, c, d));
    200     Notify<Method, Tuple4<A, B, C, D> >(method);
    201   }
    202 
    203   // TODO(mbelshe):  Add more wrappers for Notify() with more arguments.
    204 
    205  private:
    206   // See comment above ObserverListThreadSafeTraits' definition.
    207   friend struct ObserverListThreadSafeTraits<ObserverType>;
    208 
    209   struct ObserverListContext {
    210     explicit ObserverListContext(NotificationType type)
    211         : loop(base::MessageLoopProxy::current()),
    212           list(type) {
    213     }
    214 
    215     scoped_refptr<base::MessageLoopProxy> loop;
    216     ObserverList<ObserverType> list;
    217 
    218     DISALLOW_COPY_AND_ASSIGN(ObserverListContext);
    219   };
    220 
    221   ~ObserverListThreadSafe() {
    222     STLDeleteValues(&observer_lists_);
    223   }
    224 
    225   template <class Method, class Params>
    226   void Notify(const UnboundMethod<ObserverType, Method, Params>& method) {
    227     base::AutoLock lock(list_lock_);
    228     typename ObserversListMap::iterator it;
    229     for (it = observer_lists_.begin(); it != observer_lists_.end(); ++it) {
    230       ObserverListContext* context = (*it).second;
    231       context->loop->PostTask(
    232           FROM_HERE,
    233           base::Bind(&ObserverListThreadSafe<ObserverType>::
    234               template NotifyWrapper<Method, Params>, this, context, method));
    235     }
    236   }
    237 
    238   // Wrapper which is called to fire the notifications for each thread's
    239   // ObserverList.  This function MUST be called on the thread which owns
    240   // the unsafe ObserverList.
    241   template <class Method, class Params>
    242   void NotifyWrapper(ObserverListContext* context,
    243       const UnboundMethod<ObserverType, Method, Params>& method) {
    244 
    245     // Check that this list still needs notifications.
    246     {
    247       base::AutoLock lock(list_lock_);
    248       typename ObserversListMap::iterator it =
    249           observer_lists_.find(base::PlatformThread::CurrentId());
    250 
    251       // The ObserverList could have been removed already.  In fact, it could
    252       // have been removed and then re-added!  If the master list's loop
    253       // does not match this one, then we do not need to finish this
    254       // notification.
    255       if (it == observer_lists_.end() || it->second != context)
    256         return;
    257     }
    258 
    259     {
    260       typename ObserverList<ObserverType>::Iterator it(context->list);
    261       ObserverType* obs;
    262       while ((obs = it.GetNext()) != NULL)
    263         method.Run(obs);
    264     }
    265 
    266     // If there are no more observers on the list, we can now delete it.
    267     if (context->list.size() == 0) {
    268       {
    269         base::AutoLock lock(list_lock_);
    270         // Remove |list| if it's not already removed.
    271         // This can happen if multiple observers got removed in a notification.
    272         // See http://crbug.com/55725.
    273         typename ObserversListMap::iterator it =
    274             observer_lists_.find(base::PlatformThread::CurrentId());
    275         if (it != observer_lists_.end() && it->second == context)
    276           observer_lists_.erase(it);
    277       }
    278       delete context;
    279     }
    280   }
    281 
    282   // Key by PlatformThreadId because in tests, clients can attempt to remove
    283   // observers without a MessageLoop. If this were keyed by MessageLoop, that
    284   // operation would be silently ignored, leaving garbage in the ObserverList.
    285   typedef std::map<base::PlatformThreadId, ObserverListContext*>
    286       ObserversListMap;
    287 
    288   mutable base::Lock list_lock_;  // Protects the observer_lists_.
    289   ObserversListMap observer_lists_;
    290   const NotificationType type_;
    291 
    292   DISALLOW_COPY_AND_ASSIGN(ObserverListThreadSafe);
    293 };
    294 
    295 #endif  // BASE_OBSERVER_LIST_THREADSAFE_H_
    296