Home | History | Annotate | Download | only in base
      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 
     70 namespace base {
     71 
     72 namespace internal {
     73 
     74 template <typename CallbackType>
     75 class 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 
    203 template <typename Sig> class CallbackList;
    204 
    205 template <typename... Args>
    206 class 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