OLD | NEW |
(Empty) | |
| 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 /////////////////////////////////////////////////////////////////////////////// |
| 19 // |
| 20 // OVERVIEW: |
| 21 // |
| 22 // A thread-safe container for a list of observers. |
| 23 // This is similar to the observer_list (see observer_list.h), but it |
| 24 // is more robust for multi-threaded situations. |
| 25 // |
| 26 // The following use cases are supported: |
| 27 // * Observers can register for notifications from any thread. |
| 28 // Callbacks to the observer will occur on the same thread where |
| 29 // the observer initially called AddObserver() from. |
| 30 // * Any thread may trigger a notification via NOTIFY_OBSERVERS. |
| 31 // * Observers can remove themselves from the observer list inside |
| 32 // of a callback. |
| 33 // * If one thread is notifying observers concurrently with an observer |
| 34 // removing itself from the observer list, the notifications will |
| 35 // be silently dropped. |
| 36 // |
| 37 // The drawback of the threadsafe observer list is that notifications |
| 38 // are not as real-time as the non-threadsafe version of this class. |
| 39 // Notifications will always be done via PostTask() to another thread, |
| 40 // whereas with the non-thread-safe observer_list, notifications happen |
| 41 // synchronously and immediately. |
| 42 // |
| 43 // IMPLEMENTATION NOTES |
| 44 // The ObserverListThreadSafe maintains an ObserverList for each thread |
| 45 // which uses the ThreadSafeObserver. When Notifying the observers, |
| 46 // we simply call PostTask to each registered thread, and then each thread |
| 47 // will notify its regular ObserverList. |
| 48 // |
| 49 /////////////////////////////////////////////////////////////////////////////// |
| 50 template <class ObserverType> |
| 51 class ObserverListThreadSafe : |
| 52 public base::RefCountedThreadSafe<ObserverListThreadSafe<ObserverType> > { |
| 53 public: |
| 54 ObserverListThreadSafe() {} |
| 55 |
| 56 ~ObserverListThreadSafe() { |
| 57 typename ObserversListMap::const_iterator it; |
| 58 for (it = observer_lists_.begin(); it != observer_lists_.end(); ++it) |
| 59 delete (*it).second; |
| 60 observer_lists_.clear(); |
| 61 } |
| 62 |
| 63 // Add an observer to the list. |
| 64 void AddObserver(ObserverType* obs) { |
| 65 ObserverList<ObserverType>* list = NULL; |
| 66 MessageLoop* loop = MessageLoop::current(); |
| 67 { |
| 68 AutoLock lock(list_lock_); |
| 69 if (observer_lists_.find(loop) == observer_lists_.end()) |
| 70 observer_lists_[loop] = new ObserverList<ObserverType>(); |
| 71 list = observer_lists_[loop]; |
| 72 } |
| 73 list->AddObserver(obs); |
| 74 } |
| 75 |
| 76 // Remove an observer from the list. |
| 77 // If there are pending notifications in-transit to the observer, they will |
| 78 // be aborted. |
| 79 // RemoveObserver MUST be called from the same thread which called |
| 80 // AddObserver. |
| 81 void RemoveObserver(ObserverType* obs) { |
| 82 ObserverList<ObserverType>* list = NULL; |
| 83 MessageLoop* loop = MessageLoop::current(); |
| 84 { |
| 85 AutoLock lock(list_lock_); |
| 86 DCHECK(observer_lists_.find(loop) != observer_lists_.end()) << |
| 87 "RemoveObserver called on for unknown thread"; |
| 88 list = observer_lists_[loop]; |
| 89 |
| 90 // If we're about to remove the last observer from the list, |
| 91 // then we can remove this observer_list entirely. |
| 92 if (list->size() == 1) |
| 93 observer_lists_.erase(loop); |
| 94 } |
| 95 list->RemoveObserver(obs); |
| 96 |
| 97 // If RemoveObserver is called from a notification, the size will be |
| 98 // nonzero. Instead of deleting here, the NotifyWrapper will delete |
| 99 // when it finishes iterating. |
| 100 if (list->size() == 0) |
| 101 delete list; |
| 102 } |
| 103 |
| 104 // Notify methods. |
| 105 // Make a thread-safe callback to each Observer in the list. |
| 106 // Note, these calls are effectively asynchronous. You cannot assume |
| 107 // that at the completion of the Notify call that all Observers have |
| 108 // been Notified. The notification may still be pending delivery. |
| 109 template <class Method> |
| 110 void Notify(Method m) { |
| 111 UnboundMethod<ObserverType, Method, Tuple0> method(m, MakeTuple()); |
| 112 Notify<Method, Tuple0>(method); |
| 113 } |
| 114 |
| 115 template <class Method, class A> |
| 116 void Notify(Method m, const A &a) { |
| 117 UnboundMethod<ObserverType, Method, Tuple1<A> > method(m, MakeTuple(a)); |
| 118 Notify<Method, Tuple1<A> >(method); |
| 119 } |
| 120 |
| 121 // TODO(mbelshe): Add more wrappers for Notify() with more arguments. |
| 122 |
| 123 private: |
| 124 template <class Method, class Params> |
| 125 void Notify(const UnboundMethod<ObserverType, Method, Params>& method) { |
| 126 AutoLock lock(list_lock_); |
| 127 typename ObserversListMap::iterator it; |
| 128 for (it = observer_lists_.begin(); it != observer_lists_.end(); ++it) { |
| 129 MessageLoop* loop = (*it).first; |
| 130 ObserverList<ObserverType>* list = (*it).second; |
| 131 loop->PostTask(FROM_HERE, |
| 132 NewRunnableMethod(this, |
| 133 &ObserverListThreadSafe<ObserverType>:: |
| 134 template NotifyWrapper<Method, Params>, list, method)); |
| 135 } |
| 136 } |
| 137 |
| 138 // Wrapper which is called to fire the notifications for each thread's |
| 139 // ObserverList. This function MUST be called on the thread which owns |
| 140 // the unsafe ObserverList. |
| 141 template <class Method, class Params> |
| 142 void NotifyWrapper(ObserverList<ObserverType>* list, |
| 143 const UnboundMethod<ObserverType, Method, Params>& method) { |
| 144 |
| 145 // Check that this list still needs notifications. |
| 146 { |
| 147 AutoLock lock(list_lock_); |
| 148 typename ObserversListMap::iterator it = |
| 149 observer_lists_.find(MessageLoop::current()); |
| 150 |
| 151 // The ObserverList could have been removed already. In fact, it could |
| 152 // have been removed and then re-added! If the master list's loop |
| 153 // does not match this one, then we do not need to finish this |
| 154 // notification. |
| 155 if (it == observer_lists_.end() || it->second != list) |
| 156 return; |
| 157 } |
| 158 |
| 159 { |
| 160 typename ObserverList<ObserverType>::Iterator it(*list); |
| 161 ObserverType* obs; |
| 162 while ((obs = it.GetNext()) != NULL) |
| 163 method.Run(obs); |
| 164 } |
| 165 |
| 166 // If there are no more observers on the list, we can now delete it. |
| 167 if (list->size() == 0) { |
| 168 #ifndef NDEBUG |
| 169 { |
| 170 AutoLock lock(list_lock_); |
| 171 // Verify this list is no longer registered. |
| 172 typename ObserversListMap::iterator it = |
| 173 observer_lists_.find(MessageLoop::current()); |
| 174 DCHECK(it == observer_lists_.end() || it->second != list); |
| 175 } |
| 176 #endif |
| 177 delete list; |
| 178 } |
| 179 } |
| 180 |
| 181 typedef std::map<MessageLoop*, ObserverList<ObserverType>*> ObserversListMap; |
| 182 |
| 183 // These are marked mutable to facilitate having NotifyAll be const. |
| 184 Lock list_lock_; // Protects the observer_lists_. |
| 185 ObserversListMap observer_lists_; |
| 186 |
| 187 DISALLOW_EVIL_CONSTRUCTORS(ObserverListThreadSafe); |
| 188 }; |
| 189 |
| 190 #endif // BASE_OBSERVER_LIST_THREADSAFE_H_ |
OLD | NEW |