diff options
author | mbelshe@google.com <mbelshe@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-10-22 23:09:21 +0000 |
---|---|---|
committer | mbelshe@google.com <mbelshe@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-10-22 23:09:21 +0000 |
commit | 503631cfa06aaae686f2e9a6f55ebc1859e8dab3 (patch) | |
tree | 8bc3a1d62749c65585db050b0926add085485436 /base/observer_list_threadsafe.h | |
parent | 4d40fd494fc3142ded2eddc2a1becaa7a1584d06 (diff) | |
download | chromium_src-503631cfa06aaae686f2e9a6f55ebc1859e8dab3.zip chromium_src-503631cfa06aaae686f2e9a6f55ebc1859e8dab3.tar.gz chromium_src-503631cfa06aaae686f2e9a6f55ebc1859e8dab3.tar.bz2 |
Create a thread-safe observer list. Will be used
by SystemMonitor.
Right now the class requires that Observers be RefCounted<>. This is because we invoke tasks via NewRunnableMethod for them. However, because we manually track lifecycle via AddObserver/RemoveObserver, we could override the RunnableMethodTraits to not require RefCounted<>. This would have the advantage that callers do not need to make all Observers be RefCounted, but makes it more critical that observers not forget to call RemoveObserver().
Review URL: http://codereview.chromium.org/7353
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@3787 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/observer_list_threadsafe.h')
-rw-r--r-- | base/observer_list_threadsafe.h | 190 |
1 files changed, 190 insertions, 0 deletions
diff --git a/base/observer_list_threadsafe.h b/base/observer_list_threadsafe.h new file mode 100644 index 0000000..fcfa0ed --- /dev/null +++ b/base/observer_list_threadsafe.h @@ -0,0 +1,190 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_OBSERVER_LIST_THREADSAFE_H_ +#define BASE_OBSERVER_LIST_THREADSAFE_H_ + +#include <vector> +#include <algorithm> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/observer_list.h" +#include "base/ref_counted.h" +#include "base/task.h" + +/////////////////////////////////////////////////////////////////////////////// +// +// OVERVIEW: +// +// A thread-safe container for a list of observers. +// This is similar to the observer_list (see observer_list.h), but it +// is more robust for multi-threaded situations. +// +// The following use cases are supported: +// * Observers can register for notifications from any thread. +// Callbacks to the observer will occur on the same thread where +// the observer initially called AddObserver() from. +// * Any thread may trigger a notification via NOTIFY_OBSERVERS. +// * Observers can remove themselves from the observer list inside +// of a callback. +// * If one thread is notifying observers concurrently with an observer +// removing itself from the observer list, the notifications will +// be silently dropped. +// +// The drawback of the threadsafe observer list is that notifications +// are not as real-time as the non-threadsafe version of this class. +// Notifications will always be done via PostTask() to another thread, +// whereas with the non-thread-safe observer_list, notifications happen +// synchronously and immediately. +// +// IMPLEMENTATION NOTES +// The ObserverListThreadSafe maintains an ObserverList for each thread +// which uses the ThreadSafeObserver. When Notifying the observers, +// we simply call PostTask to each registered thread, and then each thread +// will notify its regular ObserverList. +// +/////////////////////////////////////////////////////////////////////////////// +template <class ObserverType> +class ObserverListThreadSafe : + public base::RefCountedThreadSafe<ObserverListThreadSafe<ObserverType> > { + public: + ObserverListThreadSafe() {} + + ~ObserverListThreadSafe() { + typename ObserversListMap::const_iterator it; + for (it = observer_lists_.begin(); it != observer_lists_.end(); ++it) + delete (*it).second; + observer_lists_.clear(); + } + + // Add an observer to the list. + void AddObserver(ObserverType* obs) { + ObserverList<ObserverType>* list = NULL; + MessageLoop* loop = MessageLoop::current(); + { + AutoLock lock(list_lock_); + if (observer_lists_.find(loop) == observer_lists_.end()) + observer_lists_[loop] = new ObserverList<ObserverType>(); + list = observer_lists_[loop]; + } + list->AddObserver(obs); + } + + // Remove an observer from the list. + // If there are pending notifications in-transit to the observer, they will + // be aborted. + // RemoveObserver MUST be called from the same thread which called + // AddObserver. + void RemoveObserver(ObserverType* obs) { + ObserverList<ObserverType>* list = NULL; + MessageLoop* loop = MessageLoop::current(); + { + AutoLock lock(list_lock_); + DCHECK(observer_lists_.find(loop) != observer_lists_.end()) << + "RemoveObserver called on for unknown thread"; + list = observer_lists_[loop]; + + // If we're about to remove the last observer from the list, + // then we can remove this observer_list entirely. + if (list->size() == 1) + observer_lists_.erase(loop); + } + list->RemoveObserver(obs); + + // If RemoveObserver is called from a notification, the size will be + // nonzero. Instead of deleting here, the NotifyWrapper will delete + // when it finishes iterating. + if (list->size() == 0) + delete list; + } + + // Notify methods. + // Make a thread-safe callback to each Observer in the list. + // Note, these calls are effectively asynchronous. You cannot assume + // that at the completion of the Notify call that all Observers have + // been Notified. The notification may still be pending delivery. + template <class Method> + void Notify(Method m) { + UnboundMethod<ObserverType, Method, Tuple0> method(m, MakeTuple()); + Notify<Method, Tuple0>(method); + } + + template <class Method, class A> + void Notify(Method m, const A &a) { + UnboundMethod<ObserverType, Method, Tuple1<A> > method(m, MakeTuple(a)); + Notify<Method, Tuple1<A> >(method); + } + + // TODO(mbelshe): Add more wrappers for Notify() with more arguments. + + private: + template <class Method, class Params> + void Notify(const UnboundMethod<ObserverType, Method, Params>& method) { + AutoLock lock(list_lock_); + typename ObserversListMap::iterator it; + for (it = observer_lists_.begin(); it != observer_lists_.end(); ++it) { + MessageLoop* loop = (*it).first; + ObserverList<ObserverType>* list = (*it).second; + loop->PostTask(FROM_HERE, + NewRunnableMethod(this, + &ObserverListThreadSafe<ObserverType>:: + template NotifyWrapper<Method, Params>, list, method)); + } + } + + // Wrapper which is called to fire the notifications for each thread's + // ObserverList. This function MUST be called on the thread which owns + // the unsafe ObserverList. + template <class Method, class Params> + void NotifyWrapper(ObserverList<ObserverType>* list, + const UnboundMethod<ObserverType, Method, Params>& method) { + + // Check that this list still needs notifications. + { + AutoLock lock(list_lock_); + typename ObserversListMap::iterator it = + observer_lists_.find(MessageLoop::current()); + + // The ObserverList could have been removed already. In fact, it could + // have been removed and then re-added! If the master list's loop + // does not match this one, then we do not need to finish this + // notification. + if (it == observer_lists_.end() || it->second != list) + return; + } + + { + typename ObserverList<ObserverType>::Iterator it(*list); + ObserverType* obs; + while ((obs = it.GetNext()) != NULL) + method.Run(obs); + } + + // If there are no more observers on the list, we can now delete it. + if (list->size() == 0) { +#ifndef NDEBUG + { + AutoLock lock(list_lock_); + // Verify this list is no longer registered. + typename ObserversListMap::iterator it = + observer_lists_.find(MessageLoop::current()); + DCHECK(it == observer_lists_.end() || it->second != list); + } +#endif + delete list; + } + } + + typedef std::map<MessageLoop*, ObserverList<ObserverType>*> ObserversListMap; + + // These are marked mutable to facilitate having NotifyAll be const. + Lock list_lock_; // Protects the observer_lists_. + ObserversListMap observer_lists_; + + DISALLOW_EVIL_CONSTRUCTORS(ObserverListThreadSafe); +}; + +#endif // BASE_OBSERVER_LIST_THREADSAFE_H_ |