1// Copyright 2013 The Chromium Authors. All rights reserved.
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_CALLBACK_LIST_H_
6#define BASE_CALLBACK_LIST_H_
7
8#include <list>
9
10#include "base/callback.h"
11#include "base/callback_internal.h"
12#include "base/compiler_specific.h"
13#include "base/logging.h"
14#include "base/macros.h"
15#include "base/memory/scoped_ptr.h"
16
17// OVERVIEW:
18//
19// A container for a list of callbacks.  Unlike a normal STL vector or list,
20// this container can be modified during iteration without invalidating the
21// iterator. It safely handles the case of a callback removing itself
22// or another callback from the list while callbacks are being run.
23//
24// TYPICAL USAGE:
25//
26// class MyWidget {
27//  public:
28//   ...
29//
30//   typedef base::Callback<void(const Foo&)> OnFooCallback;
31//
32//   scoped_ptr<base::CallbackList<void(const Foo&)>::Subscription>
33//   RegisterCallback(const OnFooCallback& cb) {
34//     return callback_list_.Add(cb);
35//   }
36//
37//  private:
38//   void NotifyFoo(const Foo& foo) {
39//      callback_list_.Notify(foo);
40//   }
41//
42//   base::CallbackList<void(const Foo&)> callback_list_;
43//
44//   DISALLOW_COPY_AND_ASSIGN(MyWidget);
45// };
46//
47//
48// class MyWidgetListener {
49//  public:
50//   MyWidgetListener::MyWidgetListener() {
51//     foo_subscription_ = MyWidget::GetCurrent()->RegisterCallback(
52//             base::Bind(&MyWidgetListener::OnFoo, this)));
53//   }
54//
55//   MyWidgetListener::~MyWidgetListener() {
56//      // Subscription gets deleted automatically and will deregister
57//      // the callback in the process.
58//   }
59//
60//  private:
61//   void OnFoo(const Foo& foo) {
62//     // Do something.
63//   }
64//
65//   scoped_ptr<base::CallbackList<void(const Foo&)>::Subscription>
66//       foo_subscription_;
67//
68//   DISALLOW_COPY_AND_ASSIGN(MyWidgetListener);
69// };
70
71namespace base {
72
73namespace internal {
74
75template <typename CallbackType>
76class CallbackListBase {
77 public:
78  class Subscription {
79   public:
80    Subscription(CallbackListBase<CallbackType>* list,
81                 typename std::list<CallbackType>::iterator iter)
82        : list_(list),
83          iter_(iter) {
84    }
85
86    ~Subscription() {
87      if (list_->active_iterator_count_) {
88        iter_->Reset();
89      } else {
90        list_->callbacks_.erase(iter_);
91        if (!list_->removal_callback_.is_null())
92          list_->removal_callback_.Run();
93      }
94    }
95
96   private:
97    CallbackListBase<CallbackType>* list_;
98    typename std::list<CallbackType>::iterator iter_;
99
100    DISALLOW_COPY_AND_ASSIGN(Subscription);
101  };
102
103  // Add a callback to the list. The callback will remain registered until the
104  // returned Subscription is destroyed, which must occur before the
105  // CallbackList is destroyed.
106  scoped_ptr<Subscription> Add(const CallbackType& cb) WARN_UNUSED_RESULT {
107    DCHECK(!cb.is_null());
108    return scoped_ptr<Subscription>(
109        new Subscription(this, callbacks_.insert(callbacks_.end(), cb)));
110  }
111
112  // Sets a callback which will be run when a subscription list is changed.
113  void set_removal_callback(const Closure& callback) {
114    removal_callback_ = callback;
115  }
116
117  // Returns true if there are no subscriptions. This is only valid to call when
118  // not looping through the list.
119  bool empty() {
120    DCHECK_EQ(0, active_iterator_count_);
121    return callbacks_.empty();
122  }
123
124 protected:
125  // An iterator class that can be used to access the list of callbacks.
126  class Iterator {
127   public:
128    explicit Iterator(CallbackListBase<CallbackType>* list)
129        : list_(list),
130          list_iter_(list_->callbacks_.begin()) {
131      ++list_->active_iterator_count_;
132    }
133
134    Iterator(const Iterator& iter)
135        : list_(iter.list_),
136          list_iter_(iter.list_iter_) {
137      ++list_->active_iterator_count_;
138    }
139
140    ~Iterator() {
141      if (list_ && --list_->active_iterator_count_ == 0) {
142        list_->Compact();
143      }
144    }
145
146    CallbackType* GetNext() {
147      while ((list_iter_ != list_->callbacks_.end()) && list_iter_->is_null())
148        ++list_iter_;
149
150      CallbackType* cb = NULL;
151      if (list_iter_ != list_->callbacks_.end()) {
152        cb = &(*list_iter_);
153        ++list_iter_;
154      }
155      return cb;
156    }
157
158   private:
159    CallbackListBase<CallbackType>* list_;
160    typename std::list<CallbackType>::iterator list_iter_;
161  };
162
163  CallbackListBase() : active_iterator_count_(0) {}
164
165  ~CallbackListBase() {
166    DCHECK_EQ(0, active_iterator_count_);
167    DCHECK_EQ(0U, callbacks_.size());
168  }
169
170  // Returns an instance of a CallbackListBase::Iterator which can be used
171  // to run callbacks.
172  Iterator GetIterator() {
173    return Iterator(this);
174  }
175
176  // Compact the list: remove any entries which were NULLed out during
177  // iteration.
178  void Compact() {
179    typename std::list<CallbackType>::iterator it = callbacks_.begin();
180    bool updated = false;
181    while (it != callbacks_.end()) {
182      if ((*it).is_null()) {
183        updated = true;
184        it = callbacks_.erase(it);
185      } else {
186        ++it;
187      }
188    }
189
190    if (updated && !removal_callback_.is_null())
191      removal_callback_.Run();
192  }
193
194 private:
195  std::list<CallbackType> callbacks_;
196  int active_iterator_count_;
197  Closure removal_callback_;
198
199  DISALLOW_COPY_AND_ASSIGN(CallbackListBase);
200};
201
202}  // namespace internal
203
204template <typename Sig> class CallbackList;
205
206template <typename... Args>
207class CallbackList<void(Args...)>
208    : public internal::CallbackListBase<Callback<void(Args...)> > {
209 public:
210  typedef Callback<void(Args...)> CallbackType;
211
212  CallbackList() {}
213
214  void Notify(
215      typename internal::CallbackParamTraits<Args>::ForwardType... args) {
216    typename internal::CallbackListBase<CallbackType>::Iterator it =
217        this->GetIterator();
218    CallbackType* cb;
219    while ((cb = it.GetNext()) != NULL) {
220      cb->Run(args...);
221    }
222  }
223
224 private:
225  DISALLOW_COPY_AND_ASSIGN(CallbackList);
226};
227
228}  // namespace base
229
230#endif  // BASE_CALLBACK_LIST_H_
231