summaryrefslogtreecommitdiffstats
path: root/base/observer_list_threadsafe.h
diff options
context:
space:
mode:
authormbelshe@google.com <mbelshe@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-10-22 23:09:21 +0000
committermbelshe@google.com <mbelshe@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-10-22 23:09:21 +0000
commit503631cfa06aaae686f2e9a6f55ebc1859e8dab3 (patch)
tree8bc3a1d62749c65585db050b0926add085485436 /base/observer_list_threadsafe.h
parent4d40fd494fc3142ded2eddc2a1becaa7a1584d06 (diff)
downloadchromium_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.h190
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_