• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2011 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.
4 
5 #ifndef BASE_OBSERVER_LIST_H_
6 #define BASE_OBSERVER_LIST_H_
7 
8 #include <stddef.h>
9 
10 #include <algorithm>
11 #include <concepts>
12 #include <iterator>
13 #include <limits>
14 #include <ostream>
15 #include <string>
16 #include <utility>
17 #include <vector>
18 
19 #include "base/check.h"
20 #include "base/check_op.h"
21 #include "base/dcheck_is_on.h"
22 #include "base/debug/dump_without_crashing.h"
23 #include "base/notreached.h"
24 #include "base/observer_list_internal.h"
25 #include "base/ranges/algorithm.h"
26 #include "base/sequence_checker.h"
27 #include "build/build_config.h"
28 
29 ///////////////////////////////////////////////////////////////////////////////
30 //
31 // OVERVIEW:
32 //
33 //   A list of observers. Unlike a standard vector or list, this container can
34 //   be modified during iteration without invalidating the iterator. So, it
35 //   safely handles the case of an observer removing itself or other observers
36 //   from the list while observers are being notified.
37 //
38 //
39 // WARNING:
40 //
41 //   ObserverList is not thread-compatible. Iterating on the same ObserverList
42 //   simultaneously in different threads is not safe, even when the ObserverList
43 //   itself is not modified.
44 //
45 //   For a thread-safe observer list, see ObserverListThreadSafe.
46 //
47 //
48 // TYPICAL USAGE:
49 //
50 //   class MyWidget {
51 //    public:
52 //     ...
53 //
54 //     class Observer : public base::CheckedObserver {
55 //      public:
56 //       virtual void OnFoo(MyWidget* w) = 0;
57 //       virtual void OnBar(MyWidget* w, int x, int y) = 0;
58 //     };
59 //
60 //     void AddObserver(Observer* obs) {
61 //       observers_.AddObserver(obs);
62 //     }
63 //
64 //     void RemoveObserver(Observer* obs) {
65 //       observers_.RemoveObserver(obs);
66 //     }
67 //
68 //     void NotifyFoo() {
69 //       observers_.Notify(&Observer::OnFoo, this);
70 //     }
71 //
72 //     void NotifyBar(int x, int y) {
73 //       // Use manual iteration when Notify() is not suitable, e.g.
74 //       // if passing different args to different observers is needed.
75 //       for (Observer& obs : observers_) {
76 //         gfx::Point local_point = GetLocalPoint(obs, x, y);
77 //         obs.OnBar(this, local_point.x(), local_point.y());
78 //       }
79 //     }
80 //
81 //    private:
82 //     base::ObserverList<Observer> observers_;
83 //   };
84 //
85 //
86 ///////////////////////////////////////////////////////////////////////////////
87 
88 namespace base {
89 
90 // Enumeration of which observers are notified by ObserverList.
91 enum class ObserverListPolicy {
92   // Specifies that any observers added during notification are notified.
93   // This is the default policy if no policy is provided to the constructor.
94   ALL,
95 
96   // Specifies that observers added while sending out notification are not
97   // notified.
98   EXISTING_ONLY,
99 };
100 
101 // When check_empty is true, assert that the list is empty on destruction.
102 // When allow_reentrancy is false, iterating throught the list while already in
103 // the iteration loop will result in DCHECK failure.
104 // TODO(oshima): Change the default to non reentrant. https://crbug.com/812109
105 template <class ObserverType,
106           bool check_empty = false,
107           bool allow_reentrancy = true,
108           class ObserverStorageType = internal::CheckedObserverAdapter>
109 class ObserverList {
110  public:
111   // Allow declaring an ObserverList<...>::Unchecked that replaces the default
112   // ObserverStorageType to use `raw_ptr<T>`. This is required to support legacy
113   // observers that do not inherit from CheckedObserver. The majority of new
114   // code should not use this, but it may be suited for performance-critical
115   // situations to avoid overheads of a CHECK(). Note the type can't be chosen
116   // based on ObserverType's definition because ObserverLists are often declared
117   // in headers using a forward-declare of ObserverType.
118   using Unchecked = ObserverList<ObserverType,
119                                  check_empty,
120                                  allow_reentrancy,
121                                  internal::UncheckedObserverAdapter<>>;
122   // Allow declaring an ObserverList<...>::UncheckedAndDanglingUntriaged that
123   // replaces the default ObserverStorageType to use
124   // `raw_ptr<T, DanglingUntriaged>`. New use of this alias is strongly
125   // discouraged.
126   // TODO(crbug.com/40212619): Triage existing dangling observer pointers and
127   // remove this alias.
128   using UncheckedAndDanglingUntriaged =
129       ObserverList<ObserverType,
130                    check_empty,
131                    allow_reentrancy,
132                    internal::UncheckedObserverAdapter<DanglingUntriaged>>;
133 
134   // Allow declaring an ObserverList<...>::UncheckedAndRawPtrExcluded that
135   // holds raw pointers without raw_ptr<>. This is used to avoid performance
136   // regressions caused by ObserverList<T>::Unchecked's raw_ptr<> operations.
137   // Use of this alias is managed by chrome-memory-safety@google.com.
138   using UncheckedAndRawPtrExcluded = ObserverList<
139       ObserverType,
140       check_empty,
141       allow_reentrancy,
142       internal::UncheckedObserverAdapter<RawPtrTraits::kEmpty, true>>;
143 
144   // An iterator class that can be used to access the list of observers.
145   class Iter {
146    public:
147     using iterator_category = std::forward_iterator_tag;
148     using value_type = ObserverType;
149     using difference_type = ptrdiff_t;
150     using pointer = ObserverType*;
151     using reference = ObserverType&;
152 
Iter()153     Iter() : index_(0), max_index_(0) {}
154 
Iter(const ObserverList * list)155     explicit Iter(const ObserverList* list)
156         : list_(const_cast<ObserverList*>(list)),
157           index_(0),
158           max_index_(list->policy_ == ObserverListPolicy::ALL
159                          ? std::numeric_limits<size_t>::max()
160                          : list->observers_.size()) {
161       DCHECK(list);
162       // TODO(crbug.com/40063488): Turn into CHECK once very prevalent failures
163       // are weeded out.
164       DUMP_WILL_BE_CHECK(allow_reentrancy || list_.IsOnlyRemainingNode());
165       // Bind to this sequence when creating the first iterator.
166       DCHECK_CALLED_ON_VALID_SEQUENCE(list_->iteration_sequence_checker_);
167       EnsureValidIndex();
168     }
169 
~Iter()170     ~Iter() {
171       if (list_.IsOnlyRemainingNode())
172         list_->Compact();
173     }
174 
Iter(const Iter & other)175     Iter(const Iter& other)
176         : index_(other.index_), max_index_(other.max_index_) {
177       if (other.list_)
178         list_.SetList(other.list_.get());
179     }
180 
181     Iter& operator=(const Iter& other) {
182       if (&other == this)
183         return *this;
184 
185       if (list_.IsOnlyRemainingNode())
186         list_->Compact();
187 
188       list_.Invalidate();
189       if (other.list_)
190         list_.SetList(other.list_.get());
191 
192       index_ = other.index_;
193       max_index_ = other.max_index_;
194       return *this;
195     }
196 
197     bool operator==(const Iter& other) const {
198       return (is_end() && other.is_end()) ||
199              (list_.get() == other.list_.get() && index_ == other.index_);
200     }
201 
202     bool operator!=(const Iter& other) const { return !(*this == other); }
203 
204     Iter& operator++() {
205       if (list_) {
206         ++index_;
207         EnsureValidIndex();
208       }
209       return *this;
210     }
211 
212     Iter operator++(int) {
213       Iter it(*this);
214       ++(*this);
215       return it;
216     }
217 
218     ObserverType* operator->() const {
219       ObserverType* const current = GetCurrent();
220       DCHECK(current);
221       return current;
222     }
223 
224     ObserverType& operator*() const {
225       ObserverType* const current = GetCurrent();
226       DCHECK(current);
227       return *current;
228     }
229 
230    private:
231     friend class ObserverListTestBase;
232 
GetCurrent()233     ObserverType* GetCurrent() const {
234       DCHECK(list_);
235       DCHECK_LT(index_, clamped_max_index());
236       return ObserverStorageType::template Get<ObserverType>(
237           list_->observers_[index_]);
238     }
239 
EnsureValidIndex()240     void EnsureValidIndex() {
241       DCHECK(list_);
242       const size_t max_index = clamped_max_index();
243       while (index_ < max_index &&
244              list_->observers_[index_].IsMarkedForRemoval()) {
245         ++index_;
246       }
247     }
248 
clamped_max_index()249     size_t clamped_max_index() const {
250       return std::min(max_index_, list_->observers_.size());
251     }
252 
is_end()253     bool is_end() const { return !list_ || index_ == clamped_max_index(); }
254 
255     // Lightweight weak pointer to the ObserverList.
256     internal::WeakLinkNode<ObserverList> list_;
257 
258     // When initially constructed and each time the iterator is incremented,
259     // |index_| is guaranteed to point to a non-null index if the iterator
260     // has not reached the end of the ObserverList.
261     size_t index_;
262     size_t max_index_;
263   };
264 
265   using iterator = Iter;
266   using const_iterator = Iter;
267   using value_type = ObserverType;
268 
begin()269   const_iterator begin() const {
270     DCHECK_CALLED_ON_VALID_SEQUENCE(iteration_sequence_checker_);
271     // An optimization: do not involve weak pointers for empty list.
272     return observers_.empty() ? const_iterator() : const_iterator(this);
273   }
274 
end()275   const_iterator end() const { return const_iterator(); }
276 
277   explicit ObserverList(ObserverListPolicy policy = ObserverListPolicy::ALL)
policy_(policy)278       : policy_(policy) {
279     // Sequence checks only apply when iterators are live.
280     DETACH_FROM_SEQUENCE(iteration_sequence_checker_);
281   }
282   ObserverList(const ObserverList&) = delete;
283   ObserverList& operator=(const ObserverList&) = delete;
~ObserverList()284   ~ObserverList() {
285     // If there are live iterators, ensure destruction is thread-safe.
286     if (!live_iterators_.empty())
287       DCHECK_CALLED_ON_VALID_SEQUENCE(iteration_sequence_checker_);
288 
289     while (!live_iterators_.empty())
290       live_iterators_.head()->value()->Invalidate();
291     if (check_empty) {
292       Compact();
293       // TODO(crbug.com/40063488): Turn into a CHECK once very prevalent
294       // failures are weeded out.
295       DUMP_WILL_BE_CHECK(observers_.empty())
296           << "\n"
297           << GetObserversCreationStackString();
298     }
299   }
300 
301   // Add an observer to this list. An observer should not be added to the same
302   // list more than once.
303   //
304   // Precondition: obs != nullptr
305   // Precondition: !HasObserver(obs)
AddObserver(ObserverType * obs)306   void AddObserver(ObserverType* obs) {
307     DCHECK(obs);
308     // TODO(crbug.com/40063488): Turn this into a CHECK once very prevalent
309     // failures are weeded out.
310     if (HasObserver(obs)) {
311       DUMP_WILL_BE_NOTREACHED() << "Observers can only be added once!";
312       return;
313     }
314     observers_count_++;
315     observers_.emplace_back(ObserverStorageType(obs));
316   }
317 
318   // Removes the given observer from this list. Does nothing if this observer is
319   // not in this list.
RemoveObserver(const ObserverType * obs)320   void RemoveObserver(const ObserverType* obs) {
321     DCHECK(obs);
322     const auto it = ranges::find_if(
323         observers_, [obs](const auto& o) { return o.IsEqual(obs); });
324     if (it == observers_.end())
325       return;
326     if (!it->IsMarkedForRemoval())
327       observers_count_--;
328     if (live_iterators_.empty()) {
329       observers_.erase(it);
330     } else {
331       DCHECK_CALLED_ON_VALID_SEQUENCE(iteration_sequence_checker_);
332       it->MarkForRemoval();
333     }
334   }
335 
336   // Determine whether a particular observer is in the list.
HasObserver(const ObserverType * obs)337   bool HasObserver(const ObserverType* obs) const {
338     // Client code passing null could be confused by the treatment of observers
339     // removed mid-iteration. TODO(crbug.com/40590447): This should
340     // probably DCHECK, but some client code currently does pass null.
341     if (obs == nullptr)
342       return false;
343     return ranges::find_if(observers_, [obs](const auto& o) {
344              return o.IsEqual(obs);
345            }) != observers_.end();
346   }
347 
348   // Removes all the observers from this list.
Clear()349   void Clear() {
350     if (live_iterators_.empty()) {
351       observers_.clear();
352     } else {
353       DCHECK_CALLED_ON_VALID_SEQUENCE(iteration_sequence_checker_);
354       for (auto& observer : observers_)
355         observer.MarkForRemoval();
356     }
357     observers_count_ = 0;
358   }
359 
empty()360   bool empty() const { return !observers_count_; }
361 
362   // Notifies all observers. It is safe to add and remove observers from within
363   // the notification method.
364   //
365   // Example:
366   //   // Instead of:
367   //   for (auto& observer : observers_) {
368   //     observer->OnFooChanged(x, y);
369   //   }
370   //   // You can use:
371   //   observers_.Notify(&Observer::OnFooChanged, x, y);
372   //
373   // Requirements:
374   //  - The observer method's arguments cannot be rvalue (T&&) or non-const
375   //    reference (T&).
376   //
377   // TODO(crbug.com/40727208): Consider handling return values from observer
378   // methods, which are currently ignored.
379   //
380   // TODO(crbug.com/40727208): Consider adding an overload that supports
381   // move-only arguments by requiring `args` to be callable objects that
382   // returns a value of the desired type.
383   template <typename Method, typename... Args>
384     requires std::invocable<Method, ObserverType*, const Args&...>
Notify(Method method,const Args &...args)385   void Notify(Method method, const Args&... args) {
386     for (auto& observer : *this) {
387       std::invoke(method, observer, args...);
388     }
389   }
390 
391  private:
392   friend class internal::WeakLinkNode<ObserverList>;
393 
394   // Compacts list of observers by removing those marked for removal.
Compact()395   void Compact() {
396     // Detach whenever the last iterator is destroyed. Detaching is safe because
397     // Compact() is only ever called when the last iterator is destroyed.
398     DETACH_FROM_SEQUENCE(iteration_sequence_checker_);
399 
400     std::erase_if(observers_,
401                   [](const auto& o) { return o.IsMarkedForRemoval(); });
402   }
403 
GetObserversCreationStackString()404   std::string GetObserversCreationStackString() const {
405 #if DCHECK_IS_ON()
406     std::string result;
407 #if BUILDFLAG(IS_IOS)
408     result += "Use go/observer-list-empty to interpret.\n";
409 #endif
410     for (const auto& observer : observers_) {
411       result += observer.GetCreationStackString();
412       result += "\n";
413     }
414     return result;
415 #else
416     return "For observer stack traces, build with `dcheck_always_on=true`.";
417 #endif  // DCHECK_IS_ON()
418   }
419 
420   std::vector<ObserverStorageType> observers_;
421 
422   base::LinkedList<internal::WeakLinkNode<ObserverList>> live_iterators_;
423 
424   size_t observers_count_{0};
425 
426   const ObserverListPolicy policy_;
427 
428   SEQUENCE_CHECKER(iteration_sequence_checker_);
429 };
430 
431 template <class ObserverType, bool check_empty = false>
432 using ReentrantObserverList = ObserverList<ObserverType, check_empty, true>;
433 
434 }  // namespace base
435 
436 #endif  // BASE_OBSERVER_LIST_H_
437