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