1#ifndef MUDUO_BASE_SIGNALSLOT_H
2#define MUDUO_BASE_SIGNALSLOT_H
3
4#include "Mutex.h"
5
6#include <boost/function.hpp>
7#include <boost/noncopyable.hpp>
8#include <boost/shared_ptr.hpp>
9#include <boost/weak_ptr.hpp>
10
11#include <vector>
12
13namespace muduo
14{
15
16namespace detail
17{
18
19template<typename Callback>
20struct SlotImpl;
21
22template<typename Callback>
23struct SignalImpl : boost::noncopyable
24{
25  typedef std::vector<boost::weak_ptr<SlotImpl<Callback> > > SlotList;
26
27  SignalImpl()
28    : slots_(new SlotList)
29  {
30  }
31
32  void copyOnWrite()
33  {
34    mutex_.assertLocked();
35    if (!slots_.unique())
36    {
37      slots_.reset(new SlotList(*slots_));
38    }
39    assert(slots_.unique());
40  }
41
42  void clean()
43  {
44    MutexLockGuard lock(mutex_);
45    copyOnWrite();
46    SlotList& list(*slots_);
47    typename SlotList::iterator it(list.begin());
48    while (it != list.end())
49    {
50      if (it->expired())
51      {
52        it = list.erase(it);
53      }
54      else
55      {
56        ++it;
57      }
58    }
59  }
60
61  MutexLock mutex_;
62  boost::shared_ptr<SlotList> slots_;
63};
64
65template<typename Callback>
66struct SlotImpl : boost::noncopyable
67{
68  typedef SignalImpl<Callback> Data;
69  SlotImpl(const boost::shared_ptr<Data>& data, Callback&& cb)
70    : data_(data), cb_(cb), tie_(), tied_(false)
71  {
72  }
73
74  SlotImpl(const boost::shared_ptr<Data>& data, Callback&& cb,
75           const boost::shared_ptr<void>& tie)
76    : data_(data), cb_(cb), tie_(tie), tied_(true)
77  {
78  }
79
80  ~SlotImpl()
81  {
82    boost::shared_ptr<Data> data(data_.lock());
83    if (data)
84    {
85      data->clean();
86    }
87  }
88
89  boost::weak_ptr<Data> data_;
90  Callback cb_;
91  boost::weak_ptr<void> tie_;
92  bool tied_;
93};
94
95}
96
97/// This is the handle for a slot
98///
99/// The slot will remain connected to the signal fot the life time of the
100/// returned Slot object (and its copies).
101typedef boost::shared_ptr<void> Slot;
102
103template<typename Signature>
104class Signal;
105
106template <typename RET, typename... ARGS>
107class Signal<RET(ARGS...)> : boost::noncopyable
108{
109 public:
110  typedef std::function<void (ARGS...)> Callback;
111  typedef detail::SignalImpl<Callback> SignalImpl;
112  typedef detail::SlotImpl<Callback> SlotImpl;
113
114  Signal()
115    : impl_(new SignalImpl)
116  {
117  }
118
119  ~Signal()
120  {
121  }
122
123  Slot connect(Callback&& func)
124  {
125    boost::shared_ptr<SlotImpl> slotImpl(
126        new SlotImpl(impl_, std::forward<Callback>(func)));
127    add(slotImpl);
128    return slotImpl;
129  }
130
131  Slot connect(Callback&& func, const boost::shared_ptr<void>& tie)
132  {
133    boost::shared_ptr<SlotImpl> slotImpl(new SlotImpl(impl_, func, tie));
134    add(slotImpl);
135    return slotImpl;
136  }
137
138  void call(ARGS&&... args)
139  {
140    SignalImpl& impl(*impl_);
141    boost::shared_ptr<typename SignalImpl::SlotList> slots;
142    {
143      MutexLockGuard lock(impl.mutex_);
144      slots = impl.slots_;
145    }
146    typename SignalImpl::SlotList& s(*slots);
147    for (typename SignalImpl::SlotList::const_iterator it = s.begin(); it != s.end(); ++it)
148    {
149      boost::shared_ptr<SlotImpl> slotImpl = it->lock();
150      if (slotImpl)
151      {
152        boost::shared_ptr<void> guard;
153        if (slotImpl->tied_)
154        {
155          guard = slotImpl->tie_.lock();
156          if (guard)
157          {
158            slotImpl->cb_(args...);
159          }
160        }
161        else
162        {
163          slotImpl->cb_(args...);
164        }
165      }
166    }
167  }
168
169 private:
170
171  void add(const boost::shared_ptr<SlotImpl>& slot)
172  {
173    SignalImpl& impl(*impl_);
174    {
175      MutexLockGuard lock(impl.mutex_);
176      impl.copyOnWrite();
177      impl.slots_->push_back(slot);
178    }
179  }
180
181  const boost::shared_ptr<SignalImpl> impl_;
182};
183
184}
185
186#endif // MUDUO_BASE_SIGNALSLOT_H
187