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