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