1// Copyright (c) 2012 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
11#include "base/basictypes.h"
12#include "base/bind.h"
13#include "base/location.h"
14#include "base/logging.h"
15#include "base/memory/ref_counted.h"
16#include "base/message_loop/message_loop.h"
17#include "base/message_loop/message_loop_proxy.h"
18#include "base/observer_list.h"
19#include "base/stl_util.h"
20#include "base/threading/platform_thread.h"
21
22///////////////////////////////////////////////////////////////////////////////
23//
24// OVERVIEW:
25//
26//   A thread-safe container for a list of observers.
27//   This is similar to the observer_list (see observer_list.h), but it
28//   is more robust for multi-threaded situations.
29//
30//   The following use cases are supported:
31//    * Observers can register for notifications from any thread.
32//      Callbacks to the observer will occur on the same thread where
33//      the observer initially called AddObserver() from.
34//    * Any thread may trigger a notification via Notify().
35//    * Observers can remove themselves from the observer list inside
36//      of a callback.
37//    * If one thread is notifying observers concurrently with an observer
38//      removing itself from the observer list, the notifications will
39//      be silently dropped.
40//
41//   The drawback of the threadsafe observer list is that notifications
42//   are not as real-time as the non-threadsafe version of this class.
43//   Notifications will always be done via PostTask() to another thread,
44//   whereas with the non-thread-safe observer_list, notifications happen
45//   synchronously and immediately.
46//
47//   IMPLEMENTATION NOTES
48//   The ObserverListThreadSafe maintains an ObserverList for each thread
49//   which uses the ThreadSafeObserver.  When Notifying the observers,
50//   we simply call PostTask to each registered thread, and then each thread
51//   will notify its regular ObserverList.
52//
53///////////////////////////////////////////////////////////////////////////////
54
55// Forward declaration for ObserverListThreadSafeTraits.
56template <class ObserverType>
57class ObserverListThreadSafe;
58
59// An UnboundMethod is a wrapper for a method where the actual object is
60// provided at Run dispatch time.
61template <class T, class Method, class Params>
62class UnboundMethod {
63 public:
64  UnboundMethod(Method m, const Params& p) : m_(m), p_(p) {
65    COMPILE_ASSERT(
66        (base::internal::ParamsUseScopedRefptrCorrectly<Params>::value),
67        badunboundmethodparams);
68  }
69  void Run(T* obj) const {
70    DispatchToMethod(obj, m_, p_);
71  }
72 private:
73  Method m_;
74  Params p_;
75};
76
77// This class is used to work around VS2005 not accepting:
78//
79// friend class
80//     base::RefCountedThreadSafe<ObserverListThreadSafe<ObserverType> >;
81//
82// Instead of friending the class, we could friend the actual function
83// which calls delete.  However, this ends up being
84// RefCountedThreadSafe::DeleteInternal(), which is private.  So we
85// define our own templated traits class so we can friend it.
86template <class T>
87struct ObserverListThreadSafeTraits {
88  static void Destruct(const ObserverListThreadSafe<T>* x) {
89    delete x;
90  }
91};
92
93template <class ObserverType>
94class ObserverListThreadSafe
95    : public base::RefCountedThreadSafe<
96        ObserverListThreadSafe<ObserverType>,
97        ObserverListThreadSafeTraits<ObserverType> > {
98 public:
99  typedef typename ObserverList<ObserverType>::NotificationType
100      NotificationType;
101
102  ObserverListThreadSafe()
103      : type_(ObserverListBase<ObserverType>::NOTIFY_ALL) {}
104  explicit ObserverListThreadSafe(NotificationType type) : type_(type) {}
105
106  // Add an observer to the list.  An observer should not be added to
107  // the same list more than once.
108  void AddObserver(ObserverType* obs) {
109    // If there is not a current MessageLoop, it is impossible to notify on it,
110    // so do not add the observer.
111    if (!base::MessageLoop::current())
112      return;
113
114    ObserverList<ObserverType>* list = NULL;
115    base::PlatformThreadId thread_id = base::PlatformThread::CurrentId();
116    {
117      base::AutoLock lock(list_lock_);
118      if (observer_lists_.find(thread_id) == observer_lists_.end())
119        observer_lists_[thread_id] = new ObserverListContext(type_);
120      list = &(observer_lists_[thread_id]->list);
121    }
122    list->AddObserver(obs);
123  }
124
125  // Remove an observer from the list if it is in the list.
126  // If there are pending notifications in-transit to the observer, they will
127  // be aborted.
128  // If the observer to be removed is in the list, RemoveObserver MUST
129  // be called from the same thread which called AddObserver.
130  void RemoveObserver(ObserverType* obs) {
131    ObserverListContext* context = NULL;
132    ObserverList<ObserverType>* list = NULL;
133    base::PlatformThreadId thread_id = base::PlatformThread::CurrentId();
134    {
135      base::AutoLock lock(list_lock_);
136      typename ObserversListMap::iterator it = observer_lists_.find(thread_id);
137      if (it == observer_lists_.end()) {
138        // This will happen if we try to remove an observer on a thread
139        // we never added an observer for.
140        return;
141      }
142      context = it->second;
143      list = &context->list;
144
145      // If we're about to remove the last observer from the list,
146      // then we can remove this observer_list entirely.
147      if (list->HasObserver(obs) && list->size() == 1)
148        observer_lists_.erase(it);
149    }
150    list->RemoveObserver(obs);
151
152    // If RemoveObserver is called from a notification, the size will be
153    // nonzero.  Instead of deleting here, the NotifyWrapper will delete
154    // when it finishes iterating.
155    if (list->size() == 0)
156      delete context;
157  }
158
159  // Verifies that the list is currently empty (i.e. there are no observers).
160  void AssertEmpty() const {
161    base::AutoLock lock(list_lock_);
162    DCHECK(observer_lists_.empty());
163  }
164
165  // Notify methods.
166  // Make a thread-safe callback to each Observer in the list.
167  // Note, these calls are effectively asynchronous.  You cannot assume
168  // that at the completion of the Notify call that all Observers have
169  // been Notified.  The notification may still be pending delivery.
170  template <class Method>
171  void Notify(Method m) {
172    UnboundMethod<ObserverType, Method, Tuple0> method(m, MakeTuple());
173    Notify<Method, Tuple0>(method);
174  }
175
176  template <class Method, class A>
177  void Notify(Method m, const A& a) {
178    UnboundMethod<ObserverType, Method, Tuple1<A> > method(m, MakeTuple(a));
179    Notify<Method, Tuple1<A> >(method);
180  }
181
182  template <class Method, class A, class B>
183  void Notify(Method m, const A& a, const B& b) {
184    UnboundMethod<ObserverType, Method, Tuple2<A, B> > method(
185        m, MakeTuple(a, b));
186    Notify<Method, Tuple2<A, B> >(method);
187  }
188
189  template <class Method, class A, class B, class C>
190  void Notify(Method m, const A& a, const B& b, const C& c) {
191    UnboundMethod<ObserverType, Method, Tuple3<A, B, C> > method(
192        m, MakeTuple(a, b, c));
193    Notify<Method, Tuple3<A, B, C> >(method);
194  }
195
196  template <class Method, class A, class B, class C, class D>
197  void Notify(Method m, const A& a, const B& b, const C& c, const D& d) {
198    UnboundMethod<ObserverType, Method, Tuple4<A, B, C, D> > method(
199        m, MakeTuple(a, b, c, d));
200    Notify<Method, Tuple4<A, B, C, D> >(method);
201  }
202
203  // TODO(mbelshe):  Add more wrappers for Notify() with more arguments.
204
205 private:
206  // See comment above ObserverListThreadSafeTraits' definition.
207  friend struct ObserverListThreadSafeTraits<ObserverType>;
208
209  struct ObserverListContext {
210    explicit ObserverListContext(NotificationType type)
211        : loop(base::MessageLoopProxy::current()),
212          list(type) {
213    }
214
215    scoped_refptr<base::MessageLoopProxy> loop;
216    ObserverList<ObserverType> list;
217
218    DISALLOW_COPY_AND_ASSIGN(ObserverListContext);
219  };
220
221  ~ObserverListThreadSafe() {
222    STLDeleteValues(&observer_lists_);
223  }
224
225  template <class Method, class Params>
226  void Notify(const UnboundMethod<ObserverType, Method, Params>& method) {
227    base::AutoLock lock(list_lock_);
228    typename ObserversListMap::iterator it;
229    for (it = observer_lists_.begin(); it != observer_lists_.end(); ++it) {
230      ObserverListContext* context = (*it).second;
231      context->loop->PostTask(
232          FROM_HERE,
233          base::Bind(&ObserverListThreadSafe<ObserverType>::
234              template NotifyWrapper<Method, Params>, this, context, method));
235    }
236  }
237
238  // Wrapper which is called to fire the notifications for each thread's
239  // ObserverList.  This function MUST be called on the thread which owns
240  // the unsafe ObserverList.
241  template <class Method, class Params>
242  void NotifyWrapper(ObserverListContext* context,
243      const UnboundMethod<ObserverType, Method, Params>& method) {
244
245    // Check that this list still needs notifications.
246    {
247      base::AutoLock lock(list_lock_);
248      typename ObserversListMap::iterator it =
249          observer_lists_.find(base::PlatformThread::CurrentId());
250
251      // The ObserverList could have been removed already.  In fact, it could
252      // have been removed and then re-added!  If the master list's loop
253      // does not match this one, then we do not need to finish this
254      // notification.
255      if (it == observer_lists_.end() || it->second != context)
256        return;
257    }
258
259    {
260      typename ObserverList<ObserverType>::Iterator it(context->list);
261      ObserverType* obs;
262      while ((obs = it.GetNext()) != NULL)
263        method.Run(obs);
264    }
265
266    // If there are no more observers on the list, we can now delete it.
267    if (context->list.size() == 0) {
268      {
269        base::AutoLock lock(list_lock_);
270        // Remove |list| if it's not already removed.
271        // This can happen if multiple observers got removed in a notification.
272        // See http://crbug.com/55725.
273        typename ObserversListMap::iterator it =
274            observer_lists_.find(base::PlatformThread::CurrentId());
275        if (it != observer_lists_.end() && it->second == context)
276          observer_lists_.erase(it);
277      }
278      delete context;
279    }
280  }
281
282  // Key by PlatformThreadId because in tests, clients can attempt to remove
283  // observers without a MessageLoop. If this were keyed by MessageLoop, that
284  // operation would be silently ignored, leaving garbage in the ObserverList.
285  typedef std::map<base::PlatformThreadId, ObserverListContext*>
286      ObserversListMap;
287
288  mutable base::Lock list_lock_;  // Protects the observer_lists_.
289  ObserversListMap observer_lists_;
290  const NotificationType type_;
291
292  DISALLOW_COPY_AND_ASSIGN(ObserverListThreadSafe);
293};
294
295#endif  // BASE_OBSERVER_LIST_THREADSAFE_H_
296