[email protected] | 3b63f8f4 | 2011-03-28 01:54:15 | [diff] [blame] | 1 | // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
[email protected] | 503631c | 2008-10-22 23:09:21 | [diff] [blame] | 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_ |
[email protected] | 32b76ef | 2010-07-26 23:08:24 | [diff] [blame] | 7 | #pragma once |
[email protected] | 503631c | 2008-10-22 23:09:21 | [diff] [blame] | 8 | |
[email protected] | 8efa680 | 2010-07-14 18:58:17 | [diff] [blame] | 9 | #include <algorithm> |
[email protected] | 5097dc8 | 2010-07-15 17:23:23 | [diff] [blame] | 10 | #include <map> |
[email protected] | 503631c | 2008-10-22 23:09:21 | [diff] [blame] | 11 | |
| 12 | #include "base/basictypes.h" |
[email protected] | 2b62324 | 2011-05-10 00:37:40 | [diff] [blame] | 13 | #include "base/callback_old.h" |
[email protected] | 503631c | 2008-10-22 23:09:21 | [diff] [blame] | 14 | #include "base/logging.h" |
[email protected] | 3b63f8f4 | 2011-03-28 01:54:15 | [diff] [blame] | 15 | #include "base/memory/ref_counted.h" |
[email protected] | 503631c | 2008-10-22 23:09:21 | [diff] [blame] | 16 | #include "base/message_loop.h" |
| 17 | #include "base/observer_list.h" |
[email protected] | 503631c | 2008-10-22 23:09:21 | [diff] [blame] | 18 | #include "base/task.h" |
| 19 | |
| 20 | /////////////////////////////////////////////////////////////////////////////// |
| 21 | // |
| 22 | // OVERVIEW: |
| 23 | // |
| 24 | // A thread-safe container for a list of observers. |
[email protected] | 52a261f | 2009-03-03 15:01:12 | [diff] [blame] | 25 | // This is similar to the observer_list (see observer_list.h), but it |
[email protected] | 503631c | 2008-10-22 23:09:21 | [diff] [blame] | 26 | // is more robust for multi-threaded situations. |
[email protected] | 52a261f | 2009-03-03 15:01:12 | [diff] [blame] | 27 | // |
[email protected] | 503631c | 2008-10-22 23:09:21 | [diff] [blame] | 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. |
[email protected] | 66761b95 | 2010-06-25 21:30:38 | [diff] [blame] | 32 | // * Any thread may trigger a notification via Notify(). |
[email protected] | 503631c | 2008-10-22 23:09:21 | [diff] [blame] | 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 |
[email protected] | 52a261f | 2009-03-03 15:01:12 | [diff] [blame] | 47 | // which uses the ThreadSafeObserver. When Notifying the observers, |
[email protected] | 503631c | 2008-10-22 23:09:21 | [diff] [blame] | 48 | // we simply call PostTask to each registered thread, and then each thread |
| 49 | // will notify its regular ObserverList. |
| 50 | // |
| 51 | /////////////////////////////////////////////////////////////////////////////// |
[email protected] | bf68712 | 2011-01-11 21:19:54 | [diff] [blame] | 52 | |
| 53 | // Forward declaration for ObserverListThreadSafeTraits. |
| 54 | template <class ObserverType> |
| 55 | class 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. |
| 66 | template <class T> |
| 67 | struct ObserverListThreadSafeTraits { |
| 68 | static void Destruct(const ObserverListThreadSafe<T>* x) { |
| 69 | delete x; |
| 70 | } |
| 71 | }; |
| 72 | |
[email protected] | 503631c | 2008-10-22 23:09:21 | [diff] [blame] | 73 | template <class ObserverType> |
[email protected] | 52a261f | 2009-03-03 15:01:12 | [diff] [blame] | 74 | class ObserverListThreadSafe |
[email protected] | bf68712 | 2011-01-11 21:19:54 | [diff] [blame] | 75 | : public base::RefCountedThreadSafe< |
| 76 | ObserverListThreadSafe<ObserverType>, |
| 77 | ObserverListThreadSafeTraits<ObserverType> > { |
[email protected] | 503631c | 2008-10-22 23:09:21 | [diff] [blame] | 78 | public: |
[email protected] | f02640dd | 2010-08-31 22:54:21 | [diff] [blame] | 79 | 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] | 503631c | 2008-10-22 23:09:21 | [diff] [blame] | 85 | |
[email protected] | 503631c | 2008-10-22 23:09:21 | [diff] [blame] | 86 | // Add an observer to the list. |
| 87 | void AddObserver(ObserverType* obs) { |
| 88 | ObserverList<ObserverType>* list = NULL; |
| 89 | MessageLoop* loop = MessageLoop::current(); |
[email protected] | c2b11ae | 2008-11-03 19:45:23 | [diff] [blame] | 90 | // 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] | 821f503 | 2008-11-03 18:15:16 | [diff] [blame] | 93 | if (!loop) |
| 94 | return; // Some unittests may access this without a message loop. |
[email protected] | 503631c | 2008-10-22 23:09:21 | [diff] [blame] | 95 | { |
[email protected] | 20305ec | 2011-01-21 04:55:52 | [diff] [blame] | 96 | base::AutoLock lock(list_lock_); |
[email protected] | 503631c | 2008-10-22 23:09:21 | [diff] [blame] | 97 | if (observer_lists_.find(loop) == observer_lists_.end()) |
[email protected] | f02640dd | 2010-08-31 22:54:21 | [diff] [blame] | 98 | observer_lists_[loop] = new ObserverList<ObserverType>(type_); |
[email protected] | 503631c | 2008-10-22 23:09:21 | [diff] [blame] | 99 | 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] | 67bdbe0 | 2008-11-03 17:15:07 | [diff] [blame] | 112 | if (!loop) |
| 113 | return; // On shutdown, it is possible that current() is already null. |
[email protected] | 503631c | 2008-10-22 23:09:21 | [diff] [blame] | 114 | { |
[email protected] | 20305ec | 2011-01-21 04:55:52 | [diff] [blame] | 115 | base::AutoLock lock(list_lock_); |
[email protected] | 503631c | 2008-10-22 23:09:21 | [diff] [blame] | 116 | list = observer_lists_[loop]; |
[email protected] | 67bdbe0 | 2008-11-03 17:15:07 | [diff] [blame] | 117 | if (!list) { |
| 118 | NOTREACHED() << "RemoveObserver called on for unknown thread"; |
| 119 | return; |
| 120 | } |
[email protected] | 503631c | 2008-10-22 23:09:21 | [diff] [blame] | 121 | |
| 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] | bf68712 | 2011-01-11 21:19:54 | [diff] [blame] | 156 | // See comment above ObserverListThreadSafeTraits' definition. |
| 157 | friend struct ObserverListThreadSafeTraits<ObserverType>; |
| 158 | |
[email protected] | 9230ac4 | 2011-01-10 20:40:36 | [diff] [blame] | 159 | ~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] | 503631c | 2008-10-22 23:09:21 | [diff] [blame] | 166 | template <class Method, class Params> |
| 167 | void Notify(const UnboundMethod<ObserverType, Method, Params>& method) { |
[email protected] | 20305ec | 2011-01-21 04:55:52 | [diff] [blame] | 168 | base::AutoLock lock(list_lock_); |
[email protected] | 503631c | 2008-10-22 23:09:21 | [diff] [blame] | 169 | 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] | 0586b0e | 2010-02-12 21:38:37 | [diff] [blame] | 173 | loop->PostTask( |
| 174 | FROM_HERE, |
[email protected] | 52a261f | 2009-03-03 15:01:12 | [diff] [blame] | 175 | NewRunnableMethod(this, |
[email protected] | 503631c | 2008-10-22 23:09:21 | [diff] [blame] | 176 | &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] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 185 | void NotifyWrapper(ObserverList<ObserverType>* list, |
[email protected] | 503631c | 2008-10-22 23:09:21 | [diff] [blame] | 186 | const UnboundMethod<ObserverType, Method, Params>& method) { |
| 187 | |
| 188 | // Check that this list still needs notifications. |
| 189 | { |
[email protected] | 20305ec | 2011-01-21 04:55:52 | [diff] [blame] | 190 | base::AutoLock lock(list_lock_); |
[email protected] | 503631c | 2008-10-22 23:09:21 | [diff] [blame] | 191 | 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] | 503631c | 2008-10-22 23:09:21 | [diff] [blame] | 211 | { |
[email protected] | 20305ec | 2011-01-21 04:55:52 | [diff] [blame] | 212 | base::AutoLock lock(list_lock_); |
[email protected] | 3c0d45e | 2010-09-17 19:33:06 | [diff] [blame] | 213 | // 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] | 503631c | 2008-10-22 23:09:21 | [diff] [blame] | 216 | typename ObserversListMap::iterator it = |
| 217 | observer_lists_.find(MessageLoop::current()); |
[email protected] | 3c0d45e | 2010-09-17 19:33:06 | [diff] [blame] | 218 | if (it != observer_lists_.end() && it->second == list) |
| 219 | observer_lists_.erase(it); |
[email protected] | 503631c | 2008-10-22 23:09:21 | [diff] [blame] | 220 | } |
[email protected] | 503631c | 2008-10-22 23:09:21 | [diff] [blame] | 221 | 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] | 20305ec | 2011-01-21 04:55:52 | [diff] [blame] | 228 | base::Lock list_lock_; // Protects the observer_lists_. |
[email protected] | 503631c | 2008-10-22 23:09:21 | [diff] [blame] | 229 | ObserversListMap observer_lists_; |
[email protected] | f02640dd | 2010-08-31 22:54:21 | [diff] [blame] | 230 | const NotificationType type_; |
[email protected] | 503631c | 2008-10-22 23:09:21 | [diff] [blame] | 231 | |
[email protected] | fc29bc70 | 2010-06-04 16:13:51 | [diff] [blame] | 232 | DISALLOW_COPY_AND_ASSIGN(ObserverListThreadSafe); |
[email protected] | 503631c | 2008-10-22 23:09:21 | [diff] [blame] | 233 | }; |
| 234 | |
| 235 | #endif // BASE_OBSERVER_LIST_THREADSAFE_H_ |