Avi Drissman | e4622aa | 2022-09-08 20:36:06 | [diff] [blame] | 1 | // Copyright 2011 The Chromium Authors |
license.bot | bf09a50 | 2008-08-24 00:55:55 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 4 | |
tfarina | a3116351 | 2015-05-13 22:10:15 | [diff] [blame] | 5 | #ifndef BASE_OBSERVER_LIST_H_ |
| 6 | #define BASE_OBSERVER_LIST_H_ |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 7 | |
avi | 9b6f4293 | 2015-12-26 22:15:14 | [diff] [blame] | 8 | #include <stddef.h> |
| 9 | |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 10 | #include <algorithm> |
jdoerrie | 977dc3f | 2017-12-12 09:23:34 | [diff] [blame] | 11 | #include <iterator> |
sky@google.com | b3e2fad0 | 2008-10-31 03:32:06 | [diff] [blame] | 12 | #include <limits> |
Lei Zhang | 91450eb | 2021-12-24 02:11:11 | [diff] [blame] | 13 | #include <ostream> |
Sumaid Syed | 22f60eeb | 2021-08-26 05:16:26 | [diff] [blame] | 14 | #include <string> |
jdoerrie | 977dc3f | 2017-12-12 09:23:34 | [diff] [blame] | 15 | #include <utility> |
sky@google.com | b3e2fad0 | 2008-10-31 03:32:06 | [diff] [blame] | 16 | #include <vector> |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 17 | |
David Sanders | 8cfb63a | 2022-04-14 19:36:30 | [diff] [blame] | 18 | #include "base/check.h" |
Hans Wennborg | 7b53371 | 2020-06-22 20:52:27 | [diff] [blame] | 19 | #include "base/check_op.h" |
Lei Zhang | a28deed | 2023-09-28 17:44:31 | [diff] [blame] | 20 | #include "base/containers/cxx20_erase_vector.h" |
David Sanders | fc1f17fa | 2022-04-15 00:15:49 | [diff] [blame] | 21 | #include "base/dcheck_is_on.h" |
Peter Boström | 807a2a6b | 2023-04-08 15:20:49 | [diff] [blame] | 22 | #include "base/debug/dump_without_crashing.h" |
Hans Wennborg | 7b53371 | 2020-06-22 20:52:27 | [diff] [blame] | 23 | #include "base/notreached.h" |
Trent Apted | 30f97fd | 2018-08-21 09:03:47 | [diff] [blame] | 24 | #include "base/observer_list_internal.h" |
Anton Bikineev | a61fb57 | 2020-10-18 08:54:44 | [diff] [blame] | 25 | #include "base/ranges/algorithm.h" |
Trent Apted | f59cdf0d3 | 2018-10-04 03:42:22 | [diff] [blame] | 26 | #include "base/sequence_checker.h" |
Sylvain Defresne | a86596e5 | 2023-01-13 18:55:24 | [diff] [blame] | 27 | #include "build/build_config.h" |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 28 | |
| 29 | /////////////////////////////////////////////////////////////////////////////// |
| 30 | // |
| 31 | // OVERVIEW: |
| 32 | // |
François Degros | d6e2d7dd | 2017-11-22 05:37:02 | [diff] [blame] | 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 | // |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 47 | // |
| 48 | // TYPICAL USAGE: |
| 49 | // |
| 50 | // class MyWidget { |
| 51 | // public: |
| 52 | // ... |
| 53 | // |
Rayan Kanso | d26a60d | 2019-02-27 16:21:40 | [diff] [blame] | 54 | // class Observer : public base::CheckedObserver { |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 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) { |
François Degros | d6e2d7dd | 2017-11-22 05:37:02 | [diff] [blame] | 61 | // observers_.AddObserver(obs); |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 62 | // } |
| 63 | // |
Evan Stade | 6bd38d4 | 2019-10-30 17:55:23 | [diff] [blame] | 64 | // void RemoveObserver(Observer* obs) { |
François Degros | d6e2d7dd | 2017-11-22 05:37:02 | [diff] [blame] | 65 | // observers_.RemoveObserver(obs); |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 66 | // } |
| 67 | // |
| 68 | // void NotifyFoo() { |
François Degros | d6e2d7dd | 2017-11-22 05:37:02 | [diff] [blame] | 69 | // for (Observer& obs : observers_) |
| 70 | // obs.OnFoo(this); |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 71 | // } |
| 72 | // |
| 73 | // void NotifyBar(int x, int y) { |
François Degros | d6e2d7dd | 2017-11-22 05:37:02 | [diff] [blame] | 74 | // for (Observer& obs : observers_) |
| 75 | // obs.OnBar(this, x, y); |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 76 | // } |
| 77 | // |
| 78 | // private: |
François Degros | d6e2d7dd | 2017-11-22 05:37:02 | [diff] [blame] | 79 | // base::ObserverList<Observer> observers_; |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 80 | // }; |
| 81 | // |
sky@google.com | b3e2fad0 | 2008-10-31 03:32:06 | [diff] [blame] | 82 | // |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 83 | /////////////////////////////////////////////////////////////////////////////// |
| 84 | |
brettw | 5a1613dc | 2015-06-02 05:34:43 | [diff] [blame] | 85 | namespace base { |
| 86 | |
François Degros | d6e2d7dd | 2017-11-22 05:37:02 | [diff] [blame] | 87 | // Enumeration of which observers are notified by ObserverList. |
| 88 | enum class ObserverListPolicy { |
| 89 | // Specifies that any observers added during notification are notified. |
| 90 | // This is the default policy if no policy is provided to the constructor. |
| 91 | ALL, |
| 92 | |
| 93 | // Specifies that observers added while sending out notification are not |
| 94 | // notified. |
| 95 | EXISTING_ONLY, |
| 96 | }; |
| 97 | |
| 98 | // When check_empty is true, assert that the list is empty on destruction. |
Mitsuru Oshima | 538b1db | 2018-02-28 04:05:23 | [diff] [blame] | 99 | // When allow_reentrancy is false, iterating throught the list while already in |
| 100 | // the iteration loop will result in DCHECK failure. |
| 101 | // TODO(oshima): Change the default to non reentrant. https://crbug.com/812109 |
| 102 | template <class ObserverType, |
| 103 | bool check_empty = false, |
Trent Apted | 30f97fd | 2018-08-21 09:03:47 | [diff] [blame] | 104 | bool allow_reentrancy = true, |
| 105 | class ObserverStorageType = internal::CheckedObserverAdapter> |
Trent Apted | f59cdf0d3 | 2018-10-04 03:42:22 | [diff] [blame] | 106 | class ObserverList { |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 107 | public: |
Trent Apted | 30f97fd | 2018-08-21 09:03:47 | [diff] [blame] | 108 | // Allow declaring an ObserverList<...>::Unchecked that replaces the default |
| 109 | // ObserverStorageType to use raw pointers. This is required to support legacy |
Trent Apted | 453d0b5b | 2018-08-23 00:13:22 | [diff] [blame] | 110 | // observers that do not inherit from CheckedObserver. The majority of new |
| 111 | // code should not use this, but it may be suited for performance-critical |
Trent Apted | 30f97fd | 2018-08-21 09:03:47 | [diff] [blame] | 112 | // situations to avoid overheads of a CHECK(). Note the type can't be chosen |
| 113 | // based on ObserverType's definition because ObserverLists are often declared |
| 114 | // in headers using a forward-declare of ObserverType. |
| 115 | using Unchecked = ObserverList<ObserverType, |
| 116 | check_empty, |
| 117 | allow_reentrancy, |
| 118 | internal::UncheckedObserverAdapter>; |
Trent Apted | a250ec3ab | 2018-08-19 08:52:19 | [diff] [blame] | 119 | |
loyso | 29025b6 | 2016-10-11 06:51:33 | [diff] [blame] | 120 | // An iterator class that can be used to access the list of observers. |
loyso | 29025b6 | 2016-10-11 06:51:33 | [diff] [blame] | 121 | class Iter { |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 122 | public: |
jdoerrie | 977dc3f | 2017-12-12 09:23:34 | [diff] [blame] | 123 | using iterator_category = std::forward_iterator_tag; |
| 124 | using value_type = ObserverType; |
| 125 | using difference_type = ptrdiff_t; |
| 126 | using pointer = ObserverType*; |
| 127 | using reference = ObserverType&; |
| 128 | |
François Degros | 36991ee | 2017-11-20 07:38:10 | [diff] [blame] | 129 | Iter() : index_(0), max_index_(0) {} |
loyso | 29025b6 | 2016-10-11 06:51:33 | [diff] [blame] | 130 | |
François Degros | d6e2d7dd | 2017-11-22 05:37:02 | [diff] [blame] | 131 | explicit Iter(const ObserverList* list) |
Trent Apted | f59cdf0d3 | 2018-10-04 03:42:22 | [diff] [blame] | 132 | : list_(const_cast<ObserverList*>(list)), |
François Degros | 36991ee | 2017-11-20 07:38:10 | [diff] [blame] | 133 | index_(0), |
François Degros | d6e2d7dd | 2017-11-22 05:37:02 | [diff] [blame] | 134 | max_index_(list->policy_ == ObserverListPolicy::ALL |
François Degros | 36991ee | 2017-11-20 07:38:10 | [diff] [blame] | 135 | ? std::numeric_limits<size_t>::max() |
| 136 | : list->observers_.size()) { |
Trent Apted | f59cdf0d3 | 2018-10-04 03:42:22 | [diff] [blame] | 137 | DCHECK(list); |
Peter Boström | bc4629d | 2023-05-09 03:21:38 | [diff] [blame] | 138 | // TODO(crbug.com/1423093): Turn into CHECK once very prevalent failures |
| 139 | // are weeded out. |
| 140 | DUMP_WILL_BE_CHECK(allow_reentrancy || list_.IsOnlyRemainingNode()); |
Trent Apted | f59cdf0d3 | 2018-10-04 03:42:22 | [diff] [blame] | 141 | // Bind to this sequence when creating the first iterator. |
| 142 | DCHECK_CALLED_ON_VALID_SEQUENCE(list_->iteration_sequence_checker_); |
François Degros | 36991ee | 2017-11-20 07:38:10 | [diff] [blame] | 143 | EnsureValidIndex(); |
François Degros | 36991ee | 2017-11-20 07:38:10 | [diff] [blame] | 144 | } |
| 145 | |
| 146 | ~Iter() { |
Trent Apted | f59cdf0d3 | 2018-10-04 03:42:22 | [diff] [blame] | 147 | if (list_.IsOnlyRemainingNode()) |
François Degros | 36991ee | 2017-11-20 07:38:10 | [diff] [blame] | 148 | list_->Compact(); |
| 149 | } |
| 150 | |
François Degros | 0659d6933 | 2017-11-15 09:31:44 | [diff] [blame] | 151 | Iter(const Iter& other) |
Trent Apted | f59cdf0d3 | 2018-10-04 03:42:22 | [diff] [blame] | 152 | : index_(other.index_), max_index_(other.max_index_) { |
| 153 | if (other.list_) |
| 154 | list_.SetList(other.list_.get()); |
François Degros | 0659d6933 | 2017-11-15 09:31:44 | [diff] [blame] | 155 | } |
| 156 | |
Trent Apted | f59cdf0d3 | 2018-10-04 03:42:22 | [diff] [blame] | 157 | Iter& operator=(const Iter& other) { |
| 158 | if (&other == this) |
| 159 | return *this; |
| 160 | |
| 161 | if (list_.IsOnlyRemainingNode()) |
| 162 | list_->Compact(); |
| 163 | |
| 164 | list_.Invalidate(); |
| 165 | if (other.list_) |
| 166 | list_.SetList(other.list_.get()); |
| 167 | |
| 168 | index_ = other.index_; |
| 169 | max_index_ = other.max_index_; |
François Degros | 0659d6933 | 2017-11-15 09:31:44 | [diff] [blame] | 170 | return *this; |
| 171 | } |
| 172 | |
François Degros | 36991ee | 2017-11-20 07:38:10 | [diff] [blame] | 173 | bool operator==(const Iter& other) const { |
| 174 | return (is_end() && other.is_end()) || |
| 175 | (list_.get() == other.list_.get() && index_ == other.index_); |
| 176 | } |
loyso | 29025b6 | 2016-10-11 06:51:33 | [diff] [blame] | 177 | |
François Degros | 36991ee | 2017-11-20 07:38:10 | [diff] [blame] | 178 | bool operator!=(const Iter& other) const { return !(*this == other); } |
| 179 | |
| 180 | Iter& operator++() { |
| 181 | if (list_) { |
| 182 | ++index_; |
| 183 | EnsureValidIndex(); |
| 184 | } |
| 185 | return *this; |
| 186 | } |
| 187 | |
jdoerrie | 977dc3f | 2017-12-12 09:23:34 | [diff] [blame] | 188 | Iter operator++(int) { |
| 189 | Iter it(*this); |
| 190 | ++(*this); |
| 191 | return it; |
| 192 | } |
| 193 | |
François Degros | 36991ee | 2017-11-20 07:38:10 | [diff] [blame] | 194 | ObserverType* operator->() const { |
| 195 | ObserverType* const current = GetCurrent(); |
| 196 | DCHECK(current); |
| 197 | return current; |
| 198 | } |
| 199 | |
| 200 | ObserverType& operator*() const { |
| 201 | ObserverType* const current = GetCurrent(); |
| 202 | DCHECK(current); |
| 203 | return *current; |
| 204 | } |
loyso | 29025b6 | 2016-10-11 06:51:33 | [diff] [blame] | 205 | |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 206 | private: |
Trent Apted | 30f97fd | 2018-08-21 09:03:47 | [diff] [blame] | 207 | friend class ObserverListTestBase; |
loyso | 29025b6 | 2016-10-11 06:51:33 | [diff] [blame] | 208 | |
François Degros | 36991ee | 2017-11-20 07:38:10 | [diff] [blame] | 209 | ObserverType* GetCurrent() const { |
| 210 | DCHECK(list_); |
| 211 | DCHECK_LT(index_, clamped_max_index()); |
Trent Apted | 30f97fd | 2018-08-21 09:03:47 | [diff] [blame] | 212 | return ObserverStorageType::template Get<ObserverType>( |
| 213 | list_->observers_[index_]); |
François Degros | 36991ee | 2017-11-20 07:38:10 | [diff] [blame] | 214 | } |
| 215 | |
| 216 | void EnsureValidIndex() { |
| 217 | DCHECK(list_); |
| 218 | const size_t max_index = clamped_max_index(); |
Trent Apted | 30f97fd | 2018-08-21 09:03:47 | [diff] [blame] | 219 | while (index_ < max_index && |
| 220 | list_->observers_[index_].IsMarkedForRemoval()) { |
François Degros | 36991ee | 2017-11-20 07:38:10 | [diff] [blame] | 221 | ++index_; |
Trent Apted | 30f97fd | 2018-08-21 09:03:47 | [diff] [blame] | 222 | } |
François Degros | 36991ee | 2017-11-20 07:38:10 | [diff] [blame] | 223 | } |
loyso | 29025b6 | 2016-10-11 06:51:33 | [diff] [blame] | 224 | |
| 225 | size_t clamped_max_index() const { |
| 226 | return std::min(max_index_, list_->observers_.size()); |
| 227 | } |
| 228 | |
| 229 | bool is_end() const { return !list_ || index_ == clamped_max_index(); } |
| 230 | |
Trent Apted | f59cdf0d3 | 2018-10-04 03:42:22 | [diff] [blame] | 231 | // Lightweight weak pointer to the ObserverList. |
| 232 | internal::WeakLinkNode<ObserverList> list_; |
François Degros | 36991ee | 2017-11-20 07:38:10 | [diff] [blame] | 233 | |
loyso | 29025b6 | 2016-10-11 06:51:33 | [diff] [blame] | 234 | // When initially constructed and each time the iterator is incremented, |
| 235 | // |index_| is guaranteed to point to a non-null index if the iterator |
| 236 | // has not reached the end of the ObserverList. |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 237 | size_t index_; |
sky@google.com | b3e2fad0 | 2008-10-31 03:32:06 | [diff] [blame] | 238 | size_t max_index_; |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 239 | }; |
| 240 | |
François Degros | 36991ee | 2017-11-20 07:38:10 | [diff] [blame] | 241 | using iterator = Iter; |
| 242 | using const_iterator = Iter; |
Trent Apted | 30f97fd | 2018-08-21 09:03:47 | [diff] [blame] | 243 | using value_type = ObserverType; |
loyso | 29025b6 | 2016-10-11 06:51:33 | [diff] [blame] | 244 | |
loyso | 29025b6 | 2016-10-11 06:51:33 | [diff] [blame] | 245 | const_iterator begin() const { |
François Degros | 36991ee | 2017-11-20 07:38:10 | [diff] [blame] | 246 | // An optimization: do not involve weak pointers for empty list. |
| 247 | return observers_.empty() ? const_iterator() : const_iterator(this); |
loyso | 29025b6 | 2016-10-11 06:51:33 | [diff] [blame] | 248 | } |
François Degros | 36991ee | 2017-11-20 07:38:10 | [diff] [blame] | 249 | |
loyso | 29025b6 | 2016-10-11 06:51:33 | [diff] [blame] | 250 | const_iterator end() const { return const_iterator(); } |
| 251 | |
Oskar Sundbom | c92fda6 | 2018-10-24 11:38:02 | [diff] [blame] | 252 | explicit ObserverList(ObserverListPolicy policy = ObserverListPolicy::ALL) |
| 253 | : policy_(policy) { |
Trent Apted | f59cdf0d3 | 2018-10-04 03:42:22 | [diff] [blame] | 254 | // Sequence checks only apply when iterators are live. |
| 255 | DETACH_FROM_SEQUENCE(iteration_sequence_checker_); |
| 256 | } |
David Bienvenu | b4b441e | 2020-09-23 05:49:57 | [diff] [blame] | 257 | ObserverList(const ObserverList&) = delete; |
| 258 | ObserverList& operator=(const ObserverList&) = delete; |
François Degros | d6e2d7dd | 2017-11-22 05:37:02 | [diff] [blame] | 259 | ~ObserverList() { |
Trent Apted | f59cdf0d3 | 2018-10-04 03:42:22 | [diff] [blame] | 260 | // If there are live iterators, ensure destruction is thread-safe. |
| 261 | if (!live_iterators_.empty()) |
| 262 | DCHECK_CALLED_ON_VALID_SEQUENCE(iteration_sequence_checker_); |
| 263 | |
| 264 | while (!live_iterators_.empty()) |
| 265 | live_iterators_.head()->value()->Invalidate(); |
François Degros | d6e2d7dd | 2017-11-22 05:37:02 | [diff] [blame] | 266 | if (check_empty) { |
| 267 | Compact(); |
Peter Boström | bc4629d | 2023-05-09 03:21:38 | [diff] [blame] | 268 | // TODO(crbug.com/1423093): Turn into a CHECK once very prevalent failures |
| 269 | // are weeded out. |
| 270 | DUMP_WILL_BE_CHECK(observers_.empty()) |
| 271 | << "\n" |
| 272 | << GetObserversCreationStackString(); |
François Degros | d6e2d7dd | 2017-11-22 05:37:02 | [diff] [blame] | 273 | } |
| 274 | } |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 275 | |
François Degros | 36991ee | 2017-11-20 07:38:10 | [diff] [blame] | 276 | // Add an observer to this list. An observer should not be added to the same |
| 277 | // list more than once. |
| 278 | // |
| 279 | // Precondition: obs != nullptr |
| 280 | // Precondition: !HasObserver(obs) |
| 281 | void AddObserver(ObserverType* obs) { |
| 282 | DCHECK(obs); |
Peter Boström | 807a2a6b | 2023-04-08 15:20:49 | [diff] [blame] | 283 | // TODO(crbug.com/1423093): Turn this into a CHECK once very prevalent |
| 284 | // failures are weeded out. |
Xiyuan Xia | 406bc1e | 2023-02-22 07:32:22 | [diff] [blame] | 285 | if (HasObserver(obs)) { |
| 286 | NOTREACHED() << "Observers can only be added once!"; |
| 287 | return; |
| 288 | } |
Mitsuru Oshima | 71ad733 | 2020-12-04 02:55:38 | [diff] [blame] | 289 | observers_count_++; |
Trent Apted | 30f97fd | 2018-08-21 09:03:47 | [diff] [blame] | 290 | observers_.emplace_back(ObserverStorageType(obs)); |
François Degros | 36991ee | 2017-11-20 07:38:10 | [diff] [blame] | 291 | } |
willchan@chromium.org | 84aebed | 2010-02-25 03:09:41 | [diff] [blame] | 292 | |
François Degros | 36991ee | 2017-11-20 07:38:10 | [diff] [blame] | 293 | // Removes the given observer from this list. Does nothing if this observer is |
| 294 | // not in this list. |
| 295 | void RemoveObserver(const ObserverType* obs) { |
| 296 | DCHECK(obs); |
Anton Bikineev | a61fb57 | 2020-10-18 08:54:44 | [diff] [blame] | 297 | const auto it = ranges::find_if( |
| 298 | observers_, [obs](const auto& o) { return o.IsEqual(obs); }); |
François Degros | 36991ee | 2017-11-20 07:38:10 | [diff] [blame] | 299 | if (it == observers_.end()) |
| 300 | return; |
Mitsuru Oshima | 71ad733 | 2020-12-04 02:55:38 | [diff] [blame] | 301 | if (!it->IsMarkedForRemoval()) |
| 302 | observers_count_--; |
Trent Apted | f59cdf0d3 | 2018-10-04 03:42:22 | [diff] [blame] | 303 | if (live_iterators_.empty()) { |
François Degros | 36991ee | 2017-11-20 07:38:10 | [diff] [blame] | 304 | observers_.erase(it); |
Trent Apted | f59cdf0d3 | 2018-10-04 03:42:22 | [diff] [blame] | 305 | } else { |
| 306 | DCHECK_CALLED_ON_VALID_SEQUENCE(iteration_sequence_checker_); |
| 307 | it->MarkForRemoval(); |
François Degros | 36991ee | 2017-11-20 07:38:10 | [diff] [blame] | 308 | } |
| 309 | } |
willchan@chromium.org | 84aebed | 2010-02-25 03:09:41 | [diff] [blame] | 310 | |
mgiuca | 64ccf236 | 2014-11-10 06:44:23 | [diff] [blame] | 311 | // Determine whether a particular observer is in the list. |
François Degros | 36991ee | 2017-11-20 07:38:10 | [diff] [blame] | 312 | bool HasObserver(const ObserverType* obs) const { |
Trent Apted | 30f97fd | 2018-08-21 09:03:47 | [diff] [blame] | 313 | // Client code passing null could be confused by the treatment of observers |
Trent Apted | 453d0b5b | 2018-08-23 00:13:22 | [diff] [blame] | 314 | // removed mid-iteration. TODO(https://crbug.com/876588): This should |
| 315 | // probably DCHECK, but some client code currently does pass null. |
Trent Apted | 30f97fd | 2018-08-21 09:03:47 | [diff] [blame] | 316 | if (obs == nullptr) |
| 317 | return false; |
Anton Bikineev | a61fb57 | 2020-10-18 08:54:44 | [diff] [blame] | 318 | return ranges::find_if(observers_, [obs](const auto& o) { |
| 319 | return o.IsEqual(obs); |
| 320 | }) != observers_.end(); |
François Degros | 36991ee | 2017-11-20 07:38:10 | [diff] [blame] | 321 | } |
willchan@chromium.org | 84aebed | 2010-02-25 03:09:41 | [diff] [blame] | 322 | |
François Degros | 36991ee | 2017-11-20 07:38:10 | [diff] [blame] | 323 | // Removes all the observers from this list. |
| 324 | void Clear() { |
Trent Apted | f59cdf0d3 | 2018-10-04 03:42:22 | [diff] [blame] | 325 | if (live_iterators_.empty()) { |
| 326 | observers_.clear(); |
| 327 | } else { |
| 328 | DCHECK_CALLED_ON_VALID_SEQUENCE(iteration_sequence_checker_); |
Trent Apted | 30f97fd | 2018-08-21 09:03:47 | [diff] [blame] | 329 | for (auto& observer : observers_) |
| 330 | observer.MarkForRemoval(); |
François Degros | 36991ee | 2017-11-20 07:38:10 | [diff] [blame] | 331 | } |
Mitsuru Oshima | 71ad733 | 2020-12-04 02:55:38 | [diff] [blame] | 332 | observers_count_ = 0; |
François Degros | 36991ee | 2017-11-20 07:38:10 | [diff] [blame] | 333 | } |
willchan@chromium.org | 84aebed | 2010-02-25 03:09:41 | [diff] [blame] | 334 | |
Mitsuru Oshima | de680cf | 2021-01-13 19:22:18 | [diff] [blame] | 335 | bool empty() const { return !observers_count_; } |
Mitsuru Oshima | 71ad733 | 2020-12-04 02:55:38 | [diff] [blame] | 336 | |
François Degros | d6e2d7dd | 2017-11-22 05:37:02 | [diff] [blame] | 337 | private: |
Trent Apted | f59cdf0d3 | 2018-10-04 03:42:22 | [diff] [blame] | 338 | friend class internal::WeakLinkNode<ObserverList>; |
| 339 | |
Trent Apted | 30f97fd | 2018-08-21 09:03:47 | [diff] [blame] | 340 | // Compacts list of observers by removing those marked for removal. |
François Degros | 36991ee | 2017-11-20 07:38:10 | [diff] [blame] | 341 | void Compact() { |
Trent Apted | f59cdf0d3 | 2018-10-04 03:42:22 | [diff] [blame] | 342 | // Detach whenever the last iterator is destroyed. Detaching is safe because |
| 343 | // Compact() is only ever called when the last iterator is destroyed. |
| 344 | DETACH_FROM_SEQUENCE(iteration_sequence_checker_); |
| 345 | |
Sakib Shabir Tantray | f37bdad | 2023-09-25 20:52:08 | [diff] [blame] | 346 | base::EraseIf(observers_, |
| 347 | [](const auto& o) { return o.IsMarkedForRemoval(); }); |
François Degros | 36991ee | 2017-11-20 07:38:10 | [diff] [blame] | 348 | } |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 349 | |
Marc Treib | d29f5d58 | 2021-02-23 18:32:58 | [diff] [blame] | 350 | std::string GetObserversCreationStackString() const { |
Peter Boström | 7a4f04c | 2023-01-10 17:28:44 | [diff] [blame] | 351 | #if DCHECK_IS_ON() |
Marc Treib | d29f5d58 | 2021-02-23 18:32:58 | [diff] [blame] | 352 | std::string result; |
Sylvain Defresne | a86596e5 | 2023-01-13 18:55:24 | [diff] [blame] | 353 | #if BUILDFLAG(IS_IOS) |
| 354 | result += "Use go/observer-list-empty to interpret.\n"; |
| 355 | #endif |
Hans Wennborg | d3c6ab5 | 2021-04-01 09:34:51 | [diff] [blame] | 356 | for (const auto& observer : observers_) { |
| 357 | result += observer.GetCreationStackString(); |
| 358 | result += "\n"; |
| 359 | } |
Marc Treib | d29f5d58 | 2021-02-23 18:32:58 | [diff] [blame] | 360 | return result; |
Peter Boström | 51b9321 | 2021-07-09 20:24:16 | [diff] [blame] | 361 | #else |
Peter Boström | 7a4f04c | 2023-01-10 17:28:44 | [diff] [blame] | 362 | return "For observer stack traces, build with `dcheck_always_on=true`."; |
| 363 | #endif // DCHECK_IS_ON() |
Marc Treib | d29f5d58 | 2021-02-23 18:32:58 | [diff] [blame] | 364 | } |
| 365 | |
Trent Apted | 30f97fd | 2018-08-21 09:03:47 | [diff] [blame] | 366 | std::vector<ObserverStorageType> observers_; |
François Degros | 0659d6933 | 2017-11-15 09:31:44 | [diff] [blame] | 367 | |
Trent Apted | f59cdf0d3 | 2018-10-04 03:42:22 | [diff] [blame] | 368 | base::LinkedList<internal::WeakLinkNode<ObserverList>> live_iterators_; |
François Degros | 0659d6933 | 2017-11-15 09:31:44 | [diff] [blame] | 369 | |
Mitsuru Oshima | 71ad733 | 2020-12-04 02:55:38 | [diff] [blame] | 370 | size_t observers_count_{0}; |
| 371 | |
Oskar Sundbom | c92fda6 | 2018-10-24 11:38:02 | [diff] [blame] | 372 | const ObserverListPolicy policy_; |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 373 | |
Trent Apted | f59cdf0d3 | 2018-10-04 03:42:22 | [diff] [blame] | 374 | SEQUENCE_CHECKER(iteration_sequence_checker_); |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 375 | }; |
| 376 | |
Mitsuru Oshima | 538b1db | 2018-02-28 04:05:23 | [diff] [blame] | 377 | template <class ObserverType, bool check_empty = false> |
| 378 | using ReentrantObserverList = ObserverList<ObserverType, check_empty, true>; |
| 379 | |
brettw | 5a1613dc | 2015-06-02 05:34:43 | [diff] [blame] | 380 | } // namespace base |
| 381 | |
tfarina | a3116351 | 2015-05-13 22:10:15 | [diff] [blame] | 382 | #endif // BASE_OBSERVER_LIST_H_ |