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_ |