• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
UnboundMethod(Method m,const Params & p)64   UnboundMethod(Method m, const Params& p) : m_(m), p_(p) {
65     COMPILE_ASSERT(
66         (base::internal::ParamsUseScopedRefptrCorrectly<Params>::value),
67         badunboundmethodparams);
68   }
Run(T * obj)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 {
DestructObserverListThreadSafeTraits88   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 
ObserverListThreadSafe()102   ObserverListThreadSafe()
103       : type_(ObserverListBase<ObserverType>::NOTIFY_ALL) {}
ObserverListThreadSafe(NotificationType type)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.
AddObserver(ObserverType * obs)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.
RemoveObserver(ObserverType * obs)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).
AssertEmpty()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>
Notify(Method m)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>
Notify(Method m,const A & 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>
Notify(Method m,const A & a,const B & 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>
Notify(Method m,const A & a,const B & b,const C & 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>
Notify(Method m,const A & a,const B & b,const C & c,const D & 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