observer_list_threadsafe.h revision c7f5f8508d98d5952d42ed7648c2a8f30a4da156
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///////////////////////////////////////////////////////////////////////////////
50template <class ObserverType>
51class 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    // TODO(mbelshe): Get rid of this check.  Its needed right now because
68    //                Time currently triggers usage of the ObserverList.
69    //                And unittests use time without a MessageLoop.
70    if (!loop)
71      return;  // Some unittests may access this without a message loop.
72    {
73      AutoLock lock(list_lock_);
74      if (observer_lists_.find(loop) == observer_lists_.end())
75        observer_lists_[loop] = new ObserverList<ObserverType>();
76      list = observer_lists_[loop];
77    }
78    list->AddObserver(obs);
79  }
80
81  // Remove an observer from the list.
82  // If there are pending notifications in-transit to the observer, they will
83  // be aborted.
84  // RemoveObserver MUST be called from the same thread which called
85  // AddObserver.
86  void RemoveObserver(ObserverType* obs) {
87    ObserverList<ObserverType>* list = NULL;
88    MessageLoop* loop = MessageLoop::current();
89    if (!loop)
90      return;  // On shutdown, it is possible that current() is already null.
91    {
92      AutoLock lock(list_lock_);
93      list = observer_lists_[loop];
94      if (!list) {
95        NOTREACHED() << "RemoveObserver called on for unknown thread";
96        return;
97      }
98
99      // If we're about to remove the last observer from the list,
100      // then we can remove this observer_list entirely.
101      if (list->size() == 1)
102        observer_lists_.erase(loop);
103    }
104    list->RemoveObserver(obs);
105
106    // If RemoveObserver is called from a notification, the size will be
107    // nonzero.  Instead of deleting here, the NotifyWrapper will delete
108    // when it finishes iterating.
109    if (list->size() == 0)
110      delete list;
111  }
112
113  // Notify methods.
114  // Make a thread-safe callback to each Observer in the list.
115  // Note, these calls are effectively asynchronous.  You cannot assume
116  // that at the completion of the Notify call that all Observers have
117  // been Notified.  The notification may still be pending delivery.
118  template <class Method>
119  void Notify(Method m) {
120    UnboundMethod<ObserverType, Method, Tuple0> method(m, MakeTuple());
121    Notify<Method, Tuple0>(method);
122  }
123
124  template <class Method, class A>
125  void Notify(Method m, const A &a) {
126    UnboundMethod<ObserverType, Method, Tuple1<A> > method(m, MakeTuple(a));
127    Notify<Method, Tuple1<A> >(method);
128  }
129
130  // TODO(mbelshe):  Add more wrappers for Notify() with more arguments.
131
132 private:
133  template <class Method, class Params>
134  void Notify(const UnboundMethod<ObserverType, Method, Params>& method) {
135    AutoLock lock(list_lock_);
136    typename ObserversListMap::iterator it;
137    for (it = observer_lists_.begin(); it != observer_lists_.end(); ++it) {
138      MessageLoop* loop = (*it).first;
139      ObserverList<ObserverType>* list = (*it).second;
140      loop->PostTask(FROM_HERE,
141          NewRunnableMethod(this,
142              &ObserverListThreadSafe<ObserverType>::
143                 template NotifyWrapper<Method, Params>, list, method));
144    }
145  }
146
147  // Wrapper which is called to fire the notifications for each thread's
148  // ObserverList.  This function MUST be called on the thread which owns
149  // the unsafe ObserverList.
150  template <class Method, class Params>
151  void NotifyWrapper(ObserverList<ObserverType>* list,
152      const UnboundMethod<ObserverType, Method, Params>& method) {
153
154    // Check that this list still needs notifications.
155    {
156      AutoLock lock(list_lock_);
157      typename ObserversListMap::iterator it =
158          observer_lists_.find(MessageLoop::current());
159
160      // The ObserverList could have been removed already.  In fact, it could
161      // have been removed and then re-added!  If the master list's loop
162      // does not match this one, then we do not need to finish this
163      // notification.
164      if (it == observer_lists_.end() || it->second != list)
165        return;
166    }
167
168    {
169      typename ObserverList<ObserverType>::Iterator it(*list);
170      ObserverType* obs;
171      while ((obs = it.GetNext()) != NULL)
172        method.Run(obs);
173    }
174
175    // If there are no more observers on the list, we can now delete it.
176    if (list->size() == 0) {
177#ifndef NDEBUG
178      {
179        AutoLock lock(list_lock_);
180        // Verify this list is no longer registered.
181        typename ObserversListMap::iterator it =
182            observer_lists_.find(MessageLoop::current());
183        DCHECK(it == observer_lists_.end() || it->second != list);
184      }
185#endif
186      delete list;
187    }
188  }
189
190  typedef std::map<MessageLoop*, ObserverList<ObserverType>*> ObserversListMap;
191
192  // These are marked mutable to facilitate having NotifyAll be const.
193  Lock list_lock_;  // Protects the observer_lists_.
194  ObserversListMap observer_lists_;
195
196  DISALLOW_EVIL_CONSTRUCTORS(ObserverListThreadSafe);
197};
198
199#endif  // BASE_OBSERVER_LIST_THREADSAFE_H_
200