blob: ab49594e379a01c41fb8380600042a2ea9d22ee2 [file] [log] [blame]
[email protected]4c03b2e92012-01-03 19:36:571// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]503631c2008-10-22 23:09:212// 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
fdoray6aad3142017-04-10 18:58:218#include <unordered_map>
Lei Zhang52637ed2019-02-20 01:38:379#include <utility>
[email protected]503631c2008-10-22 23:09:2110
Francois Dorayf248acd2017-10-30 17:48:4911#include "base/base_export.h"
[email protected]a6ffd7d2011-10-12 20:26:5012#include "base/bind.h"
Hans Wennborg7b533712020-06-22 20:52:2713#include "base/check_op.h"
Lei Zhangc0f9fc52021-05-22 08:00:5314#include "base/containers/contains.h"
Francois Dorayf248acd2017-10-30 17:48:4915#include "base/lazy_instance.h"
[email protected]c62dd9d2011-09-21 18:05:4116#include "base/location.h"
Keishi Hattori0e45c022021-11-27 09:25:5217#include "base/memory/raw_ptr.h"
[email protected]3b63f8f42011-03-28 01:54:1518#include "base/memory/ref_counted.h"
[email protected]503631c2008-10-22 23:09:2119#include "base/observer_list.h"
fdoray6aad3142017-04-10 18:58:2120#include "base/synchronization/lock.h"
Patrick Monette643cdf62021-10-15 19:13:4221#include "base/task/sequenced_task_runner.h"
fdoray6aad3142017-04-10 18:58:2122#include "base/threading/sequenced_task_runner_handle.h"
23#include "base/threading/thread_local.h"
24#include "build/build_config.h"
25
[email protected]503631c2008-10-22 23:09:2126///////////////////////////////////////////////////////////////////////////////
27//
28// OVERVIEW:
29//
fdoray6aad3142017-04-10 18:58:2130// A thread-safe container for a list of observers. This is similar to the
31// observer_list (see observer_list.h), but it is more robust for multi-
32// threaded situations.
[email protected]52a261f2009-03-03 15:01:1233//
[email protected]503631c2008-10-22 23:09:2134// The following use cases are supported:
fdoray6aad3142017-04-10 18:58:2135// * Observers can register for notifications from any sequence. They are
36// always notified on the sequence from which they were registered.
37// * Any sequence may trigger a notification via Notify().
38// * Observers can remove themselves from the observer list inside of a
39// callback.
40// * If one sequence is notifying observers concurrently with an observer
41// removing itself from the observer list, the notifications will be
42// silently dropped.
[email protected]503631c2008-10-22 23:09:2143//
fdoray6aad3142017-04-10 18:58:2144// The drawback of the threadsafe observer list is that notifications are not
45// as real-time as the non-threadsafe version of this class. Notifications
46// will always be done via PostTask() to another sequence, whereas with the
Francois Doray4ffc9e12021-05-07 14:34:4347// non-thread-safe ObserverList, notifications happen synchronously.
48//
49// Note: this class previously supported synchronous notifications for
50// same-sequence observers, but it was error-prone and removed in
51// crbug.com/1193750, think twice before re-considering this paradigm.
[email protected]503631c2008-10-22 23:09:2152//
53///////////////////////////////////////////////////////////////////////////////
[email protected]bf687122011-01-11 21:19:5454
brettw5a1613dc2015-06-02 05:34:4355namespace base {
brettw5a1613dc2015-06-02 05:34:4356namespace internal {
57
Francois Dorayf248acd2017-10-30 17:48:4958class BASE_EXPORT ObserverListThreadSafeBase
59 : public RefCountedThreadSafe<ObserverListThreadSafeBase> {
60 public:
61 ObserverListThreadSafeBase() = default;
David Bienvenu5f4d4f02020-09-27 16:55:0362 ObserverListThreadSafeBase(const ObserverListThreadSafeBase&) = delete;
63 ObserverListThreadSafeBase& operator=(const ObserverListThreadSafeBase&) =
64 delete;
tzikb7990cc2016-08-25 02:19:3665
Francois Dorayf248acd2017-10-30 17:48:4966 protected:
67 template <typename ObserverType, typename Method>
68 struct Dispatcher;
69
70 template <typename ObserverType, typename ReceiverType, typename... Params>
71 struct Dispatcher<ObserverType, void (ReceiverType::*)(Params...)> {
72 static void Run(void (ReceiverType::*m)(Params...),
73 Params... params,
74 ObserverType* obj) {
75 (obj->*m)(std::forward<Params>(params)...);
76 }
77 };
78
79 struct NotificationDataBase {
80 NotificationDataBase(void* observer_list_in, const Location& from_here_in)
81 : observer_list(observer_list_in), from_here(from_here_in) {}
82
Keishi Hattori0e45c022021-11-27 09:25:5283 raw_ptr<void> observer_list;
Francois Dorayf248acd2017-10-30 17:48:4984 Location from_here;
85 };
86
87 virtual ~ObserverListThreadSafeBase() = default;
88
89 static LazyInstance<ThreadLocalPointer<const NotificationDataBase>>::Leaky
90 tls_current_notification_;
91
92 private:
93 friend class RefCountedThreadSafe<ObserverListThreadSafeBase>;
[email protected]4c03b2e92012-01-03 19:36:5794};
95
brettw5a1613dc2015-06-02 05:34:4396} // namespace internal
97
[email protected]503631c2008-10-22 23:09:2198template <class ObserverType>
Francois Dorayf248acd2017-10-30 17:48:4999class ObserverListThreadSafe : public internal::ObserverListThreadSafeBase {
[email protected]503631c2008-10-22 23:09:21100 public:
Eric Seckler3676f3c2021-04-27 14:32:38101 enum class AddObserverResult {
102 kBecameNonEmpty,
103 kWasAlreadyNonEmpty,
104 };
105 enum class RemoveObserverResult {
106 kWasOrBecameEmpty,
107 kRemainsNonEmpty,
108 };
109
fdoray6aad3142017-04-10 18:58:21110 ObserverListThreadSafe() = default;
François Degrosd6e2d7dd2017-11-22 05:37:02111 explicit ObserverListThreadSafe(ObserverListPolicy policy)
112 : policy_(policy) {}
David Bienvenu5f4d4f02020-09-27 16:55:03113 ObserverListThreadSafe(const ObserverListThreadSafe&) = delete;
114 ObserverListThreadSafe& operator=(const ObserverListThreadSafe&) = delete;
[email protected]503631c2008-10-22 23:09:21115
fdoray6aad3142017-04-10 18:58:21116 // Adds |observer| to the list. |observer| must not already be in the list.
Eric Seckler3676f3c2021-04-27 14:32:38117 AddObserverResult AddObserver(ObserverType* observer) {
Francois Dorayc91b0c32020-06-30 15:53:55118 DCHECK(SequencedTaskRunnerHandle::IsSet())
119 << "An observer can only be registered when SequencedTaskRunnerHandle "
120 "is set. If this is in a unit test, you're likely merely missing a "
121 "base::test::(SingleThread)TaskEnvironment in your fixture. "
122 "Otherwise, try running this code on a named thread (main/UI/IO) or "
123 "from a task posted to a base::SequencedTaskRunner or "
124 "base::SingleThreadTaskRunner.";
[email protected]c2b1b302011-11-23 20:34:04125
fdoray6aad3142017-04-10 18:58:21126 AutoLock auto_lock(lock_);
127
Eric Seckler3676f3c2021-04-27 14:32:38128 bool was_empty = observers_.empty();
129
fdoray6aad3142017-04-10 18:58:21130 // Add |observer| to the list of observers.
Jan Wilken Dörrief61e74c2019-06-07 08:20:02131 DCHECK(!Contains(observers_, observer));
fdoray6aad3142017-04-10 18:58:21132 const scoped_refptr<SequencedTaskRunner> task_runner =
133 SequencedTaskRunnerHandle::Get();
Etienne Bergeron3f80c2cf2021-03-22 17:32:14134 // Each observer gets a unique identifier. These unique identifiers are used
135 // to avoid execution of pending posted-tasks over removed or released
136 // observers.
137 const size_t observer_id = ++observer_id_counter_;
138 ObserverTaskRunnerInfo task_info = {task_runner, observer_id};
139 observers_[observer] = std::move(task_info);
fdoray6aad3142017-04-10 18:58:21140
141 // If this is called while a notification is being dispatched on this thread
François Degrosd6e2d7dd2017-11-22 05:37:02142 // and |policy_| is ALL, |observer| must be notified (if a notification is
143 // being dispatched on another thread in parallel, the notification may or
144 // may not make it to |observer| depending on the outcome of the race to
fdoray6aad3142017-04-10 18:58:21145 // |lock_|).
François Degrosd6e2d7dd2017-11-22 05:37:02146 if (policy_ == ObserverListPolicy::ALL) {
Francois Dorayf248acd2017-10-30 17:48:49147 const NotificationDataBase* current_notification =
148 tls_current_notification_.Get().Get();
149 if (current_notification && current_notification->observer_list == this) {
Etienne Bergeron3f80c2cf2021-03-22 17:32:14150 const NotificationData* notification_data =
151 static_cast<const NotificationData*>(current_notification);
fdoray6aad3142017-04-10 18:58:21152 task_runner->PostTask(
153 current_notification->from_here,
Etienne Bergeron3f80c2cf2021-03-22 17:32:14154 BindOnce(&ObserverListThreadSafe<ObserverType>::NotifyWrapper, this,
155 observer,
156 NotificationData(this, observer_id,
157 current_notification->from_here,
158 notification_data->method)));
avi816e3cf2016-10-27 04:10:51159 }
[email protected]503631c2008-10-22 23:09:21160 }
Eric Seckler3676f3c2021-04-27 14:32:38161
162 return was_empty ? AddObserverResult::kBecameNonEmpty
163 : AddObserverResult::kWasAlreadyNonEmpty;
[email protected]503631c2008-10-22 23:09:21164 }
165
[email protected]631739f2011-06-05 07:07:12166 // Remove an observer from the list if it is in the list.
fdoray6aad3142017-04-10 18:58:21167 //
168 // If a notification was sent to the observer but hasn't started to run yet,
169 // it will be aborted. If a notification has started to run, removing the
170 // observer won't stop it.
Eric Seckler3676f3c2021-04-27 14:32:38171 RemoveObserverResult RemoveObserver(ObserverType* observer) {
fdoray6aad3142017-04-10 18:58:21172 AutoLock auto_lock(lock_);
173 observers_.erase(observer);
Eric Seckler3676f3c2021-04-27 14:32:38174 return observers_.empty() ? RemoveObserverResult::kWasOrBecameEmpty
175 : RemoveObserverResult::kRemainsNonEmpty;
[email protected]503631c2008-10-22 23:09:21176 }
177
[email protected]f6969fe2012-02-08 00:22:11178 // Verifies that the list is currently empty (i.e. there are no observers).
179 void AssertEmpty() const {
fdoray6aad3142017-04-10 18:58:21180#if DCHECK_IS_ON()
181 AutoLock auto_lock(lock_);
182 DCHECK(observers_.empty());
183#endif
[email protected]f6969fe2012-02-08 00:22:11184 }
185
fdoray6aad3142017-04-10 18:58:21186 // Asynchronously invokes a callback on all observers, on their registration
187 // sequence. You cannot assume that at the completion of the Notify call that
188 // all Observers have been Notified. The notification may still be pending
189 // delivery.
tzikb7990cc2016-08-25 02:19:36190 template <typename Method, typename... Params>
Brett Wilson8e88b312017-09-12 05:22:16191 void Notify(const Location& from_here, Method m, Params&&... params) {
kylecharb2695fc2019-04-24 14:51:20192 RepeatingCallback<void(ObserverType*)> method =
193 BindRepeating(&Dispatcher<ObserverType, Method>::Run, m,
194 std::forward<Params>(params)...);
[email protected]503631c2008-10-22 23:09:21195
fdoray6aad3142017-04-10 18:58:21196 AutoLock lock(lock_);
197 for (const auto& observer : observers_) {
Etienne Bergeron3f80c2cf2021-03-22 17:32:14198 observer.second.task_runner->PostTask(
reillyg9a77a722015-02-09 20:18:33199 from_here,
tzik92b7a422017-04-11 15:00:44200 BindOnce(&ObserverListThreadSafe<ObserverType>::NotifyWrapper, this,
Etienne Bergeron3f80c2cf2021-03-22 17:32:14201 observer.first,
202 NotificationData(this, observer.second.observer_id,
203 from_here, method)));
thakis662b3e42014-12-23 02:02:57204 }
[email protected]503631c2008-10-22 23:09:21205 }
206
[email protected]503631c2008-10-22 23:09:21207 private:
Francois Dorayf248acd2017-10-30 17:48:49208 friend class RefCountedThreadSafe<ObserverListThreadSafeBase>;
[email protected]bf687122011-01-11 21:19:54209
Francois Dorayf248acd2017-10-30 17:48:49210 struct NotificationData : public NotificationDataBase {
211 NotificationData(ObserverListThreadSafe* observer_list_in,
Etienne Bergeron3f80c2cf2021-03-22 17:32:14212 size_t observer_id_in,
Francois Dorayf248acd2017-10-30 17:48:49213 const Location& from_here_in,
kylecharb2695fc2019-04-24 14:51:20214 const RepeatingCallback<void(ObserverType*)>& method_in)
Francois Dorayf248acd2017-10-30 17:48:49215 : NotificationDataBase(observer_list_in, from_here_in),
Etienne Bergeron3f80c2cf2021-03-22 17:32:14216 method(method_in),
217 observer_id(observer_id_in) {}
[email protected]920b1fe2011-08-09 21:29:59218
kylecharb2695fc2019-04-24 14:51:20219 RepeatingCallback<void(ObserverType*)> method;
Etienne Bergeron3f80c2cf2021-03-22 17:32:14220 size_t observer_id;
[email protected]920b1fe2011-08-09 21:29:59221 };
222
Takuto Ikuta88317a4c2018-04-27 18:19:58223 ~ObserverListThreadSafe() override = default;
[email protected]503631c2008-10-22 23:09:21224
fdoray6aad3142017-04-10 18:58:21225 void NotifyWrapper(ObserverType* observer,
226 const NotificationData& notification) {
tsergeant0091f852017-02-01 00:47:41227 {
fdoray6aad3142017-04-10 18:58:21228 AutoLock auto_lock(lock_);
avi816e3cf2016-10-27 04:10:51229
fdoray6aad3142017-04-10 18:58:21230 // Check whether the observer still needs a notification.
Etienne Bergeron3f80c2cf2021-03-22 17:32:14231 DCHECK_EQ(notification.observer_list, this);
fdoray6aad3142017-04-10 18:58:21232 auto it = observers_.find(observer);
Etienne Bergeron3f80c2cf2021-03-22 17:32:14233 if (it == observers_.end() ||
234 it->second.observer_id != notification.observer_id) {
tsergeant0091f852017-02-01 00:47:41235 return;
Etienne Bergeron3f80c2cf2021-03-22 17:32:14236 }
237 DCHECK(it->second.task_runner->RunsTasksInCurrentSequence());
tsergeant0091f852017-02-01 00:47:41238 }
[email protected]503631c2008-10-22 23:09:21239
fdoray6aad3142017-04-10 18:58:21240 // Keep track of the notification being dispatched on the current thread.
241 // This will be used if the callback below calls AddObserver().
242 //
243 // Note: |tls_current_notification_| may not be nullptr if this runs in a
244 // nested loop started by a notification callback. In that case, it is
245 // important to save the previous value to restore it later.
Francois Dorayf248acd2017-10-30 17:48:49246 auto& tls_current_notification = tls_current_notification_.Get();
247 const NotificationDataBase* const previous_notification =
248 tls_current_notification.Get();
249 tls_current_notification.Set(&notification);
fdoray50624472017-01-31 20:26:28250
fdoray6aad3142017-04-10 18:58:21251 // Invoke the callback.
252 notification.method.Run(observer);
253
254 // Reset the notification being dispatched on the current thread to its
255 // previous value.
Francois Dorayf248acd2017-10-30 17:48:49256 tls_current_notification.Set(previous_notification);
tsergeant0091f852017-02-01 00:47:41257 }
258
François Degrosd6e2d7dd2017-11-22 05:37:02259 const ObserverListPolicy policy_ = ObserverListPolicy::ALL;
tsergeant0091f852017-02-01 00:47:41260
fdoray6aad3142017-04-10 18:58:21261 mutable Lock lock_;
tsergeant0091f852017-02-01 00:47:41262
Etienne Bergeron3f80c2cf2021-03-22 17:32:14263 size_t observer_id_counter_ GUARDED_BY(lock_) = 0;
264
265 struct ObserverTaskRunnerInfo {
266 scoped_refptr<SequencedTaskRunner> task_runner;
267 size_t observer_id = 0;
268 };
269
fdoray6aad3142017-04-10 18:58:21270 // Keys are observers. Values are the SequencedTaskRunners on which they must
271 // be notified.
Etienne Bergeron3f80c2cf2021-03-22 17:32:14272 std::unordered_map<ObserverType*, ObserverTaskRunnerInfo> observers_
273 GUARDED_BY(lock_);
[email protected]503631c2008-10-22 23:09:21274};
275
brettw5a1613dc2015-06-02 05:34:43276} // namespace base
277
[email protected]503631c2008-10-22 23:09:21278#endif // BASE_OBSERVER_LIST_THREADSAFE_H_