|
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_ |