signal/signal.hpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2013-2017 Regents of the University of California.
4  *
5  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
6  *
7  * ndn-cxx library is free software: you can redistribute it and/or modify it under the
8  * terms of the GNU Lesser General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later version.
10  *
11  * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
12  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13  * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
14  *
15  * You should have received copies of the GNU General Public License and GNU Lesser
16  * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
17  * <http://www.gnu.org/licenses/>.
18  *
19  * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
20  */
21 
22 #ifndef NDN_UTIL_SIGNAL_SIGNAL_HPP
23 #define NDN_UTIL_SIGNAL_SIGNAL_HPP
24 
25 #include "connection.hpp"
26 #include <list>
27 
28 namespace ndn {
29 namespace util {
30 namespace signal {
31 
32 class DummyExtraArg;
33 
49 template<typename Owner, typename ...TArgs>
50 class Signal : noncopyable
51 {
52 public: // API for anyone
55  typedef function<void(const TArgs&...)> Handler;
56 
57  Signal();
58 
59  ~Signal();
60 
66  connect(const Handler& handler);
67 
73  connectSingleShot(const Handler& handler);
74 
75 private: // API for owner
78  bool
79  isEmpty() const;
80 
88  void
89  operator()(const TArgs&... args);
90 
94  void
95  operator()(const TArgs&... args, const DummyExtraArg&);
96 
97  // make Owner a friend of Signal<Owner, ...> so that API for owner can be called
98  friend Owner;
99 
100 private: // internal implementation
101  typedef Signal<Owner, TArgs...> Self;
102  struct Slot;
103 
108  typedef std::list<Slot> SlotList;
109 
112  struct Slot
113  {
116  Handler handler;
117 
128  shared_ptr<function<void()>> disconnect;
129  };
130 
133  SlotList m_slots;
134 
137  bool m_isExecuting;
138 
142  typename SlotList::iterator m_currentSlot;
143 
146  void
147  disconnect(typename SlotList::iterator it);
148 };
149 
150 template<typename Owner, typename ...TArgs>
152  : m_isExecuting(false)
153 {
154 }
155 
156 template<typename Owner, typename ...TArgs>
158 {
159  BOOST_ASSERT(!m_isExecuting);
160 }
161 
162 template<typename Owner, typename ...TArgs>
165 {
166  typename SlotList::iterator it = m_slots.insert(m_slots.end(), {handler, nullptr});
167  it->disconnect = make_shared<function<void()>>(bind(&Self::disconnect, this, it));
168 
169  return signal::Connection(weak_ptr<function<void()>>(it->disconnect));
170 }
171 
172 template<typename Owner, typename ...TArgs>
175 {
176  typename SlotList::iterator it = m_slots.insert(m_slots.end(), {nullptr, nullptr});
177  it->disconnect = make_shared<function<void()>>(bind(&Self::disconnect, this, it));
178  signal::Connection conn(weak_ptr<function<void()>>(it->disconnect));
179 
180  it->handler = [conn, handler] (const TArgs&... args) mutable {
181  handler(args...);
182  conn.disconnect();
183  };
184 
185  return conn;
186 }
187 
188 template<typename Owner, typename ...TArgs>
189 void
190 Signal<Owner, TArgs...>::disconnect(typename SlotList::iterator it)
191 {
192  // 'it' could be const_iterator, but gcc 4.6 doesn't support std::list::erase(const_iterator)
193 
194  if (m_isExecuting) {
195  // during signal emission, only the currently executing handler can be disconnected
196  BOOST_ASSERT_MSG(it == m_currentSlot, "cannot disconnect another handler from a handler");
197 
198  // this serves to indicate that the current slot needs to be erased from the list
199  // after it finishes executing; we cannot do it here because of bug #2333
200  m_currentSlot = m_slots.end();
201 
202  // expire all weak_ptrs, to prevent double disconnections
203  it->disconnect.reset();
204  }
205  else {
206  m_slots.erase(it);
207  }
208 }
209 
210 template<typename Owner, typename ...TArgs>
211 bool
213 {
214  return !m_isExecuting && m_slots.empty();
215 }
216 
217 template<typename Owner, typename ...TArgs>
218 void
219 Signal<Owner, TArgs...>::operator()(const TArgs&... args)
220 {
221  BOOST_ASSERT_MSG(!m_isExecuting, "cannot emit signal from a handler");
222 
223  if (m_slots.empty()) {
224  return;
225  }
226 
227  auto it = m_slots.begin();
228  auto last = std::prev(m_slots.end());
229  m_isExecuting = true;
230 
231  try {
232  bool isLast = false;
233  while (!isLast) {
234  m_currentSlot = it;
235  isLast = it == last;
236 
237  m_currentSlot->handler(args...);
238 
239  if (m_currentSlot == m_slots.end())
240  it = m_slots.erase(it);
241  else
242  ++it;
243  }
244  }
245  catch (...) {
246  m_isExecuting = false;
247  throw;
248  }
249 
250  m_isExecuting = false;
251 }
252 
253 template<typename Owner, typename ...TArgs>
254 void
255 Signal<Owner, TArgs...>::operator()(const TArgs&... args, const DummyExtraArg&)
256 {
257  this->operator()(args...);
258 }
259 
260 } // namespace signal
261 
262 // expose as ndn::util::Signal
263 using signal::Signal;
264 
265 } // namespace util
266 } // namespace ndn
267 
268 #endif // NDN_UTIL_SIGNAL_SIGNAL_HPP
Connection connectSingleShot(const Handler &handler)
connects a single-shot handler to the signal
Copyright (c) 2013-2017 Regents of the University of California.
Definition: common.hpp:66
represents a connection to a signal
Definition: connection.hpp:34
provides a lightweight signal / event system
Connection connect(const Handler &handler)
connects a handler to the signal
(implementation detail) a filler for extra argument
Definition: emit.hpp:42
function< void(const TArgs &...)> Handler
represents a function that can connect to the signal