Chromium Code Reviews| Index: base/callback_list.h |
| diff --git a/base/callback_list.h b/base/callback_list.h |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..c91985237d9d8f5458f5f1585ba5daf1af4189f9 |
| --- /dev/null |
| +++ b/base/callback_list.h |
| @@ -0,0 +1,242 @@ |
| +// Copyright 2013 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#ifndef BASE_CALLBACK_LIST_H_ |
| +#define BASE_CALLBACK_LIST_H_ |
| + |
| +#include <algorithm> |
| +#include <limits> |
| +#include <vector> |
| + |
| +#include "base/basictypes.h" |
| +#include "base/bind.h" |
| +#include "base/callback.h" |
| +#include "base/memory/weak_ptr.h" |
| + |
| +/////////////////////////////////////////////////////////////////////////////// |
| +// |
| +// OVERVIEW: |
| +// |
| +// A container for a list of callbacks. Unlike a normal STL vector or list, |
| +// this container can be modified during iteration without invalidating the |
| +// iterator. So, it safely handles the case of an callback removing itself |
| +// or other callback from the list while callbacks are being run. |
| +// |
| +// TYPICAL USAGE: |
| +// |
| +// class MyWidget { |
| +// public: |
| +// ... |
| +// |
| +// typedef base::Callback<void(const Foo&)> OnFooCallback; |
| +// |
| +// base::Closure RegisterCallback(const OnFooCallback& cb) { |
| +// return callback_list_.RegisterCallback(cb); |
| +// } |
| +// |
| +// private: |
| +// void NotifyFoo(const Foo& foo) { |
| +// callback_list_.Run(foo); |
| +// } |
| +// |
| +// CallbackList<Foo> callback_list_; |
| +// }; |
| +// |
| +// |
| +// class MyWidgetListener { |
| +// public: |
| +// |
| +// MyWidgetListener::MyWidgetListener() { |
| +// remove_foo_callback_ = MyWidget::GetCurrent()->RegisterCallback( |
| +// base::Bind(&MyWidgetListener::OnFoo, this)); |
| +// } |
| +// |
| +// MyWidgetListener::~MyWidgetListener() { |
| +// remove_foo_callback_.Run(); |
| +// } |
| +// |
| +// void OnFoo(const Foo& foo) { |
| +// // Do something. |
| +// } |
| +// |
| +// private: |
| +// base::Closure remove_foo_callback_; |
| +// }; |
| +// |
| +/////////////////////////////////////////////////////////////////////////////// |
| +struct NoDetail {}; |
| + |
| +template <typename Detail> |
| +class CallbackList { |
| + public: |
| + typedef base::Callback<void(const Detail&)> CallbackType; |
| + |
| + // Enumeration of which callbacks are called. |
| + enum NotificationType { |
| + // Specifies that any callbacks added during notification are called. |
| + // This is the default type if no type is provided to the constructor. |
| + NOTIFY_ALL, |
| + |
| + // Specifies that callbacks added while sending out notifications are not |
| + // called. |
| + NOTIFY_EXISTING_ONLY |
| + }; |
| + |
| + CallbackList() |
| + : active_iterator_count_(0), |
| + type_(NOTIFY_ALL), |
| + weak_factory_(this) {} |
| + |
| + explicit CallbackList(NotificationType type) |
| + : active_iterator_count_(0), |
| + type_(type), |
| + weak_factory_(this) {} |
| + |
| + ~CallbackList() { |
| + Compact(); |
| + DCHECK_EQ(0U, size()); |
| + } |
| + |
| + // Add a callback to the list. A callback should not be added to |
| + // the same list more than once. The returned closure (used to remove the |
| + // callback from the list) is guaranteed to be safe to run. |
| + base::Closure RegisterCallback( |
| + const base::Callback<void(const Detail&)>& cb) { |
| + DCHECK(!cb.is_null()); |
| + for (size_t i = 0; i < callbacks_.size(); i++) { |
| + if (callbacks_[i].Equals(cb)) { |
| + NOTREACHED() << "Callbacks can only be added once!"; |
| + return base::Bind(&base::DoNothing); |
| + } |
| + } |
| + callbacks_.push_back(cb); |
| + base::Closure removal_cb = base::Bind( |
| + &CallbackList::RemoveCallback, GetWeakPtr(), cb); |
| + return removal_cb; |
| + } |
| + |
| + // Remove an callback from the list if it is in the list. |
| + void RemoveCallback(const base::Callback<void(const Detail&)>& cb) { |
|
awong
2013/08/27 01:01:06
Does this and HasCallback need to be exposed as pu
|
| + for (size_t i = 0; i < callbacks_.size(); i++) { |
| + if (callbacks_[i].Equals(cb)) { |
| + if (active_iterator_count_) |
| + callbacks_[i].Reset(); |
| + else |
|
awong
2013/08/27 01:01:06
I think style guide says you're supposed to use br
Mark Mentovai
2013/08/27 02:08:21
awong wrote:
awong
2013/08/27 16:51:35
Thanks for looking it up. Even so, in this case th
|
| + callbacks_.erase(callbacks_.begin() + i); |
| + return; |
| + } |
| + } |
| + } |
| + |
| + bool HasCallback(const base::Callback<void(const Detail&)>& cb) const { |
| + for (size_t i = 0; i < callbacks_.size(); ++i) { |
| + if (callbacks_[i].Equals(cb)) |
| + return true; |
| + } |
| + return false; |
| + } |
| + |
| + void Run(const Detail& details) { |
| + if (might_have_callbacks()) { |
| + RunImpl(details); |
| + } |
| + } |
| + |
| + void Clear() { |
| + if (active_iterator_count_) { |
| + for (size_t i = 0; i < callbacks_.size(); ++i) { |
| + callbacks_[i].Reset(); |
| + } |
| + } else { |
| + callbacks_.clear(); |
| + } |
| + } |
| + |
| + bool might_have_callbacks() const { |
| + return size() != 0; |
| + } |
| + |
| + private: |
| + typedef std::vector<base::Callback<void(const Detail&)> > ListType; |
| + |
| + // An iterator class that can be used to access the list of callbacks. |
| + class Iterator { |
| + public: |
| + Iterator(CallbackList<Detail>* list) |
| + : list_(list->GetWeakPtr()), |
| + index_(0), |
| + max_index_(list->type_ == NOTIFY_ALL ? |
| + std::numeric_limits<size_t>::max() : |
| + list->callbacks_.size()) { |
| + ++list_->active_iterator_count_; |
| + } |
| + |
| + ~Iterator() { |
| + if (list_.get() && --list_->active_iterator_count_ == 0) |
| + list_->Compact(); |
| + } |
| + |
| + base::Callback<void(const Detail&)> GetNext() { |
| + if (!list_.get()) |
| + return base::Callback<void(const Detail&)>(); |
| + ListType& callbacks = list_->callbacks_; |
| + size_t max_index = std::min(max_index_, callbacks.size()); |
| + while (index_ < max_index && callbacks[index_].is_null()) |
| + ++index_; |
| + return index_ < max_index ? callbacks[index_++] : |
| + base::Callback<void(const Detail&)>(); |
| + } |
| + |
| + private: |
| + base::WeakPtr<CallbackList<Detail> > list_; |
| + size_t index_; |
| + size_t max_index_; |
| + }; |
| + |
| + base::WeakPtr<CallbackList<Detail> > GetWeakPtr() { |
| + return weak_factory_.GetWeakPtr(); |
| + } |
| + |
| + void Compact() { |
| + size_t i = 0; |
| + while (i < callbacks_.size()) { |
| + if (callbacks_[i].is_null()) |
| + callbacks_.erase(callbacks_.begin() + i); |
| + else |
| + ++i; |
| + } |
| + } |
| + |
| + // TODO(caitkp): Is there a better way to do this, while still keeping |
| + // Iterator priavte? |
| + void RunImpl(const Detail& details) { |
| + Iterator it(this); |
| + CallbackType cb; |
| + while(!(cb = it.GetNext()).is_null()) |
| + cb.Run(details); |
| + } |
| + size_t size() const { return callbacks_.size(); } |
| + |
| + ListType callbacks_; |
| + int active_iterator_count_; |
| + NotificationType type_; |
| + base::WeakPtrFactory<CallbackList<Detail> > weak_factory_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(CallbackList); |
| +}; |
| + |
| +// TODO(caitkp): Figure out how to share implementation so we don't have to |
| +// rewrite the whole class in the specialization. |
| +/*template <> |
| +class CallbackList<NoDetail> { |
|
awong
2013/08/27 01:01:06
Hmm...thinking back on the mess between scoped_ptr
|
| + public: |
| + void Run() { |
| + // Specialization for NoDetails has do Run method. |
| + if (CallbackList<NoDetail>::might_have_callbacks()) |
| + RunImpl(NoDetail()); |
| + } |
| +}; |
| +*/ |
| + |
| +#endif // BASE_CALLBACK_LIST_H_ |