[email protected] | 631739f | 2011-06-05 07:07:12 | [diff] [blame] | 1 | // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
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> |
[email protected] | b3e2fad0 | 2008-10-31 03:32:06 | [diff] [blame] | 11 | #include <limits> |
| 12 | #include <vector> |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 13 | |
loyso | 29025b6 | 2016-10-11 06:51:33 | [diff] [blame] | 14 | #include "base/gtest_prod_util.h" |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 15 | #include "base/logging.h" |
avi | 9b6f4293 | 2015-12-26 22:15:14 | [diff] [blame] | 16 | #include "base/macros.h" |
[email protected] | 671b74d | 2011-06-09 03:41:18 | [diff] [blame] | 17 | #include "base/memory/weak_ptr.h" |
thestig | 6c335d4 | 2015-12-07 18:25:34 | [diff] [blame] | 18 | #include "base/stl_util.h" |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 19 | |
| 20 | /////////////////////////////////////////////////////////////////////////////// |
| 21 | // |
| 22 | // OVERVIEW: |
| 23 | // |
| 24 | // A container for a list of observers. Unlike a normal STL vector or list, |
| 25 | // this container can be modified during iteration without invalidating the |
| 26 | // iterator. So, it safely handles the case of an observer removing itself |
| 27 | // or other observers from the list while observers are being notified. |
| 28 | // |
| 29 | // TYPICAL USAGE: |
| 30 | // |
| 31 | // class MyWidget { |
| 32 | // public: |
| 33 | // ... |
| 34 | // |
| 35 | // class Observer { |
| 36 | // public: |
| 37 | // virtual void OnFoo(MyWidget* w) = 0; |
| 38 | // virtual void OnBar(MyWidget* w, int x, int y) = 0; |
| 39 | // }; |
| 40 | // |
| 41 | // void AddObserver(Observer* obs) { |
| 42 | // observer_list_.AddObserver(obs); |
| 43 | // } |
| 44 | // |
| 45 | // void RemoveObserver(Observer* obs) { |
| 46 | // observer_list_.RemoveObserver(obs); |
| 47 | // } |
| 48 | // |
| 49 | // void NotifyFoo() { |
loyso | 29025b6 | 2016-10-11 06:51:33 | [diff] [blame] | 50 | // for (auto& observer : observer_list_) |
| 51 | // observer.OnFoo(this); |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 52 | // } |
| 53 | // |
| 54 | // void NotifyBar(int x, int y) { |
loyso | 29025b6 | 2016-10-11 06:51:33 | [diff] [blame] | 55 | // for (FooList::iterator i = observer_list.begin(), |
| 56 | // e = observer_list.end(); i != e; ++i) |
| 57 | // i->OnBar(this, x, y); |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 58 | // } |
| 59 | // |
| 60 | // private: |
brettw | 5a1613dc | 2015-06-02 05:34:43 | [diff] [blame] | 61 | // base::ObserverList<Observer> observer_list_; |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 62 | // }; |
| 63 | // |
[email protected] | b3e2fad0 | 2008-10-31 03:32:06 | [diff] [blame] | 64 | // |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 65 | /////////////////////////////////////////////////////////////////////////////// |
| 66 | |
brettw | 5a1613dc | 2015-06-02 05:34:43 | [diff] [blame] | 67 | namespace base { |
| 68 | |
[email protected] | 84aebed | 2010-02-25 03:09:41 | [diff] [blame] | 69 | template <typename ObserverType> |
| 70 | class ObserverListThreadSafe; |
| 71 | |
| 72 | template <class ObserverType> |
[email protected] | 671b74d | 2011-06-09 03:41:18 | [diff] [blame] | 73 | class ObserverListBase |
brettw | 5a1613dc | 2015-06-02 05:34:43 | [diff] [blame] | 74 | : public SupportsWeakPtr<ObserverListBase<ObserverType>> { |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 75 | public: |
[email protected] | b3e2fad0 | 2008-10-31 03:32:06 | [diff] [blame] | 76 | // Enumeration of which observers are notified. |
| 77 | enum NotificationType { |
| 78 | // Specifies that any observers added during notification are notified. |
| 79 | // This is the default type if non type is provided to the constructor. |
| 80 | NOTIFY_ALL, |
| 81 | |
| 82 | // Specifies that observers added while sending out notification are not |
| 83 | // notified. |
| 84 | NOTIFY_EXISTING_ONLY |
| 85 | }; |
| 86 | |
loyso | 29025b6 | 2016-10-11 06:51:33 | [diff] [blame] | 87 | // An iterator class that can be used to access the list of observers. |
| 88 | template <class ContainerType> |
| 89 | class Iter { |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 90 | public: |
loyso | 29025b6 | 2016-10-11 06:51:33 | [diff] [blame] | 91 | Iter(); |
| 92 | explicit Iter(ContainerType* list); |
| 93 | ~Iter(); |
| 94 | |
loyso | 29025b6 | 2016-10-11 06:51:33 | [diff] [blame] | 95 | // A workaround for C2244. MSVC requires fully qualified type name for |
| 96 | // return type on a function definition to match a function declaration. |
| 97 | using ThisType = |
| 98 | typename ObserverListBase<ObserverType>::template Iter<ContainerType>; |
| 99 | |
| 100 | bool operator==(const Iter& other) const; |
| 101 | bool operator!=(const Iter& other) const; |
| 102 | ThisType& operator++(); |
| 103 | ObserverType* operator->() const; |
| 104 | ObserverType& operator*() const; |
| 105 | |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 106 | private: |
loyso | 29025b6 | 2016-10-11 06:51:33 | [diff] [blame] | 107 | FRIEND_TEST_ALL_PREFIXES(ObserverListTest, BasicStdIterator); |
| 108 | FRIEND_TEST_ALL_PREFIXES(ObserverListTest, StdIteratorRemoveFront); |
| 109 | |
| 110 | ObserverType* GetCurrent() const; |
| 111 | void EnsureValidIndex(); |
| 112 | |
| 113 | size_t clamped_max_index() const { |
| 114 | return std::min(max_index_, list_->observers_.size()); |
| 115 | } |
| 116 | |
| 117 | bool is_end() const { return !list_ || index_ == clamped_max_index(); } |
| 118 | |
brettw | 5a1613dc | 2015-06-02 05:34:43 | [diff] [blame] | 119 | WeakPtr<ObserverListBase<ObserverType>> list_; |
loyso | 29025b6 | 2016-10-11 06:51:33 | [diff] [blame] | 120 | // When initially constructed and each time the iterator is incremented, |
| 121 | // |index_| is guaranteed to point to a non-null index if the iterator |
| 122 | // has not reached the end of the ObserverList. |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 123 | size_t index_; |
[email protected] | b3e2fad0 | 2008-10-31 03:32:06 | [diff] [blame] | 124 | size_t max_index_; |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 125 | }; |
| 126 | |
loyso | 29025b6 | 2016-10-11 06:51:33 | [diff] [blame] | 127 | using Iterator = Iter<ObserverListBase<ObserverType>>; |
| 128 | |
| 129 | using iterator = Iter<ObserverListBase<ObserverType>>; |
| 130 | iterator begin() { |
| 131 | // An optimization: do not involve weak pointers for empty list. |
| 132 | // Note: can't use ?: operator here due to some MSVC bug (unit tests fail) |
| 133 | if (observers_.empty()) |
| 134 | return iterator(); |
| 135 | return iterator(this); |
| 136 | } |
| 137 | iterator end() { return iterator(); } |
| 138 | |
| 139 | using const_iterator = Iter<const ObserverListBase<ObserverType>>; |
| 140 | const_iterator begin() const { |
| 141 | if (observers_.empty()) |
| 142 | return const_iterator(); |
| 143 | return const_iterator(this); |
| 144 | } |
| 145 | const_iterator end() const { return const_iterator(); } |
| 146 | |
[email protected] | 84aebed | 2010-02-25 03:09:41 | [diff] [blame] | 147 | ObserverListBase() : notify_depth_(0), type_(NOTIFY_ALL) {} |
| 148 | explicit ObserverListBase(NotificationType type) |
| 149 | : notify_depth_(0), type_(type) {} |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 150 | |
[email protected] | 631739f | 2011-06-05 07:07:12 | [diff] [blame] | 151 | // Add an observer to the list. An observer should not be added to |
| 152 | // the same list more than once. |
[email protected] | 86262b2 | 2014-07-17 12:39:34 | [diff] [blame] | 153 | void AddObserver(ObserverType* obs); |
[email protected] | 84aebed | 2010-02-25 03:09:41 | [diff] [blame] | 154 | |
[email protected] | 631739f | 2011-06-05 07:07:12 | [diff] [blame] | 155 | // Remove an observer from the list if it is in the list. |
[email protected] | 86262b2 | 2014-07-17 12:39:34 | [diff] [blame] | 156 | void RemoveObserver(ObserverType* obs); |
[email protected] | 84aebed | 2010-02-25 03:09:41 | [diff] [blame] | 157 | |
mgiuca | 64ccf236 | 2014-11-10 06:44:23 | [diff] [blame] | 158 | // Determine whether a particular observer is in the list. |
| 159 | bool HasObserver(const ObserverType* observer) const; |
[email protected] | 84aebed | 2010-02-25 03:09:41 | [diff] [blame] | 160 | |
[email protected] | 86262b2 | 2014-07-17 12:39:34 | [diff] [blame] | 161 | void Clear(); |
[email protected] | 84aebed | 2010-02-25 03:09:41 | [diff] [blame] | 162 | |
[email protected] | 188a47b | 2013-08-29 03:55:13 | [diff] [blame] | 163 | protected: |
[email protected] | 121594b | 2010-04-29 18:17:29 | [diff] [blame] | 164 | size_t size() const { return observers_.size(); } |
[email protected] | 84aebed | 2010-02-25 03:09:41 | [diff] [blame] | 165 | |
[email protected] | 86262b2 | 2014-07-17 12:39:34 | [diff] [blame] | 166 | void Compact(); |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 167 | |
[email protected] | 84aebed | 2010-02-25 03:09:41 | [diff] [blame] | 168 | private: |
| 169 | friend class ObserverListThreadSafe<ObserverType>; |
| 170 | |
| 171 | typedef std::vector<ObserverType*> ListType; |
| 172 | |
| 173 | ListType observers_; |
| 174 | int notify_depth_; |
[email protected] | b3e2fad0 | 2008-10-31 03:32:06 | [diff] [blame] | 175 | NotificationType type_; |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 176 | |
loyso | 29025b6 | 2016-10-11 06:51:33 | [diff] [blame] | 177 | template <class ContainerType> |
| 178 | friend class Iter; |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 179 | |
[email protected] | 84aebed | 2010-02-25 03:09:41 | [diff] [blame] | 180 | DISALLOW_COPY_AND_ASSIGN(ObserverListBase); |
| 181 | }; |
| 182 | |
[email protected] | 86262b2 | 2014-07-17 12:39:34 | [diff] [blame] | 183 | template <class ObserverType> |
loyso | 29025b6 | 2016-10-11 06:51:33 | [diff] [blame] | 184 | template <class ContainerType> |
| 185 | ObserverListBase<ObserverType>::Iter<ContainerType>::Iter() |
| 186 | : index_(0), max_index_(0) {} |
| 187 | |
| 188 | template <class ObserverType> |
| 189 | template <class ContainerType> |
| 190 | ObserverListBase<ObserverType>::Iter<ContainerType>::Iter(ContainerType* list) |
| 191 | : list_(const_cast<ObserverListBase<ObserverType>*>(list)->AsWeakPtr()), |
[email protected] | 86262b2 | 2014-07-17 12:39:34 | [diff] [blame] | 192 | index_(0), |
danakj | 4ef352d | 2015-03-09 17:45:39 | [diff] [blame] | 193 | max_index_(list->type_ == NOTIFY_ALL ? std::numeric_limits<size_t>::max() |
| 194 | : list->observers_.size()) { |
loyso | 29025b6 | 2016-10-11 06:51:33 | [diff] [blame] | 195 | EnsureValidIndex(); |
| 196 | DCHECK(list_); |
[email protected] | 86262b2 | 2014-07-17 12:39:34 | [diff] [blame] | 197 | ++list_->notify_depth_; |
| 198 | } |
| 199 | |
| 200 | template <class ObserverType> |
loyso | 29025b6 | 2016-10-11 06:51:33 | [diff] [blame] | 201 | template <class ContainerType> |
| 202 | ObserverListBase<ObserverType>::Iter<ContainerType>::~Iter() { |
| 203 | if (list_ && --list_->notify_depth_ == 0) |
[email protected] | 86262b2 | 2014-07-17 12:39:34 | [diff] [blame] | 204 | list_->Compact(); |
| 205 | } |
| 206 | |
| 207 | template <class ObserverType> |
loyso | 29025b6 | 2016-10-11 06:51:33 | [diff] [blame] | 208 | template <class ContainerType> |
| 209 | bool ObserverListBase<ObserverType>::Iter<ContainerType>::operator==( |
| 210 | const Iter& other) const { |
| 211 | if (is_end() && other.is_end()) |
| 212 | return true; |
| 213 | return list_.get() == other.list_.get() && index_ == other.index_; |
| 214 | } |
| 215 | |
| 216 | template <class ObserverType> |
| 217 | template <class ContainerType> |
| 218 | bool ObserverListBase<ObserverType>::Iter<ContainerType>::operator!=( |
| 219 | const Iter& other) const { |
| 220 | return !operator==(other); |
| 221 | } |
| 222 | |
| 223 | template <class ObserverType> |
| 224 | template <class ContainerType> |
| 225 | typename ObserverListBase<ObserverType>::template Iter<ContainerType>& |
| 226 | ObserverListBase<ObserverType>::Iter<ContainerType>::operator++() { |
| 227 | if (list_) { |
[email protected] | 86262b2 | 2014-07-17 12:39:34 | [diff] [blame] | 228 | ++index_; |
loyso | 29025b6 | 2016-10-11 06:51:33 | [diff] [blame] | 229 | EnsureValidIndex(); |
| 230 | } |
| 231 | return *this; |
| 232 | } |
| 233 | |
| 234 | template <class ObserverType> |
| 235 | template <class ContainerType> |
| 236 | ObserverType* ObserverListBase<ObserverType>::Iter<ContainerType>::operator->() |
| 237 | const { |
| 238 | ObserverType* current = GetCurrent(); |
| 239 | DCHECK(current); |
| 240 | return current; |
| 241 | } |
| 242 | |
| 243 | template <class ObserverType> |
| 244 | template <class ContainerType> |
| 245 | ObserverType& ObserverListBase<ObserverType>::Iter<ContainerType>::operator*() |
| 246 | const { |
| 247 | ObserverType* current = GetCurrent(); |
| 248 | DCHECK(current); |
| 249 | return *current; |
| 250 | } |
| 251 | |
| 252 | template <class ObserverType> |
| 253 | template <class ContainerType> |
| 254 | ObserverType* ObserverListBase<ObserverType>::Iter<ContainerType>::GetCurrent() |
| 255 | const { |
| 256 | if (!list_) |
| 257 | return nullptr; |
| 258 | return index_ < clamped_max_index() ? list_->observers_[index_] : nullptr; |
| 259 | } |
| 260 | |
| 261 | template <class ObserverType> |
| 262 | template <class ContainerType> |
| 263 | void ObserverListBase<ObserverType>::Iter<ContainerType>::EnsureValidIndex() { |
| 264 | if (!list_) |
| 265 | return; |
| 266 | |
| 267 | size_t max_index = clamped_max_index(); |
| 268 | while (index_ < max_index && !list_->observers_[index_]) |
| 269 | ++index_; |
| 270 | } |
| 271 | |
| 272 | template <class ObserverType> |
[email protected] | 86262b2 | 2014-07-17 12:39:34 | [diff] [blame] | 273 | void ObserverListBase<ObserverType>::AddObserver(ObserverType* obs) { |
oshima | 8750dd9 | 2015-04-08 22:57:52 | [diff] [blame] | 274 | DCHECK(obs); |
thestig | 6c335d4 | 2015-12-07 18:25:34 | [diff] [blame] | 275 | if (ContainsValue(observers_, obs)) { |
[email protected] | 86262b2 | 2014-07-17 12:39:34 | [diff] [blame] | 276 | NOTREACHED() << "Observers can only be added once!"; |
| 277 | return; |
| 278 | } |
| 279 | observers_.push_back(obs); |
| 280 | } |
| 281 | |
| 282 | template <class ObserverType> |
| 283 | void ObserverListBase<ObserverType>::RemoveObserver(ObserverType* obs) { |
oshima | 8750dd9 | 2015-04-08 22:57:52 | [diff] [blame] | 284 | DCHECK(obs); |
[email protected] | 86262b2 | 2014-07-17 12:39:34 | [diff] [blame] | 285 | typename ListType::iterator it = |
| 286 | std::find(observers_.begin(), observers_.end(), obs); |
| 287 | if (it != observers_.end()) { |
| 288 | if (notify_depth_) { |
robliao | 6f5e423 | 2015-04-23 22:45:32 | [diff] [blame] | 289 | *it = nullptr; |
[email protected] | 86262b2 | 2014-07-17 12:39:34 | [diff] [blame] | 290 | } else { |
| 291 | observers_.erase(it); |
| 292 | } |
| 293 | } |
| 294 | } |
| 295 | |
| 296 | template <class ObserverType> |
mgiuca | 64ccf236 | 2014-11-10 06:44:23 | [diff] [blame] | 297 | bool ObserverListBase<ObserverType>::HasObserver( |
| 298 | const ObserverType* observer) const { |
[email protected] | 86262b2 | 2014-07-17 12:39:34 | [diff] [blame] | 299 | for (size_t i = 0; i < observers_.size(); ++i) { |
| 300 | if (observers_[i] == observer) |
| 301 | return true; |
| 302 | } |
| 303 | return false; |
| 304 | } |
| 305 | |
| 306 | template <class ObserverType> |
| 307 | void ObserverListBase<ObserverType>::Clear() { |
| 308 | if (notify_depth_) { |
| 309 | for (typename ListType::iterator it = observers_.begin(); |
| 310 | it != observers_.end(); ++it) { |
robliao | 6f5e423 | 2015-04-23 22:45:32 | [diff] [blame] | 311 | *it = nullptr; |
[email protected] | 86262b2 | 2014-07-17 12:39:34 | [diff] [blame] | 312 | } |
| 313 | } else { |
| 314 | observers_.clear(); |
| 315 | } |
| 316 | } |
| 317 | |
| 318 | template <class ObserverType> |
| 319 | void ObserverListBase<ObserverType>::Compact() { |
loyso | 29025b6 | 2016-10-11 06:51:33 | [diff] [blame] | 320 | observers_.erase(std::remove(observers_.begin(), observers_.end(), nullptr), |
| 321 | observers_.end()); |
[email protected] | 86262b2 | 2014-07-17 12:39:34 | [diff] [blame] | 322 | } |
| 323 | |
[email protected] | 84aebed | 2010-02-25 03:09:41 | [diff] [blame] | 324 | template <class ObserverType, bool check_empty = false> |
| 325 | class ObserverList : public ObserverListBase<ObserverType> { |
| 326 | public: |
| 327 | typedef typename ObserverListBase<ObserverType>::NotificationType |
| 328 | NotificationType; |
| 329 | |
| 330 | ObserverList() {} |
| 331 | explicit ObserverList(NotificationType type) |
| 332 | : ObserverListBase<ObserverType>(type) {} |
| 333 | |
| 334 | ~ObserverList() { |
| 335 | // When check_empty is true, assert that the list is empty on destruction. |
| 336 | if (check_empty) { |
| 337 | ObserverListBase<ObserverType>::Compact(); |
| 338 | DCHECK_EQ(ObserverListBase<ObserverType>::size(), 0U); |
| 339 | } |
| 340 | } |
[email protected] | fde0830 | 2011-10-13 14:13:43 | [diff] [blame] | 341 | |
| 342 | bool might_have_observers() const { |
| 343 | return ObserverListBase<ObserverType>::size() != 0; |
| 344 | } |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 345 | }; |
| 346 | |
brettw | 5a1613dc | 2015-06-02 05:34:43 | [diff] [blame] | 347 | } // namespace base |
| 348 | |
tfarina | a3116351 | 2015-05-13 22:10:15 | [diff] [blame] | 349 | #endif // BASE_OBSERVER_LIST_H_ |