1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #ifndef BASE_OBSERVER_LIST_THREADSAFE_H_
6 #define BASE_OBSERVER_LIST_THREADSAFE_H_
8 #include <unordered_map>
11 #include "base/auto_reset.h"
12 #include "base/base_export.h"
13 #include "base/check.h"
14 #include "base/check_op.h"
15 #include "base/containers/contains.h"
16 #include "base/dcheck_is_on.h"
17 #include "base/debug/stack_trace.h"
18 #include "base/functional/bind.h"
19 #include "base/location.h"
20 #include "base/memory/raw_ptr.h"
21 #include "base/memory/ref_counted.h"
22 #include "base/observer_list.h"
23 #include "base/strings/strcat.h"
24 #include "base/synchronization/lock.h"
25 #include "base/task/sequenced_task_runner.h"
26 #include "base/task/single_thread_task_runner.h"
27 #include "build/build_config.h"
28 #include "third_party/abseil-cpp/absl/base/attributes.h"
30 ///////////////////////////////////////////////////////////////////////////////
34 // A thread-safe container for a list of observers. This is similar to the
35 // observer_list (see observer_list.h), but it is more robust for multi-
36 // threaded situations.
38 // The following use cases are supported:
39 // * Observers can register for notifications from any sequence. They are
40 // always notified on the sequence from which they were registered.
41 // * Any sequence may trigger a notification via Notify().
42 // * Observers can remove themselves from the observer list inside of a
44 // * If one sequence is notifying observers concurrently with an observer
45 // removing itself from the observer list, the notifications will be
46 // silently dropped. However if the observer is currently inside a
47 // notification callback, the callback will finish running.
49 // By default, observers can be removed from any sequence. However this can be
50 // error-prone since an observer may be running a callback when it's removed,
51 // in which case it isn't safe to delete until the callback is finished.
52 // Consider using the RemoveObserverPolicy::kAddingSequenceOnly template
53 // parameter, which will CHECK that observers are only removed from the
54 // sequence where they were added (which is also the sequence that runs
57 // The drawback of the threadsafe observer list is that notifications are not
58 // as real-time as the non-threadsafe version of this class. Notifications
59 // will always be done via PostTask() to another sequence, whereas with the
60 // non-thread-safe ObserverList, notifications happen synchronously.
62 // Note: this class previously supported synchronous notifications for
63 // same-sequence observers, but it was error-prone and removed in
64 // crbug.com/1193750, think twice before re-considering this paradigm.
66 ///////////////////////////////////////////////////////////////////////////////
71 class BASE_EXPORT ObserverListThreadSafeBase
72 : public RefCountedThreadSafe<ObserverListThreadSafeBase> {
74 struct NotificationDataBase {
75 NotificationDataBase(void* observer_list_in, const Location& from_here_in)
76 : observer_list(observer_list_in), from_here(from_here_in) {}
78 raw_ptr<void> observer_list;
82 ObserverListThreadSafeBase() = default;
83 ObserverListThreadSafeBase(const ObserverListThreadSafeBase&) = delete;
84 ObserverListThreadSafeBase& operator=(const ObserverListThreadSafeBase&) =
88 template <typename ObserverType, typename Method>
91 template <typename ObserverType, typename ReceiverType, typename... Params>
92 struct Dispatcher<ObserverType, void (ReceiverType::*)(Params...)> {
93 static void Run(void (ReceiverType::*m)(Params...),
96 (obj->*m)(std::forward<Params>(params)...);
100 static const NotificationDataBase*& GetCurrentNotification();
102 virtual ~ObserverListThreadSafeBase() = default;
105 friend class RefCountedThreadSafe<ObserverListThreadSafeBase>;
108 } // namespace internal
110 enum class RemoveObserverPolicy {
111 // Observers can be removed from any sequence.
113 // Observers can only be removed from the sequence that added them.
117 template <class ObserverType,
118 RemoveObserverPolicy RemovePolicy =
119 RemoveObserverPolicy::kAnySequence>
120 class ObserverListThreadSafe : public internal::ObserverListThreadSafeBase {
121 using Self = ObserverListThreadSafe<ObserverType, RemovePolicy>;
124 enum class AddObserverResult {
128 enum class RemoveObserverResult {
133 ObserverListThreadSafe() = default;
134 explicit ObserverListThreadSafe(ObserverListPolicy policy)
136 ObserverListThreadSafe(const ObserverListThreadSafe&) = delete;
137 ObserverListThreadSafe& operator=(const ObserverListThreadSafe&) = delete;
139 // Adds |observer| to the list. |observer| must not already be in the list.
140 AddObserverResult AddObserver(ObserverType* observer) {
141 DCHECK(SequencedTaskRunner::HasCurrentDefault())
142 << "An observer can only be registered when "
143 "SequencedTaskRunner::HasCurrentDefault. If this is in a unit test, "
144 "you're likely merely missing a "
145 "base::test::(SingleThread)TaskEnvironment in your fixture. "
146 "Otherwise, try running this code on a named thread (main/UI/IO) or "
147 "from a task posted to a base::SequencedTaskRunner or "
148 "base::SingleThreadTaskRunner.";
150 AutoLock auto_lock(lock_);
152 bool was_empty = observers_.empty();
154 // Add |observer| to the list of observers.
155 DCHECK(!Contains(observers_, observer));
156 const scoped_refptr<SequencedTaskRunner> task_runner =
157 SequencedTaskRunner::GetCurrentDefault();
158 // Each observer gets a unique identifier. These unique identifiers are used
159 // to avoid execution of pending posted-tasks over removed or released
161 const size_t observer_id = ++observer_id_counter_;
163 ObserverTaskRunnerInfo task_info = {task_runner, base::debug::StackTrace(),
166 ObserverTaskRunnerInfo task_info = {task_runner, observer_id};
168 observers_[observer] = std::move(task_info);
170 // If this is called while a notification is being dispatched on this thread
171 // and |policy_| is ALL, |observer| must be notified (if a notification is
172 // being dispatched on another thread in parallel, the notification may or
173 // may not make it to |observer| depending on the outcome of the race to
175 if (policy_ == ObserverListPolicy::ALL) {
176 if (const NotificationDataBase* const current_notification =
177 GetCurrentNotification();
178 current_notification && current_notification->observer_list == this) {
179 const NotificationData* notification_data =
180 static_cast<const NotificationData*>(current_notification);
181 task_runner->PostTask(
182 current_notification->from_here,
183 BindOnce(&Self::NotifyWrapper, this,
184 // While `observer` may be dangling, we pass it and
185 // check it wasn't deallocated in NotifyWrapper() which can
186 // check `observers_` to verify presence (the owner of the
187 // observer is responsible for removing it from that list
188 // before deallocation).
189 UnsafeDangling(observer),
190 NotificationData(this, observer_id,
191 current_notification->from_here,
192 notification_data->method)));
196 return was_empty ? AddObserverResult::kBecameNonEmpty
197 : AddObserverResult::kWasAlreadyNonEmpty;
200 // Remove an observer from the list if it is in the list.
202 // If a notification was sent to the observer but hasn't started to run yet,
203 // it will be aborted. If a notification has started to run, removing the
204 // observer won't stop it.
205 RemoveObserverResult RemoveObserver(ObserverType* observer) {
206 AutoLock auto_lock(lock_);
207 if constexpr (RemovePolicy == RemoveObserverPolicy::kAddingSequenceOnly) {
208 const auto it = observers_.find(observer);
209 CHECK(it == observers_.end() ||
210 it->second.task_runner->RunsTasksInCurrentSequence());
212 observers_.erase(observer);
213 return observers_.empty() ? RemoveObserverResult::kWasOrBecameEmpty
214 : RemoveObserverResult::kRemainsNonEmpty;
217 // Verifies that the list is currently empty (i.e. there are no observers).
218 void AssertEmpty() const {
220 AutoLock auto_lock(lock_);
221 bool observers_is_empty = observers_.empty();
222 DUMP_WILL_BE_CHECK(observers_is_empty)
224 << GetObserversCreationStackStringLocked();
228 // Asynchronously invokes a callback on all observers, on their registration
229 // sequence. You cannot assume that at the completion of the Notify call that
230 // all Observers have been Notified. The notification may still be pending
232 template <typename Method, typename... Params>
233 void Notify(const Location& from_here, Method m, Params&&... params) {
234 RepeatingCallback<void(ObserverType*)> method =
235 BindRepeating(&Dispatcher<ObserverType, Method>::Run, m,
236 std::forward<Params>(params)...);
238 AutoLock lock(lock_);
239 for (const auto& observer : observers_) {
240 observer.second.task_runner->PostTask(
242 BindOnce(&Self::NotifyWrapper, this,
243 // While `observer.first` may be dangling, we pass it and
244 // check it wasn't deallocated in NotifyWrapper() which can
245 // check `observers_` to verify presence (the owner of the
246 // observer is responsible for removing it from that list
247 // before deallocation).
248 UnsafeDangling(observer.first),
249 NotificationData(this, observer.second.observer_id,
250 from_here, method)));
255 friend class RefCountedThreadSafe<ObserverListThreadSafeBase>;
257 struct NotificationData : public NotificationDataBase {
258 NotificationData(ObserverListThreadSafe* observer_list_in,
259 size_t observer_id_in,
260 const Location& from_here_in,
261 const RepeatingCallback<void(ObserverType*)>& method_in)
262 : NotificationDataBase(observer_list_in, from_here_in),
264 observer_id(observer_id_in) {}
266 RepeatingCallback<void(ObserverType*)> method;
270 ~ObserverListThreadSafe() override = default;
272 void NotifyWrapper(MayBeDangling<ObserverType> observer,
273 const NotificationData& notification) {
275 AutoLock auto_lock(lock_);
277 // Check whether the observer still needs a notification.
278 DCHECK_EQ(notification.observer_list, this);
279 auto it = observers_.find(observer);
280 if (it == observers_.end() ||
281 it->second.observer_id != notification.observer_id) {
284 DCHECK(it->second.task_runner->RunsTasksInCurrentSequence());
287 // Keep track of the notification being dispatched on the current thread.
288 // This will be used if the callback below calls AddObserver().
290 // Note: GetCurrentNotification() may not return null if this runs in a
291 // nested loop started by a notification callback. In that case, it is
292 // important to save the previous value to restore it later.
293 const AutoReset<const NotificationDataBase*> resetter_(
294 &GetCurrentNotification(), ¬ification);
296 // Invoke the callback.
297 notification.method.Run(observer);
300 std::string GetObserversCreationStackStringLocked() const
301 EXCLUSIVE_LOCKS_REQUIRED(lock_) {
304 for (const auto& observer : observers_) {
306 {observer.second.add_observer_stack_.ToString(), "\n"});
312 const ObserverListPolicy policy_ = ObserverListPolicy::ALL;
316 size_t observer_id_counter_ GUARDED_BY(lock_) = 0;
318 struct ObserverTaskRunnerInfo {
319 scoped_refptr<SequencedTaskRunner> task_runner;
321 base::debug::StackTrace add_observer_stack_;
323 size_t observer_id = 0;
326 // Keys are observers. Values are the SequencedTaskRunners on which they must
328 std::unordered_map<ObserverType*, ObserverTaskRunnerInfo> observers_
334 #endif // BASE_OBSERVER_LIST_THREADSAFE_H_