observer_list_threadsafe.h revision c407dc5cd9bdc5668497f21b26b09d988ab439de
1// Copyright (c) 2010 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#include <vector> 11 12#include "base/basictypes.h" 13#include "base/callback.h" 14#include "base/logging.h" 15#include "base/message_loop.h" 16#include "base/observer_list.h" 17#include "base/ref_counted.h" 18#include "base/task.h" 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(). 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/////////////////////////////////////////////////////////////////////////////// 52template <class ObserverType> 53class ObserverListThreadSafe 54 : public base::RefCountedThreadSafe<ObserverListThreadSafe<ObserverType> > { 55 public: 56 ObserverListThreadSafe() {} 57 58 ~ObserverListThreadSafe() { 59 typename ObserversListMap::const_iterator it; 60 for (it = observer_lists_.begin(); it != observer_lists_.end(); ++it) 61 delete (*it).second; 62 observer_lists_.clear(); 63 } 64 65 // Add an observer to the list. 66 void AddObserver(ObserverType* obs) { 67 ObserverList<ObserverType>* list = NULL; 68 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 if (!loop) 73 return; // Some unittests may access this without a message loop. 74 { 75 AutoLock lock(list_lock_); 76 if (observer_lists_.find(loop) == observer_lists_.end()) 77 observer_lists_[loop] = new ObserverList<ObserverType>(); 78 list = observer_lists_[loop]; 79 } 80 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 void RemoveObserver(ObserverType* obs) { 89 ObserverList<ObserverType>* list = NULL; 90 MessageLoop* loop = MessageLoop::current(); 91 if (!loop) 92 return; // On shutdown, it is possible that current() is already null. 93 { 94 AutoLock lock(list_lock_); 95 list = observer_lists_[loop]; 96 if (!list) { 97 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 if (list->size() == 1) 104 observer_lists_.erase(loop); 105 } 106 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 if (list->size() == 0) 112 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 void Notify(Method m, const A &a) { 128 UnboundMethod<ObserverType, Method, Tuple1<A> > method(m, MakeTuple(a)); 129 Notify<Method, Tuple1<A> >(method); 130 } 131 132 // TODO(mbelshe): Add more wrappers for Notify() with more arguments. 133 134 private: 135 template <class Method, class Params> 136 void Notify(const UnboundMethod<ObserverType, Method, Params>& method) { 137 AutoLock lock(list_lock_); 138 typename ObserversListMap::iterator it; 139 for (it = observer_lists_.begin(); it != observer_lists_.end(); ++it) { 140 MessageLoop* loop = (*it).first; 141 ObserverList<ObserverType>* list = (*it).second; 142 loop->PostTask( 143 FROM_HERE, 144 NewRunnableMethod(this, 145 &ObserverListThreadSafe<ObserverType>:: 146 template NotifyWrapper<Method, Params>, list, method)); 147 } 148 } 149 150 // Wrapper which is called to fire the notifications for each thread's 151 // ObserverList. This function MUST be called on the thread which owns 152 // the unsafe ObserverList. 153 template <class Method, class Params> 154 void NotifyWrapper(ObserverList<ObserverType>* list, 155 const UnboundMethod<ObserverType, Method, Params>& method) { 156 157 // Check that this list still needs notifications. 158 { 159 AutoLock lock(list_lock_); 160 typename ObserversListMap::iterator it = 161 observer_lists_.find(MessageLoop::current()); 162 163 // The ObserverList could have been removed already. In fact, it could 164 // have been removed and then re-added! If the master list's loop 165 // does not match this one, then we do not need to finish this 166 // notification. 167 if (it == observer_lists_.end() || it->second != list) 168 return; 169 } 170 171 { 172 typename ObserverList<ObserverType>::Iterator it(*list); 173 ObserverType* obs; 174 while ((obs = it.GetNext()) != NULL) 175 method.Run(obs); 176 } 177 178 // If there are no more observers on the list, we can now delete it. 179 if (list->size() == 0) { 180#ifndef NDEBUG 181 { 182 AutoLock lock(list_lock_); 183 // Verify this list is no longer registered. 184 typename ObserversListMap::iterator it = 185 observer_lists_.find(MessageLoop::current()); 186 DCHECK(it == observer_lists_.end() || it->second != list); 187 } 188#endif 189 delete list; 190 } 191 } 192 193 typedef std::map<MessageLoop*, ObserverList<ObserverType>*> ObserversListMap; 194 195 // These are marked mutable to facilitate having NotifyAll be const. 196 Lock list_lock_; // Protects the observer_lists_. 197 ObserversListMap observer_lists_; 198 199 DISALLOW_COPY_AND_ASSIGN(ObserverListThreadSafe); 200}; 201 202#endif // BASE_OBSERVER_LIST_THREADSAFE_H_ 203