1// Copyright (c) 2011 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#pragma once
8
9#include <algorithm>
10#include <map>
11
12#include "base/basictypes.h"
13#include "base/callback.h"
14#include "base/logging.h"
15#include "base/memory/ref_counted.h"
16#include "base/message_loop.h"
17#include "base/observer_list.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///////////////////////////////////////////////////////////////////////////////
52
53// Forward declaration for ObserverListThreadSafeTraits.
54template <class ObserverType>
55class ObserverListThreadSafe;
56
57// This class is used to work around VS2005 not accepting:
58//
59// friend class
60//     base::RefCountedThreadSafe<ObserverListThreadSafe<ObserverType> >;
61//
62// Instead of friending the class, we could friend the actual function
63// which calls delete.  However, this ends up being
64// RefCountedThreadSafe::DeleteInternal(), which is private.  So we
65// define our own templated traits class so we can friend it.
66template <class T>
67struct ObserverListThreadSafeTraits {
68  static void Destruct(const ObserverListThreadSafe<T>* x) {
69    delete x;
70  }
71};
72
73template <class ObserverType>
74class ObserverListThreadSafe
75    : public base::RefCountedThreadSafe<
76        ObserverListThreadSafe<ObserverType>,
77        ObserverListThreadSafeTraits<ObserverType> > {
78 public:
79  typedef typename ObserverList<ObserverType>::NotificationType
80      NotificationType;
81
82  ObserverListThreadSafe()
83      : type_(ObserverListBase<ObserverType>::NOTIFY_ALL) {}
84  explicit ObserverListThreadSafe(NotificationType type) : type_(type) {}
85
86  // Add an observer to the list.
87  void AddObserver(ObserverType* obs) {
88    ObserverList<ObserverType>* list = NULL;
89    MessageLoop* loop = MessageLoop::current();
90    // TODO(mbelshe): Get rid of this check.  Its needed right now because
91    //                Time currently triggers usage of the ObserverList.
92    //                And unittests use time without a MessageLoop.
93    if (!loop)
94      return;  // Some unittests may access this without a message loop.
95    {
96      base::AutoLock lock(list_lock_);
97      if (observer_lists_.find(loop) == observer_lists_.end())
98        observer_lists_[loop] = new ObserverList<ObserverType>(type_);
99      list = observer_lists_[loop];
100    }
101    list->AddObserver(obs);
102  }
103
104  // Remove an observer from the list.
105  // If there are pending notifications in-transit to the observer, they will
106  // be aborted.
107  // RemoveObserver MUST be called from the same thread which called
108  // AddObserver.
109  void RemoveObserver(ObserverType* obs) {
110    ObserverList<ObserverType>* list = NULL;
111    MessageLoop* loop = MessageLoop::current();
112    if (!loop)
113      return;  // On shutdown, it is possible that current() is already null.
114    {
115      base::AutoLock lock(list_lock_);
116      list = observer_lists_[loop];
117      if (!list) {
118        NOTREACHED() << "RemoveObserver called on for unknown thread";
119        return;
120      }
121
122      // If we're about to remove the last observer from the list,
123      // then we can remove this observer_list entirely.
124      if (list->size() == 1)
125        observer_lists_.erase(loop);
126    }
127    list->RemoveObserver(obs);
128
129    // If RemoveObserver is called from a notification, the size will be
130    // nonzero.  Instead of deleting here, the NotifyWrapper will delete
131    // when it finishes iterating.
132    if (list->size() == 0)
133      delete list;
134  }
135
136  // Notify methods.
137  // Make a thread-safe callback to each Observer in the list.
138  // Note, these calls are effectively asynchronous.  You cannot assume
139  // that at the completion of the Notify call that all Observers have
140  // been Notified.  The notification may still be pending delivery.
141  template <class Method>
142  void Notify(Method m) {
143    UnboundMethod<ObserverType, Method, Tuple0> method(m, MakeTuple());
144    Notify<Method, Tuple0>(method);
145  }
146
147  template <class Method, class A>
148  void Notify(Method m, const A &a) {
149    UnboundMethod<ObserverType, Method, Tuple1<A> > method(m, MakeTuple(a));
150    Notify<Method, Tuple1<A> >(method);
151  }
152
153  // TODO(mbelshe):  Add more wrappers for Notify() with more arguments.
154
155 private:
156  // See comment above ObserverListThreadSafeTraits' definition.
157  friend struct ObserverListThreadSafeTraits<ObserverType>;
158
159  ~ObserverListThreadSafe() {
160    typename ObserversListMap::const_iterator it;
161    for (it = observer_lists_.begin(); it != observer_lists_.end(); ++it)
162      delete (*it).second;
163    observer_lists_.clear();
164  }
165
166  template <class Method, class Params>
167  void Notify(const UnboundMethod<ObserverType, Method, Params>& method) {
168    base::AutoLock lock(list_lock_);
169    typename ObserversListMap::iterator it;
170    for (it = observer_lists_.begin(); it != observer_lists_.end(); ++it) {
171      MessageLoop* loop = (*it).first;
172      ObserverList<ObserverType>* list = (*it).second;
173      loop->PostTask(
174          FROM_HERE,
175          NewRunnableMethod(this,
176              &ObserverListThreadSafe<ObserverType>::
177                 template NotifyWrapper<Method, Params>, list, method));
178    }
179  }
180
181  // Wrapper which is called to fire the notifications for each thread's
182  // ObserverList.  This function MUST be called on the thread which owns
183  // the unsafe ObserverList.
184  template <class Method, class Params>
185  void NotifyWrapper(ObserverList<ObserverType>* list,
186      const UnboundMethod<ObserverType, Method, Params>& method) {
187
188    // Check that this list still needs notifications.
189    {
190      base::AutoLock lock(list_lock_);
191      typename ObserversListMap::iterator it =
192          observer_lists_.find(MessageLoop::current());
193
194      // The ObserverList could have been removed already.  In fact, it could
195      // have been removed and then re-added!  If the master list's loop
196      // does not match this one, then we do not need to finish this
197      // notification.
198      if (it == observer_lists_.end() || it->second != list)
199        return;
200    }
201
202    {
203      typename ObserverList<ObserverType>::Iterator it(*list);
204      ObserverType* obs;
205      while ((obs = it.GetNext()) != NULL)
206        method.Run(obs);
207    }
208
209    // If there are no more observers on the list, we can now delete it.
210    if (list->size() == 0) {
211      {
212        base::AutoLock lock(list_lock_);
213        // Remove |list| if it's not already removed.
214        // This can happen if multiple observers got removed in a notification.
215        // See http://crbug.com/55725.
216        typename ObserversListMap::iterator it =
217            observer_lists_.find(MessageLoop::current());
218        if (it != observer_lists_.end() && it->second == list)
219          observer_lists_.erase(it);
220      }
221      delete list;
222    }
223  }
224
225  typedef std::map<MessageLoop*, ObserverList<ObserverType>*> ObserversListMap;
226
227  // These are marked mutable to facilitate having NotifyAll be const.
228  base::Lock list_lock_;  // Protects the observer_lists_.
229  ObserversListMap observer_lists_;
230  const NotificationType type_;
231
232  DISALLOW_COPY_AND_ASSIGN(ObserverListThreadSafe);
233};
234
235#endif  // BASE_OBSERVER_LIST_THREADSAFE_H_
236