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