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 
     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 
     71 namespace base {
     72 
     73 namespace internal {
     74 
     75 template <typename CallbackType>
     76 class 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 
    204 template <typename Sig> class CallbackList;
    205 
    206 template <typename... Args>
    207 class 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