blob: a84ac6383a2f0af5847d4837520b7fd9c9606b79 [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2012 The Chromium Authors
[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
Peter Kasting960e2d32023-03-14 17:18:4111#include "base/auto_reset.h"
Francois Dorayf248acd2017-10-30 17:48:4912#include "base/base_export.h"
David Sanders8cfb63a2022-04-14 19:36:3013#include "base/check.h"
Hans Wennborg7b533712020-06-22 20:52:2714#include "base/check_op.h"
Lei Zhangc0f9fc52021-05-22 08:00:5315#include "base/containers/contains.h"
David Sandersfc1f17fa2022-04-15 00:15:4916#include "base/dcheck_is_on.h"
Daniel White32856a6792023-05-26 14:31:1517#include "base/debug/stack_trace.h"
Ari Chivukula621212d02023-01-11 11:53:1018#include "base/functional/bind.h"
[email protected]c62dd9d2011-09-21 18:05:4119#include "base/location.h"
Keishi Hattori0e45c022021-11-27 09:25:5220#include "base/memory/raw_ptr.h"
[email protected]3b63f8f42011-03-28 01:54:1521#include "base/memory/ref_counted.h"
[email protected]503631c2008-10-22 23:09:2122#include "base/observer_list.h"
Daniel White32856a6792023-05-26 14:31:1523#include "base/strings/strcat.h"
fdoray6aad3142017-04-10 18:58:2124#include "base/synchronization/lock.h"
Patrick Monette643cdf62021-10-15 19:13:4225#include "base/task/sequenced_task_runner.h"
Sean Mahere672a662023-01-09 21:42:2826#include "base/task/single_thread_task_runner.h"
fdoray6aad3142017-04-10 18:58:2127#include "build/build_config.h"
28
[email protected]503631c2008-10-22 23:09:2129///////////////////////////////////////////////////////////////////////////////
30//
31// OVERVIEW:
32//
fdoray6aad3142017-04-10 18:58:2133// A thread-safe container for a list of observers. This is similar to the
34// observer_list (see observer_list.h), but it is more robust for multi-
35// threaded situations.
[email protected]52a261f2009-03-03 15:01:1236//
[email protected]503631c2008-10-22 23:09:2137// The following use cases are supported:
fdoray6aad3142017-04-10 18:58:2138// * Observers can register for notifications from any sequence. They are
39// always notified on the sequence from which they were registered.
40// * Any sequence may trigger a notification via Notify().
41// * Observers can remove themselves from the observer list inside of a
42// callback.
43// * If one sequence is notifying observers concurrently with an observer
44// removing itself from the observer list, the notifications will be
Joe Mason7eda66122023-08-17 19:10:5745// silently dropped. However if the observer is currently inside a
46// notification callback, the callback will finish running.
47//
48// By default, observers can be removed from any sequence. However this can be
49// error-prone since an observer may be running a callback when it's removed,
50// in which case it isn't safe to delete until the callback is finished.
51// Consider using the RemoveObserverPolicy::kAddingSequenceOnly template
52// parameter, which will CHECK that observers are only removed from the
53// sequence where they were added (which is also the sequence that runs
54// callbacks).
[email protected]503631c2008-10-22 23:09:2155//
fdoray6aad3142017-04-10 18:58:2156// The drawback of the threadsafe observer list is that notifications are not
57// as real-time as the non-threadsafe version of this class. Notifications
58// will always be done via PostTask() to another sequence, whereas with the
Francois Doray4ffc9e12021-05-07 14:34:4359// non-thread-safe ObserverList, notifications happen synchronously.
60//
61// Note: this class previously supported synchronous notifications for
62// same-sequence observers, but it was error-prone and removed in
63// crbug.com/1193750, think twice before re-considering this paradigm.
[email protected]503631c2008-10-22 23:09:2164//
65///////////////////////////////////////////////////////////////////////////////
[email protected]bf687122011-01-11 21:19:5466
brettw5a1613dc2015-06-02 05:34:4367namespace base {
brettw5a1613dc2015-06-02 05:34:4368namespace internal {
69
Francois Dorayf248acd2017-10-30 17:48:4970class BASE_EXPORT ObserverListThreadSafeBase
71 : public RefCountedThreadSafe<ObserverListThreadSafeBase> {
72 public:
Peter Kasting960e2d32023-03-14 17:18:4173 struct NotificationDataBase {
74 NotificationDataBase(void* observer_list_in, const Location& from_here_in)
75 : observer_list(observer_list_in), from_here(from_here_in) {}
76
77 raw_ptr<void> observer_list;
78 Location from_here;
79 };
80
Francois Dorayf248acd2017-10-30 17:48:4981 ObserverListThreadSafeBase() = default;
David Bienvenu5f4d4f02020-09-27 16:55:0382 ObserverListThreadSafeBase(const ObserverListThreadSafeBase&) = delete;
83 ObserverListThreadSafeBase& operator=(const ObserverListThreadSafeBase&) =
84 delete;
tzikb7990cc2016-08-25 02:19:3685
Francois Dorayf248acd2017-10-30 17:48:4986 protected:
87 template <typename ObserverType, typename Method>
88 struct Dispatcher;
89
90 template <typename ObserverType, typename ReceiverType, typename... Params>
91 struct Dispatcher<ObserverType, void (ReceiverType::*)(Params...)> {
92 static void Run(void (ReceiverType::*m)(Params...),
93 Params... params,
94 ObserverType* obj) {
95 (obj->*m)(std::forward<Params>(params)...);
96 }
97 };
98
Peter Kasting960e2d32023-03-14 17:18:4199 static const NotificationDataBase*& GetCurrentNotification();
Francois Dorayf248acd2017-10-30 17:48:49100
101 virtual ~ObserverListThreadSafeBase() = default;
102
Francois Dorayf248acd2017-10-30 17:48:49103 private:
104 friend class RefCountedThreadSafe<ObserverListThreadSafeBase>;
[email protected]4c03b2e92012-01-03 19:36:57105};
106
brettw5a1613dc2015-06-02 05:34:43107} // namespace internal
108
Joe Mason7eda66122023-08-17 19:10:57109enum class RemoveObserverPolicy {
110 // Observers can be removed from any sequence.
111 kAnySequence,
112 // Observers can only be removed from the sequence that added them.
113 kAddingSequenceOnly,
114};
115
116template <class ObserverType,
117 RemoveObserverPolicy RemovePolicy =
118 RemoveObserverPolicy::kAnySequence>
Francois Dorayf248acd2017-10-30 17:48:49119class ObserverListThreadSafe : public internal::ObserverListThreadSafeBase {
Joe Mason7eda66122023-08-17 19:10:57120 using Self = ObserverListThreadSafe<ObserverType, RemovePolicy>;
121
[email protected]503631c2008-10-22 23:09:21122 public:
Eric Seckler3676f3c2021-04-27 14:32:38123 enum class AddObserverResult {
124 kBecameNonEmpty,
125 kWasAlreadyNonEmpty,
126 };
127 enum class RemoveObserverResult {
128 kWasOrBecameEmpty,
129 kRemainsNonEmpty,
130 };
131
fdoray6aad3142017-04-10 18:58:21132 ObserverListThreadSafe() = default;
François Degrosd6e2d7dd2017-11-22 05:37:02133 explicit ObserverListThreadSafe(ObserverListPolicy policy)
134 : policy_(policy) {}
David Bienvenu5f4d4f02020-09-27 16:55:03135 ObserverListThreadSafe(const ObserverListThreadSafe&) = delete;
136 ObserverListThreadSafe& operator=(const ObserverListThreadSafe&) = delete;
[email protected]503631c2008-10-22 23:09:21137
fdoray6aad3142017-04-10 18:58:21138 // Adds |observer| to the list. |observer| must not already be in the list.
Eric Seckler3676f3c2021-04-27 14:32:38139 AddObserverResult AddObserver(ObserverType* observer) {
Sean Maher7d0e8052022-12-09 01:46:32140 DCHECK(SequencedTaskRunner::HasCurrentDefault())
Sean Maher70f2942932023-01-04 22:15:06141 << "An observer can only be registered when "
142 "SequencedTaskRunner::HasCurrentDefault. If this is in a unit test, "
143 "you're likely merely missing a "
Francois Dorayc91b0c32020-06-30 15:53:55144 "base::test::(SingleThread)TaskEnvironment in your fixture. "
145 "Otherwise, try running this code on a named thread (main/UI/IO) or "
146 "from a task posted to a base::SequencedTaskRunner or "
147 "base::SingleThreadTaskRunner.";
[email protected]c2b1b302011-11-23 20:34:04148
fdoray6aad3142017-04-10 18:58:21149 AutoLock auto_lock(lock_);
150
Eric Seckler3676f3c2021-04-27 14:32:38151 bool was_empty = observers_.empty();
152
fdoray6aad3142017-04-10 18:58:21153 // Add |observer| to the list of observers.
Jan Wilken Dörrief61e74c2019-06-07 08:20:02154 DCHECK(!Contains(observers_, observer));
fdoray6aad3142017-04-10 18:58:21155 const scoped_refptr<SequencedTaskRunner> task_runner =
Sean Maher7d0e8052022-12-09 01:46:32156 SequencedTaskRunner::GetCurrentDefault();
Etienne Bergeron3f80c2cf2021-03-22 17:32:14157 // Each observer gets a unique identifier. These unique identifiers are used
158 // to avoid execution of pending posted-tasks over removed or released
159 // observers.
160 const size_t observer_id = ++observer_id_counter_;
Daniel White32856a6792023-05-26 14:31:15161#if DCHECK_IS_ON()
162 ObserverTaskRunnerInfo task_info = {task_runner, base::debug::StackTrace(),
163 observer_id};
164#else
Etienne Bergeron3f80c2cf2021-03-22 17:32:14165 ObserverTaskRunnerInfo task_info = {task_runner, observer_id};
Daniel White32856a6792023-05-26 14:31:15166#endif
Etienne Bergeron3f80c2cf2021-03-22 17:32:14167 observers_[observer] = std::move(task_info);
fdoray6aad3142017-04-10 18:58:21168
169 // If this is called while a notification is being dispatched on this thread
François Degrosd6e2d7dd2017-11-22 05:37:02170 // and |policy_| is ALL, |observer| must be notified (if a notification is
171 // being dispatched on another thread in parallel, the notification may or
172 // may not make it to |observer| depending on the outcome of the race to
fdoray6aad3142017-04-10 18:58:21173 // |lock_|).
François Degrosd6e2d7dd2017-11-22 05:37:02174 if (policy_ == ObserverListPolicy::ALL) {
Peter Kasting960e2d32023-03-14 17:18:41175 if (const NotificationDataBase* const current_notification =
176 GetCurrentNotification();
177 current_notification && current_notification->observer_list == this) {
Etienne Bergeron3f80c2cf2021-03-22 17:32:14178 const NotificationData* notification_data =
179 static_cast<const NotificationData*>(current_notification);
fdoray6aad3142017-04-10 18:58:21180 task_runner->PostTask(
181 current_notification->from_here,
Joe Mason7eda66122023-08-17 19:10:57182 BindOnce(&Self::NotifyWrapper, this,
Ari Chivukula621212d02023-01-11 11:53:10183 // While `observer` may be dangling, we pass it and
184 // check it wasn't deallocated in NotifyWrapper() which can
185 // check `observers_` to verify presence (the owner of the
186 // observer is responsible for removing it from that list
187 // before deallocation).
188 UnsafeDangling(observer),
Etienne Bergeron3f80c2cf2021-03-22 17:32:14189 NotificationData(this, observer_id,
190 current_notification->from_here,
191 notification_data->method)));
avi816e3cf2016-10-27 04:10:51192 }
[email protected]503631c2008-10-22 23:09:21193 }
Eric Seckler3676f3c2021-04-27 14:32:38194
195 return was_empty ? AddObserverResult::kBecameNonEmpty
196 : AddObserverResult::kWasAlreadyNonEmpty;
[email protected]503631c2008-10-22 23:09:21197 }
198
[email protected]631739f2011-06-05 07:07:12199 // Remove an observer from the list if it is in the list.
fdoray6aad3142017-04-10 18:58:21200 //
201 // If a notification was sent to the observer but hasn't started to run yet,
202 // it will be aborted. If a notification has started to run, removing the
203 // observer won't stop it.
Eric Seckler3676f3c2021-04-27 14:32:38204 RemoveObserverResult RemoveObserver(ObserverType* observer) {
fdoray6aad3142017-04-10 18:58:21205 AutoLock auto_lock(lock_);
Joe Mason7eda66122023-08-17 19:10:57206 if constexpr (RemovePolicy == RemoveObserverPolicy::kAddingSequenceOnly) {
207 const auto it = observers_.find(observer);
208 CHECK(it == observers_.end() ||
209 it->second.task_runner->RunsTasksInCurrentSequence());
210 }
fdoray6aad3142017-04-10 18:58:21211 observers_.erase(observer);
Eric Seckler3676f3c2021-04-27 14:32:38212 return observers_.empty() ? RemoveObserverResult::kWasOrBecameEmpty
213 : RemoveObserverResult::kRemainsNonEmpty;
[email protected]503631c2008-10-22 23:09:21214 }
215
[email protected]f6969fe2012-02-08 00:22:11216 // Verifies that the list is currently empty (i.e. there are no observers).
217 void AssertEmpty() const {
fdoray6aad3142017-04-10 18:58:21218#if DCHECK_IS_ON()
219 AutoLock auto_lock(lock_);
Daniel White32856a6792023-05-26 14:31:15220 bool observers_is_empty = observers_.empty();
221 DUMP_WILL_BE_CHECK(observers_is_empty)
222 << "\n"
223 << GetObserversCreationStackStringLocked();
fdoray6aad3142017-04-10 18:58:21224#endif
[email protected]f6969fe2012-02-08 00:22:11225 }
226
fdoray6aad3142017-04-10 18:58:21227 // Asynchronously invokes a callback on all observers, on their registration
228 // sequence. You cannot assume that at the completion of the Notify call that
229 // all Observers have been Notified. The notification may still be pending
230 // delivery.
tzikb7990cc2016-08-25 02:19:36231 template <typename Method, typename... Params>
Brett Wilson8e88b312017-09-12 05:22:16232 void Notify(const Location& from_here, Method m, Params&&... params) {
kylecharb2695fc2019-04-24 14:51:20233 RepeatingCallback<void(ObserverType*)> method =
234 BindRepeating(&Dispatcher<ObserverType, Method>::Run, m,
235 std::forward<Params>(params)...);
[email protected]503631c2008-10-22 23:09:21236
fdoray6aad3142017-04-10 18:58:21237 AutoLock lock(lock_);
238 for (const auto& observer : observers_) {
Etienne Bergeron3f80c2cf2021-03-22 17:32:14239 observer.second.task_runner->PostTask(
reillyg9a77a722015-02-09 20:18:33240 from_here,
Joe Mason7eda66122023-08-17 19:10:57241 BindOnce(&Self::NotifyWrapper, this,
Ari Chivukula621212d02023-01-11 11:53:10242 // While `observer.first` may be dangling, we pass it and
243 // check it wasn't deallocated in NotifyWrapper() which can
244 // check `observers_` to verify presence (the owner of the
245 // observer is responsible for removing it from that list
246 // before deallocation).
247 UnsafeDangling(observer.first),
Etienne Bergeron3f80c2cf2021-03-22 17:32:14248 NotificationData(this, observer.second.observer_id,
249 from_here, method)));
thakis662b3e42014-12-23 02:02:57250 }
[email protected]503631c2008-10-22 23:09:21251 }
252
[email protected]503631c2008-10-22 23:09:21253 private:
Francois Dorayf248acd2017-10-30 17:48:49254 friend class RefCountedThreadSafe<ObserverListThreadSafeBase>;
[email protected]bf687122011-01-11 21:19:54255
Francois Dorayf248acd2017-10-30 17:48:49256 struct NotificationData : public NotificationDataBase {
257 NotificationData(ObserverListThreadSafe* observer_list_in,
Etienne Bergeron3f80c2cf2021-03-22 17:32:14258 size_t observer_id_in,
Francois Dorayf248acd2017-10-30 17:48:49259 const Location& from_here_in,
kylecharb2695fc2019-04-24 14:51:20260 const RepeatingCallback<void(ObserverType*)>& method_in)
Francois Dorayf248acd2017-10-30 17:48:49261 : NotificationDataBase(observer_list_in, from_here_in),
Etienne Bergeron3f80c2cf2021-03-22 17:32:14262 method(method_in),
263 observer_id(observer_id_in) {}
[email protected]920b1fe2011-08-09 21:29:59264
kylecharb2695fc2019-04-24 14:51:20265 RepeatingCallback<void(ObserverType*)> method;
Etienne Bergeron3f80c2cf2021-03-22 17:32:14266 size_t observer_id;
[email protected]920b1fe2011-08-09 21:29:59267 };
268
Takuto Ikuta88317a4c2018-04-27 18:19:58269 ~ObserverListThreadSafe() override = default;
[email protected]503631c2008-10-22 23:09:21270
Ari Chivukula621212d02023-01-11 11:53:10271 void NotifyWrapper(MayBeDangling<ObserverType> observer,
fdoray6aad3142017-04-10 18:58:21272 const NotificationData& notification) {
tsergeant0091f852017-02-01 00:47:41273 {
fdoray6aad3142017-04-10 18:58:21274 AutoLock auto_lock(lock_);
avi816e3cf2016-10-27 04:10:51275
fdoray6aad3142017-04-10 18:58:21276 // Check whether the observer still needs a notification.
Etienne Bergeron3f80c2cf2021-03-22 17:32:14277 DCHECK_EQ(notification.observer_list, this);
fdoray6aad3142017-04-10 18:58:21278 auto it = observers_.find(observer);
Etienne Bergeron3f80c2cf2021-03-22 17:32:14279 if (it == observers_.end() ||
280 it->second.observer_id != notification.observer_id) {
tsergeant0091f852017-02-01 00:47:41281 return;
Etienne Bergeron3f80c2cf2021-03-22 17:32:14282 }
283 DCHECK(it->second.task_runner->RunsTasksInCurrentSequence());
tsergeant0091f852017-02-01 00:47:41284 }
[email protected]503631c2008-10-22 23:09:21285
fdoray6aad3142017-04-10 18:58:21286 // Keep track of the notification being dispatched on the current thread.
287 // This will be used if the callback below calls AddObserver().
288 //
Peter Kasting960e2d32023-03-14 17:18:41289 // Note: GetCurrentNotification() may not return null if this runs in a
fdoray6aad3142017-04-10 18:58:21290 // nested loop started by a notification callback. In that case, it is
291 // important to save the previous value to restore it later.
Peter Kasting960e2d32023-03-14 17:18:41292 const AutoReset<const NotificationDataBase*> resetter_(
293 &GetCurrentNotification(), &notification);
fdoray50624472017-01-31 20:26:28294
fdoray6aad3142017-04-10 18:58:21295 // Invoke the callback.
296 notification.method.Run(observer);
tsergeant0091f852017-02-01 00:47:41297 }
298
Daniel White32856a6792023-05-26 14:31:15299 std::string GetObserversCreationStackStringLocked() const
300 EXCLUSIVE_LOCKS_REQUIRED(lock_) {
301 std::string result;
302#if DCHECK_IS_ON()
303 for (const auto& observer : observers_) {
304 StrAppend(&result,
305 {observer.second.add_observer_stack_.ToString(), "\n"});
306 }
307#endif
308 return result;
309 }
310
François Degrosd6e2d7dd2017-11-22 05:37:02311 const ObserverListPolicy policy_ = ObserverListPolicy::ALL;
tsergeant0091f852017-02-01 00:47:41312
fdoray6aad3142017-04-10 18:58:21313 mutable Lock lock_;
tsergeant0091f852017-02-01 00:47:41314
Etienne Bergeron3f80c2cf2021-03-22 17:32:14315 size_t observer_id_counter_ GUARDED_BY(lock_) = 0;
316
317 struct ObserverTaskRunnerInfo {
318 scoped_refptr<SequencedTaskRunner> task_runner;
Daniel White32856a6792023-05-26 14:31:15319#if DCHECK_IS_ON()
320 base::debug::StackTrace add_observer_stack_;
321#endif
Etienne Bergeron3f80c2cf2021-03-22 17:32:14322 size_t observer_id = 0;
323 };
324
fdoray6aad3142017-04-10 18:58:21325 // Keys are observers. Values are the SequencedTaskRunners on which they must
326 // be notified.
Etienne Bergeron3f80c2cf2021-03-22 17:32:14327 std::unordered_map<ObserverType*, ObserverTaskRunnerInfo> observers_
328 GUARDED_BY(lock_);
[email protected]503631c2008-10-22 23:09:21329};
330
brettw5a1613dc2015-06-02 05:34:43331} // namespace base
332
[email protected]503631c2008-10-22 23:09:21333#endif // BASE_OBSERVER_LIST_THREADSAFE_H_