blob: 31e0cae784662df364dae92fa6bd37f1b7f35a6f [file] [log] [blame]
[email protected]3b63f8f42011-03-28 01:54:151// Copyright (c) 2011 The Chromium Authors. All rights reserved.
[email protected]503631c2008-10-22 23:09:212// 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_
[email protected]32b76ef2010-07-26 23:08:247#pragma once
[email protected]503631c2008-10-22 23:09:218
[email protected]8efa6802010-07-14 18:58:179#include <algorithm>
[email protected]5097dc82010-07-15 17:23:2310#include <map>
[email protected]503631c2008-10-22 23:09:2111
12#include "base/basictypes.h"
[email protected]2b623242011-05-10 00:37:4013#include "base/callback_old.h"
[email protected]503631c2008-10-22 23:09:2114#include "base/logging.h"
[email protected]3b63f8f42011-03-28 01:54:1515#include "base/memory/ref_counted.h"
[email protected]503631c2008-10-22 23:09:2116#include "base/message_loop.h"
17#include "base/observer_list.h"
[email protected]503631c2008-10-22 23:09:2118#include "base/task.h"
19
20///////////////////////////////////////////////////////////////////////////////
21//
22// OVERVIEW:
23//
24// A thread-safe container for a list of observers.
[email protected]52a261f2009-03-03 15:01:1225// This is similar to the observer_list (see observer_list.h), but it
[email protected]503631c2008-10-22 23:09:2126// is more robust for multi-threaded situations.
[email protected]52a261f2009-03-03 15:01:1227//
[email protected]503631c2008-10-22 23:09:2128// The following use cases are supported:
29// * Observers can register for notifications from any thread.
30// Callbacks to the observer will occur on the same thread where
31// the observer initially called AddObserver() from.
[email protected]66761b952010-06-25 21:30:3832// * Any thread may trigger a notification via Notify().
[email protected]503631c2008-10-22 23:09:2133// * Observers can remove themselves from the observer list inside
34// of a callback.
35// * If one thread is notifying observers concurrently with an observer
36// removing itself from the observer list, the notifications will
37// be silently dropped.
38//
39// The drawback of the threadsafe observer list is that notifications
40// are not as real-time as the non-threadsafe version of this class.
41// Notifications will always be done via PostTask() to another thread,
42// whereas with the non-thread-safe observer_list, notifications happen
43// synchronously and immediately.
44//
45// IMPLEMENTATION NOTES
46// The ObserverListThreadSafe maintains an ObserverList for each thread
[email protected]52a261f2009-03-03 15:01:1247// which uses the ThreadSafeObserver. When Notifying the observers,
[email protected]503631c2008-10-22 23:09:2148// we simply call PostTask to each registered thread, and then each thread
49// will notify its regular ObserverList.
50//
51///////////////////////////////////////////////////////////////////////////////
[email protected]bf687122011-01-11 21:19:5452
53// Forward declaration for ObserverListThreadSafeTraits.
54template <class ObserverType>
55class ObserverListThreadSafe;
56
57// This class is used to work around VS2005 not accepting:
58//
59// friend class
60// base::RefCountedThreadSafe<ObserverListThreadSafe<ObserverType> >;
61//
62// Instead of friending the class, we could friend the actual function
63// which calls delete. However, this ends up being
64// RefCountedThreadSafe::DeleteInternal(), which is private. So we
65// define our own templated traits class so we can friend it.
66template <class T>
67struct ObserverListThreadSafeTraits {
68 static void Destruct(const ObserverListThreadSafe<T>* x) {
69 delete x;
70 }
71};
72
[email protected]503631c2008-10-22 23:09:2173template <class ObserverType>
[email protected]52a261f2009-03-03 15:01:1274class ObserverListThreadSafe
[email protected]bf687122011-01-11 21:19:5475 : public base::RefCountedThreadSafe<
76 ObserverListThreadSafe<ObserverType>,
77 ObserverListThreadSafeTraits<ObserverType> > {
[email protected]503631c2008-10-22 23:09:2178 public:
[email protected]f02640dd2010-08-31 22:54:2179 typedef typename ObserverList<ObserverType>::NotificationType
80 NotificationType;
81
82 ObserverListThreadSafe()
83 : type_(ObserverListBase<ObserverType>::NOTIFY_ALL) {}
84 explicit ObserverListThreadSafe(NotificationType type) : type_(type) {}
[email protected]503631c2008-10-22 23:09:2185
[email protected]503631c2008-10-22 23:09:2186 // Add an observer to the list.
87 void AddObserver(ObserverType* obs) {
88 ObserverList<ObserverType>* list = NULL;
89 MessageLoop* loop = MessageLoop::current();
[email protected]c2b11ae2008-11-03 19:45:2390 // TODO(mbelshe): Get rid of this check. Its needed right now because
91 // Time currently triggers usage of the ObserverList.
92 // And unittests use time without a MessageLoop.
[email protected]821f5032008-11-03 18:15:1693 if (!loop)
94 return; // Some unittests may access this without a message loop.
[email protected]503631c2008-10-22 23:09:2195 {
[email protected]20305ec2011-01-21 04:55:5296 base::AutoLock lock(list_lock_);
[email protected]503631c2008-10-22 23:09:2197 if (observer_lists_.find(loop) == observer_lists_.end())
[email protected]f02640dd2010-08-31 22:54:2198 observer_lists_[loop] = new ObserverList<ObserverType>(type_);
[email protected]503631c2008-10-22 23:09:2199 list = observer_lists_[loop];
100 }
101 list->AddObserver(obs);
102 }
103
104 // Remove an observer from the list.
105 // If there are pending notifications in-transit to the observer, they will
106 // be aborted.
107 // RemoveObserver MUST be called from the same thread which called
108 // AddObserver.
109 void RemoveObserver(ObserverType* obs) {
110 ObserverList<ObserverType>* list = NULL;
111 MessageLoop* loop = MessageLoop::current();
[email protected]67bdbe02008-11-03 17:15:07112 if (!loop)
113 return; // On shutdown, it is possible that current() is already null.
[email protected]503631c2008-10-22 23:09:21114 {
[email protected]20305ec2011-01-21 04:55:52115 base::AutoLock lock(list_lock_);
[email protected]503631c2008-10-22 23:09:21116 list = observer_lists_[loop];
[email protected]67bdbe02008-11-03 17:15:07117 if (!list) {
118 NOTREACHED() << "RemoveObserver called on for unknown thread";
119 return;
120 }
[email protected]503631c2008-10-22 23:09:21121
122 // If we're about to remove the last observer from the list,
123 // then we can remove this observer_list entirely.
124 if (list->size() == 1)
125 observer_lists_.erase(loop);
126 }
127 list->RemoveObserver(obs);
128
129 // If RemoveObserver is called from a notification, the size will be
130 // nonzero. Instead of deleting here, the NotifyWrapper will delete
131 // when it finishes iterating.
132 if (list->size() == 0)
133 delete list;
134 }
135
136 // Notify methods.
137 // Make a thread-safe callback to each Observer in the list.
138 // Note, these calls are effectively asynchronous. You cannot assume
139 // that at the completion of the Notify call that all Observers have
140 // been Notified. The notification may still be pending delivery.
141 template <class Method>
142 void Notify(Method m) {
143 UnboundMethod<ObserverType, Method, Tuple0> method(m, MakeTuple());
144 Notify<Method, Tuple0>(method);
145 }
146
147 template <class Method, class A>
148 void Notify(Method m, const A &a) {
149 UnboundMethod<ObserverType, Method, Tuple1<A> > method(m, MakeTuple(a));
150 Notify<Method, Tuple1<A> >(method);
151 }
152
153 // TODO(mbelshe): Add more wrappers for Notify() with more arguments.
154
155 private:
[email protected]bf687122011-01-11 21:19:54156 // See comment above ObserverListThreadSafeTraits' definition.
157 friend struct ObserverListThreadSafeTraits<ObserverType>;
158
[email protected]9230ac42011-01-10 20:40:36159 ~ObserverListThreadSafe() {
160 typename ObserversListMap::const_iterator it;
161 for (it = observer_lists_.begin(); it != observer_lists_.end(); ++it)
162 delete (*it).second;
163 observer_lists_.clear();
164 }
165
[email protected]503631c2008-10-22 23:09:21166 template <class Method, class Params>
167 void Notify(const UnboundMethod<ObserverType, Method, Params>& method) {
[email protected]20305ec2011-01-21 04:55:52168 base::AutoLock lock(list_lock_);
[email protected]503631c2008-10-22 23:09:21169 typename ObserversListMap::iterator it;
170 for (it = observer_lists_.begin(); it != observer_lists_.end(); ++it) {
171 MessageLoop* loop = (*it).first;
172 ObserverList<ObserverType>* list = (*it).second;
[email protected]0586b0e2010-02-12 21:38:37173 loop->PostTask(
174 FROM_HERE,
[email protected]52a261f2009-03-03 15:01:12175 NewRunnableMethod(this,
[email protected]503631c2008-10-22 23:09:21176 &ObserverListThreadSafe<ObserverType>::
177 template NotifyWrapper<Method, Params>, list, method));
178 }
179 }
180
181 // Wrapper which is called to fire the notifications for each thread's
182 // ObserverList. This function MUST be called on the thread which owns
183 // the unsafe ObserverList.
184 template <class Method, class Params>
[email protected]f0a51fb52009-03-05 12:46:38185 void NotifyWrapper(ObserverList<ObserverType>* list,
[email protected]503631c2008-10-22 23:09:21186 const UnboundMethod<ObserverType, Method, Params>& method) {
187
188 // Check that this list still needs notifications.
189 {
[email protected]20305ec2011-01-21 04:55:52190 base::AutoLock lock(list_lock_);
[email protected]503631c2008-10-22 23:09:21191 typename ObserversListMap::iterator it =
192 observer_lists_.find(MessageLoop::current());
193
194 // The ObserverList could have been removed already. In fact, it could
195 // have been removed and then re-added! If the master list's loop
196 // does not match this one, then we do not need to finish this
197 // notification.
198 if (it == observer_lists_.end() || it->second != list)
199 return;
200 }
201
202 {
203 typename ObserverList<ObserverType>::Iterator it(*list);
204 ObserverType* obs;
205 while ((obs = it.GetNext()) != NULL)
206 method.Run(obs);
207 }
208
209 // If there are no more observers on the list, we can now delete it.
210 if (list->size() == 0) {
[email protected]503631c2008-10-22 23:09:21211 {
[email protected]20305ec2011-01-21 04:55:52212 base::AutoLock lock(list_lock_);
[email protected]3c0d45e2010-09-17 19:33:06213 // Remove |list| if it's not already removed.
214 // This can happen if multiple observers got removed in a notification.
215 // See http://crbug.com/55725.
[email protected]503631c2008-10-22 23:09:21216 typename ObserversListMap::iterator it =
217 observer_lists_.find(MessageLoop::current());
[email protected]3c0d45e2010-09-17 19:33:06218 if (it != observer_lists_.end() && it->second == list)
219 observer_lists_.erase(it);
[email protected]503631c2008-10-22 23:09:21220 }
[email protected]503631c2008-10-22 23:09:21221 delete list;
222 }
223 }
224
225 typedef std::map<MessageLoop*, ObserverList<ObserverType>*> ObserversListMap;
226
227 // These are marked mutable to facilitate having NotifyAll be const.
[email protected]20305ec2011-01-21 04:55:52228 base::Lock list_lock_; // Protects the observer_lists_.
[email protected]503631c2008-10-22 23:09:21229 ObserversListMap observer_lists_;
[email protected]f02640dd2010-08-31 22:54:21230 const NotificationType type_;
[email protected]503631c2008-10-22 23:09:21231
[email protected]fc29bc702010-06-04 16:13:51232 DISALLOW_COPY_AND_ASSIGN(ObserverListThreadSafe);
[email protected]503631c2008-10-22 23:09:21233};
234
235#endif // BASE_OBSERVER_LIST_THREADSAFE_H_