security/sandbox/chromium/base/observer_list_threadsafe.h

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/security/sandbox/chromium/base/observer_list_threadsafe.h	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,295 @@
     1.4 +// Copyright (c) 2012 The Chromium Authors. All rights reserved.
     1.5 +// Use of this source code is governed by a BSD-style license that can be
     1.6 +// found in the LICENSE file.
     1.7 +
     1.8 +#ifndef BASE_OBSERVER_LIST_THREADSAFE_H_
     1.9 +#define BASE_OBSERVER_LIST_THREADSAFE_H_
    1.10 +
    1.11 +#include <algorithm>
    1.12 +#include <map>
    1.13 +
    1.14 +#include "base/basictypes.h"
    1.15 +#include "base/bind.h"
    1.16 +#include "base/location.h"
    1.17 +#include "base/logging.h"
    1.18 +#include "base/memory/ref_counted.h"
    1.19 +#include "base/message_loop/message_loop.h"
    1.20 +#include "base/message_loop/message_loop_proxy.h"
    1.21 +#include "base/observer_list.h"
    1.22 +#include "base/stl_util.h"
    1.23 +#include "base/threading/platform_thread.h"
    1.24 +
    1.25 +///////////////////////////////////////////////////////////////////////////////
    1.26 +//
    1.27 +// OVERVIEW:
    1.28 +//
    1.29 +//   A thread-safe container for a list of observers.
    1.30 +//   This is similar to the observer_list (see observer_list.h), but it
    1.31 +//   is more robust for multi-threaded situations.
    1.32 +//
    1.33 +//   The following use cases are supported:
    1.34 +//    * Observers can register for notifications from any thread.
    1.35 +//      Callbacks to the observer will occur on the same thread where
    1.36 +//      the observer initially called AddObserver() from.
    1.37 +//    * Any thread may trigger a notification via Notify().
    1.38 +//    * Observers can remove themselves from the observer list inside
    1.39 +//      of a callback.
    1.40 +//    * If one thread is notifying observers concurrently with an observer
    1.41 +//      removing itself from the observer list, the notifications will
    1.42 +//      be silently dropped.
    1.43 +//
    1.44 +//   The drawback of the threadsafe observer list is that notifications
    1.45 +//   are not as real-time as the non-threadsafe version of this class.
    1.46 +//   Notifications will always be done via PostTask() to another thread,
    1.47 +//   whereas with the non-thread-safe observer_list, notifications happen
    1.48 +//   synchronously and immediately.
    1.49 +//
    1.50 +//   IMPLEMENTATION NOTES
    1.51 +//   The ObserverListThreadSafe maintains an ObserverList for each thread
    1.52 +//   which uses the ThreadSafeObserver.  When Notifying the observers,
    1.53 +//   we simply call PostTask to each registered thread, and then each thread
    1.54 +//   will notify its regular ObserverList.
    1.55 +//
    1.56 +///////////////////////////////////////////////////////////////////////////////
    1.57 +
    1.58 +// Forward declaration for ObserverListThreadSafeTraits.
    1.59 +template <class ObserverType>
    1.60 +class ObserverListThreadSafe;
    1.61 +
    1.62 +// An UnboundMethod is a wrapper for a method where the actual object is
    1.63 +// provided at Run dispatch time.
    1.64 +template <class T, class Method, class Params>
    1.65 +class UnboundMethod {
    1.66 + public:
    1.67 +  UnboundMethod(Method m, const Params& p) : m_(m), p_(p) {
    1.68 +    COMPILE_ASSERT(
    1.69 +        (base::internal::ParamsUseScopedRefptrCorrectly<Params>::value),
    1.70 +        badunboundmethodparams);
    1.71 +  }
    1.72 +  void Run(T* obj) const {
    1.73 +    DispatchToMethod(obj, m_, p_);
    1.74 +  }
    1.75 + private:
    1.76 +  Method m_;
    1.77 +  Params p_;
    1.78 +};
    1.79 +
    1.80 +// This class is used to work around VS2005 not accepting:
    1.81 +//
    1.82 +// friend class
    1.83 +//     base::RefCountedThreadSafe<ObserverListThreadSafe<ObserverType> >;
    1.84 +//
    1.85 +// Instead of friending the class, we could friend the actual function
    1.86 +// which calls delete.  However, this ends up being
    1.87 +// RefCountedThreadSafe::DeleteInternal(), which is private.  So we
    1.88 +// define our own templated traits class so we can friend it.
    1.89 +template <class T>
    1.90 +struct ObserverListThreadSafeTraits {
    1.91 +  static void Destruct(const ObserverListThreadSafe<T>* x) {
    1.92 +    delete x;
    1.93 +  }
    1.94 +};
    1.95 +
    1.96 +template <class ObserverType>
    1.97 +class ObserverListThreadSafe
    1.98 +    : public base::RefCountedThreadSafe<
    1.99 +        ObserverListThreadSafe<ObserverType>,
   1.100 +        ObserverListThreadSafeTraits<ObserverType> > {
   1.101 + public:
   1.102 +  typedef typename ObserverList<ObserverType>::NotificationType
   1.103 +      NotificationType;
   1.104 +
   1.105 +  ObserverListThreadSafe()
   1.106 +      : type_(ObserverListBase<ObserverType>::NOTIFY_ALL) {}
   1.107 +  explicit ObserverListThreadSafe(NotificationType type) : type_(type) {}
   1.108 +
   1.109 +  // Add an observer to the list.  An observer should not be added to
   1.110 +  // the same list more than once.
   1.111 +  void AddObserver(ObserverType* obs) {
   1.112 +    // If there is not a current MessageLoop, it is impossible to notify on it,
   1.113 +    // so do not add the observer.
   1.114 +    if (!base::MessageLoop::current())
   1.115 +      return;
   1.116 +
   1.117 +    ObserverList<ObserverType>* list = NULL;
   1.118 +    base::PlatformThreadId thread_id = base::PlatformThread::CurrentId();
   1.119 +    {
   1.120 +      base::AutoLock lock(list_lock_);
   1.121 +      if (observer_lists_.find(thread_id) == observer_lists_.end())
   1.122 +        observer_lists_[thread_id] = new ObserverListContext(type_);
   1.123 +      list = &(observer_lists_[thread_id]->list);
   1.124 +    }
   1.125 +    list->AddObserver(obs);
   1.126 +  }
   1.127 +
   1.128 +  // Remove an observer from the list if it is in the list.
   1.129 +  // If there are pending notifications in-transit to the observer, they will
   1.130 +  // be aborted.
   1.131 +  // If the observer to be removed is in the list, RemoveObserver MUST
   1.132 +  // be called from the same thread which called AddObserver.
   1.133 +  void RemoveObserver(ObserverType* obs) {
   1.134 +    ObserverListContext* context = NULL;
   1.135 +    ObserverList<ObserverType>* list = NULL;
   1.136 +    base::PlatformThreadId thread_id = base::PlatformThread::CurrentId();
   1.137 +    {
   1.138 +      base::AutoLock lock(list_lock_);
   1.139 +      typename ObserversListMap::iterator it = observer_lists_.find(thread_id);
   1.140 +      if (it == observer_lists_.end()) {
   1.141 +        // This will happen if we try to remove an observer on a thread
   1.142 +        // we never added an observer for.
   1.143 +        return;
   1.144 +      }
   1.145 +      context = it->second;
   1.146 +      list = &context->list;
   1.147 +
   1.148 +      // If we're about to remove the last observer from the list,
   1.149 +      // then we can remove this observer_list entirely.
   1.150 +      if (list->HasObserver(obs) && list->size() == 1)
   1.151 +        observer_lists_.erase(it);
   1.152 +    }
   1.153 +    list->RemoveObserver(obs);
   1.154 +
   1.155 +    // If RemoveObserver is called from a notification, the size will be
   1.156 +    // nonzero.  Instead of deleting here, the NotifyWrapper will delete
   1.157 +    // when it finishes iterating.
   1.158 +    if (list->size() == 0)
   1.159 +      delete context;
   1.160 +  }
   1.161 +
   1.162 +  // Verifies that the list is currently empty (i.e. there are no observers).
   1.163 +  void AssertEmpty() const {
   1.164 +    base::AutoLock lock(list_lock_);
   1.165 +    DCHECK(observer_lists_.empty());
   1.166 +  }
   1.167 +
   1.168 +  // Notify methods.
   1.169 +  // Make a thread-safe callback to each Observer in the list.
   1.170 +  // Note, these calls are effectively asynchronous.  You cannot assume
   1.171 +  // that at the completion of the Notify call that all Observers have
   1.172 +  // been Notified.  The notification may still be pending delivery.
   1.173 +  template <class Method>
   1.174 +  void Notify(Method m) {
   1.175 +    UnboundMethod<ObserverType, Method, Tuple0> method(m, MakeTuple());
   1.176 +    Notify<Method, Tuple0>(method);
   1.177 +  }
   1.178 +
   1.179 +  template <class Method, class A>
   1.180 +  void Notify(Method m, const A& a) {
   1.181 +    UnboundMethod<ObserverType, Method, Tuple1<A> > method(m, MakeTuple(a));
   1.182 +    Notify<Method, Tuple1<A> >(method);
   1.183 +  }
   1.184 +
   1.185 +  template <class Method, class A, class B>
   1.186 +  void Notify(Method m, const A& a, const B& b) {
   1.187 +    UnboundMethod<ObserverType, Method, Tuple2<A, B> > method(
   1.188 +        m, MakeTuple(a, b));
   1.189 +    Notify<Method, Tuple2<A, B> >(method);
   1.190 +  }
   1.191 +
   1.192 +  template <class Method, class A, class B, class C>
   1.193 +  void Notify(Method m, const A& a, const B& b, const C& c) {
   1.194 +    UnboundMethod<ObserverType, Method, Tuple3<A, B, C> > method(
   1.195 +        m, MakeTuple(a, b, c));
   1.196 +    Notify<Method, Tuple3<A, B, C> >(method);
   1.197 +  }
   1.198 +
   1.199 +  template <class Method, class A, class B, class C, class D>
   1.200 +  void Notify(Method m, const A& a, const B& b, const C& c, const D& d) {
   1.201 +    UnboundMethod<ObserverType, Method, Tuple4<A, B, C, D> > method(
   1.202 +        m, MakeTuple(a, b, c, d));
   1.203 +    Notify<Method, Tuple4<A, B, C, D> >(method);
   1.204 +  }
   1.205 +
   1.206 +  // TODO(mbelshe):  Add more wrappers for Notify() with more arguments.
   1.207 +
   1.208 + private:
   1.209 +  // See comment above ObserverListThreadSafeTraits' definition.
   1.210 +  friend struct ObserverListThreadSafeTraits<ObserverType>;
   1.211 +
   1.212 +  struct ObserverListContext {
   1.213 +    explicit ObserverListContext(NotificationType type)
   1.214 +        : loop(base::MessageLoopProxy::current()),
   1.215 +          list(type) {
   1.216 +    }
   1.217 +
   1.218 +    scoped_refptr<base::MessageLoopProxy> loop;
   1.219 +    ObserverList<ObserverType> list;
   1.220 +
   1.221 +    DISALLOW_COPY_AND_ASSIGN(ObserverListContext);
   1.222 +  };
   1.223 +
   1.224 +  ~ObserverListThreadSafe() {
   1.225 +    STLDeleteValues(&observer_lists_);
   1.226 +  }
   1.227 +
   1.228 +  template <class Method, class Params>
   1.229 +  void Notify(const UnboundMethod<ObserverType, Method, Params>& method) {
   1.230 +    base::AutoLock lock(list_lock_);
   1.231 +    typename ObserversListMap::iterator it;
   1.232 +    for (it = observer_lists_.begin(); it != observer_lists_.end(); ++it) {
   1.233 +      ObserverListContext* context = (*it).second;
   1.234 +      context->loop->PostTask(
   1.235 +          FROM_HERE,
   1.236 +          base::Bind(&ObserverListThreadSafe<ObserverType>::
   1.237 +              template NotifyWrapper<Method, Params>, this, context, method));
   1.238 +    }
   1.239 +  }
   1.240 +
   1.241 +  // Wrapper which is called to fire the notifications for each thread's
   1.242 +  // ObserverList.  This function MUST be called on the thread which owns
   1.243 +  // the unsafe ObserverList.
   1.244 +  template <class Method, class Params>
   1.245 +  void NotifyWrapper(ObserverListContext* context,
   1.246 +      const UnboundMethod<ObserverType, Method, Params>& method) {
   1.247 +
   1.248 +    // Check that this list still needs notifications.
   1.249 +    {
   1.250 +      base::AutoLock lock(list_lock_);
   1.251 +      typename ObserversListMap::iterator it =
   1.252 +          observer_lists_.find(base::PlatformThread::CurrentId());
   1.253 +
   1.254 +      // The ObserverList could have been removed already.  In fact, it could
   1.255 +      // have been removed and then re-added!  If the master list's loop
   1.256 +      // does not match this one, then we do not need to finish this
   1.257 +      // notification.
   1.258 +      if (it == observer_lists_.end() || it->second != context)
   1.259 +        return;
   1.260 +    }
   1.261 +
   1.262 +    {
   1.263 +      typename ObserverList<ObserverType>::Iterator it(context->list);
   1.264 +      ObserverType* obs;
   1.265 +      while ((obs = it.GetNext()) != NULL)
   1.266 +        method.Run(obs);
   1.267 +    }
   1.268 +
   1.269 +    // If there are no more observers on the list, we can now delete it.
   1.270 +    if (context->list.size() == 0) {
   1.271 +      {
   1.272 +        base::AutoLock lock(list_lock_);
   1.273 +        // Remove |list| if it's not already removed.
   1.274 +        // This can happen if multiple observers got removed in a notification.
   1.275 +        // See http://crbug.com/55725.
   1.276 +        typename ObserversListMap::iterator it =
   1.277 +            observer_lists_.find(base::PlatformThread::CurrentId());
   1.278 +        if (it != observer_lists_.end() && it->second == context)
   1.279 +          observer_lists_.erase(it);
   1.280 +      }
   1.281 +      delete context;
   1.282 +    }
   1.283 +  }
   1.284 +
   1.285 +  // Key by PlatformThreadId because in tests, clients can attempt to remove
   1.286 +  // observers without a MessageLoop. If this were keyed by MessageLoop, that
   1.287 +  // operation would be silently ignored, leaving garbage in the ObserverList.
   1.288 +  typedef std::map<base::PlatformThreadId, ObserverListContext*>
   1.289 +      ObserversListMap;
   1.290 +
   1.291 +  mutable base::Lock list_lock_;  // Protects the observer_lists_.
   1.292 +  ObserversListMap observer_lists_;
   1.293 +  const NotificationType type_;
   1.294 +
   1.295 +  DISALLOW_COPY_AND_ASSIGN(ObserverListThreadSafe);
   1.296 +};
   1.297 +
   1.298 +#endif  // BASE_OBSERVER_LIST_THREADSAFE_H_

mercurial