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