1 : // Copyright (c) 2006-2008 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 <vector>
9 : #include <algorithm>
10 :
11 : #include "base/basictypes.h"
12 : #include "base/logging.h"
13 : #include "base/message_loop.h"
14 : #include "base/observer_list.h"
15 : #include "base/ref_counted.h"
16 : #include "base/task.h"
17 :
18 : namespace base {
19 :
20 : ///////////////////////////////////////////////////////////////////////////////
21 : //
22 : // OVERVIEW:
23 : //
24 : // A thread-safe container for a list of observers.
25 : // This is similar to the observer_list (see observer_list.h), but it
26 : // is more robust for multi-threaded situations.
27 : //
28 : // 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.
32 : // * Any thread may trigger a notification via NOTIFY_OBSERVERS.
33 : // * 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
47 : // which uses the ThreadSafeObserver. When Notifying the observers,
48 : // we simply call PostTask to each registered thread, and then each thread
49 : // will notify its regular ObserverList.
50 : //
51 : ///////////////////////////////////////////////////////////////////////////////
52 : template <class ObserverType>
53 : class ObserverListThreadSafe
54 : : public base::RefCountedThreadSafe<ObserverListThreadSafe<ObserverType> > {
55 : public:
56 0 : ObserverListThreadSafe() {}
57 :
58 0 : ~ObserverListThreadSafe() {
59 0 : typename ObserversListMap::const_iterator it;
60 0 : for (it = observer_lists_.begin(); it != observer_lists_.end(); ++it)
61 0 : delete (*it).second;
62 0 : observer_lists_.clear();
63 0 : }
64 :
65 : // Add an observer to the list.
66 0 : void AddObserver(ObserverType* obs) {
67 0 : ObserverList<ObserverType>* list = NULL;
68 0 : MessageLoop* loop = MessageLoop::current();
69 : // TODO(mbelshe): Get rid of this check. Its needed right now because
70 : // Time currently triggers usage of the ObserverList.
71 : // And unittests use time without a MessageLoop.
72 0 : if (!loop)
73 0 : return; // Some unittests may access this without a message loop.
74 : {
75 0 : AutoLock lock(list_lock_);
76 0 : if (observer_lists_.find(loop) == observer_lists_.end())
77 0 : observer_lists_[loop] = new ObserverList<ObserverType>();
78 0 : list = observer_lists_[loop];
79 : }
80 0 : list->AddObserver(obs);
81 : }
82 :
83 : // Remove an observer from the list.
84 : // If there are pending notifications in-transit to the observer, they will
85 : // be aborted.
86 : // RemoveObserver MUST be called from the same thread which called
87 : // AddObserver.
88 0 : void RemoveObserver(ObserverType* obs) {
89 0 : ObserverList<ObserverType>* list = NULL;
90 0 : MessageLoop* loop = MessageLoop::current();
91 0 : if (!loop)
92 0 : return; // On shutdown, it is possible that current() is already null.
93 : {
94 0 : AutoLock lock(list_lock_);
95 0 : list = observer_lists_[loop];
96 0 : if (!list) {
97 0 : NOTREACHED() << "RemoveObserver called on for unknown thread";
98 : return;
99 : }
100 :
101 : // If we're about to remove the last observer from the list,
102 : // then we can remove this observer_list entirely.
103 0 : if (list->size() == 1)
104 0 : observer_lists_.erase(loop);
105 : }
106 0 : list->RemoveObserver(obs);
107 :
108 : // If RemoveObserver is called from a notification, the size will be
109 : // nonzero. Instead of deleting here, the NotifyWrapper will delete
110 : // when it finishes iterating.
111 0 : if (list->size() == 0)
112 0 : delete list;
113 : }
114 :
115 : // Notify methods.
116 : // Make a thread-safe callback to each Observer in the list.
117 : // Note, these calls are effectively asynchronous. You cannot assume
118 : // that at the completion of the Notify call that all Observers have
119 : // been Notified. The notification may still be pending delivery.
120 : template <class Method>
121 : void Notify(Method m) {
122 : UnboundMethod<ObserverType, Method, Tuple0> method(m, MakeTuple());
123 : Notify<Method, Tuple0>(method);
124 : }
125 :
126 : template <class Method, class A>
127 0 : void Notify(Method m, const A &a) {
128 0 : UnboundMethod<ObserverType, Method, Tuple1<A> > method(m, MakeTuple(a));
129 0 : Notify<Method, Tuple1<A> >(method);
130 0 : }
131 :
132 : // TODO(mbelshe): Add more wrappers for Notify() with more arguments.
133 :
134 : private:
135 : template <class Method, class Params>
136 0 : void Notify(const UnboundMethod<ObserverType, Method, Params>& method) {
137 0 : AutoLock lock(list_lock_);
138 0 : typename ObserversListMap::iterator it;
139 0 : for (it = observer_lists_.begin(); it != observer_lists_.end(); ++it) {
140 0 : MessageLoop* loop = (*it).first;
141 0 : ObserverList<ObserverType>* list = (*it).second;
142 0 : loop->PostTask(FROM_HERE,
143 : NewRunnableMethod(this,
144 : &ObserverListThreadSafe<ObserverType>::
145 : template NotifyWrapper<Method, Params>, list, method));
146 : }
147 0 : }
148 :
149 : // Wrapper which is called to fire the notifications for each thread's
150 : // ObserverList. This function MUST be called on the thread which owns
151 : // the unsafe ObserverList.
152 : template <class Method, class Params>
153 0 : void NotifyWrapper(ObserverList<ObserverType>* list,
154 : const UnboundMethod<ObserverType, Method, Params>& method) {
155 :
156 : // Check that this list still needs notifications.
157 : {
158 0 : AutoLock lock(list_lock_);
159 : typename ObserversListMap::iterator it =
160 0 : observer_lists_.find(MessageLoop::current());
161 :
162 : // The ObserverList could have been removed already. In fact, it could
163 : // have been removed and then re-added! If the master list's loop
164 : // does not match this one, then we do not need to finish this
165 : // notification.
166 0 : if (it == observer_lists_.end() || it->second != list)
167 : return;
168 : }
169 :
170 : {
171 0 : typename ObserverList<ObserverType>::Iterator it(*list);
172 : ObserverType* obs;
173 0 : while ((obs = it.GetNext()) != NULL)
174 0 : method.Run(obs);
175 : }
176 :
177 : // If there are no more observers on the list, we can now delete it.
178 0 : if (list->size() == 0) {
179 : #ifndef NDEBUG
180 : {
181 0 : AutoLock lock(list_lock_);
182 : // Verify this list is no longer registered.
183 : typename ObserversListMap::iterator it =
184 0 : observer_lists_.find(MessageLoop::current());
185 0 : DCHECK(it == observer_lists_.end() || it->second != list);
186 : }
187 : #endif
188 0 : delete list;
189 : }
190 : }
191 :
192 : typedef std::map<MessageLoop*, ObserverList<ObserverType>*> ObserversListMap;
193 :
194 : // These are marked mutable to facilitate having NotifyAll be const.
195 : Lock list_lock_; // Protects the observer_lists_.
196 : ObserversListMap observer_lists_;
197 :
198 : DISALLOW_EVIL_CONSTRUCTORS(ObserverListThreadSafe);
199 : };
200 :
201 : } // namespace base
202 :
203 : #endif // BASE_OBSERVER_LIST_THREADSAFE_H_
|