security/sandbox/chromium/base/observer_list_threadsafe.h

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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.
     5 #ifndef BASE_OBSERVER_LIST_THREADSAFE_H_
     6 #define BASE_OBSERVER_LIST_THREADSAFE_H_
     8 #include <algorithm>
     9 #include <map>
    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"
    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 ///////////////////////////////////////////////////////////////////////////////
    55 // Forward declaration for ObserverListThreadSafeTraits.
    56 template <class ObserverType>
    57 class ObserverListThreadSafe;
    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 };
    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 };
    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;
   102   ObserverListThreadSafe()
   103       : type_(ObserverListBase<ObserverType>::NOTIFY_ALL) {}
   104   explicit ObserverListThreadSafe(NotificationType type) : type_(type) {}
   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;
   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   }
   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;
   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);
   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   }
   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   }
   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   }
   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   }
   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   }
   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   }
   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   }
   203   // TODO(mbelshe):  Add more wrappers for Notify() with more arguments.
   205  private:
   206   // See comment above ObserverListThreadSafeTraits' definition.
   207   friend struct ObserverListThreadSafeTraits<ObserverType>;
   209   struct ObserverListContext {
   210     explicit ObserverListContext(NotificationType type)
   211         : loop(base::MessageLoopProxy::current()),
   212           list(type) {
   213     }
   215     scoped_refptr<base::MessageLoopProxy> loop;
   216     ObserverList<ObserverType> list;
   218     DISALLOW_COPY_AND_ASSIGN(ObserverListContext);
   219   };
   221   ~ObserverListThreadSafe() {
   222     STLDeleteValues(&observer_lists_);
   223   }
   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   }
   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) {
   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());
   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     }
   259     {
   260       typename ObserverList<ObserverType>::Iterator it(context->list);
   261       ObserverType* obs;
   262       while ((obs = it.GetNext()) != NULL)
   263         method.Run(obs);
   264     }
   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   }
   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;
   288   mutable base::Lock list_lock_;  // Protects the observer_lists_.
   289   ObserversListMap observer_lists_;
   290   const NotificationType type_;
   292   DISALLOW_COPY_AND_ASSIGN(ObserverListThreadSafe);
   293 };
   295 #endif  // BASE_OBSERVER_LIST_THREADSAFE_H_

mercurial