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-2022 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_CXX_UTIL_SIGNAL_SIGNAL_HPP
23 #define NDN_CXX_UTIL_SIGNAL_SIGNAL_HPP
24 
25 #include "ndn-cxx/util/scope.hpp"
27 
28 #include <list>
29 
30 namespace ndn {
31 namespace util {
32 namespace signal {
33 
34 class DummyExtraArg;
35 
51 template<typename Owner, typename ...TArgs>
52 class Signal : noncopyable
53 {
54 public: // API for anyone
57  typedef function<void(const TArgs&...)> Handler;
58 
59  Signal();
60 
62 
68  connect(Handler handler);
69 
76 
77 private: // API for owner
80  bool
81  isEmpty() const;
82 
90  void
91  operator()(const TArgs&... args);
92 
96  void
97  operator()(const TArgs&... args, const DummyExtraArg&);
98 
99  // make Owner a friend of Signal<Owner, ...> so that API for owner can be called
100  friend Owner;
101 
102 private: // internal implementation
103  typedef Signal<Owner, TArgs...> Self;
104 
107  struct Slot
108  {
111  Handler handler;
112 
123  shared_ptr<DisconnectFunction> disconnect;
124  };
125 
130  typedef std::list<Slot> SlotList;
131  SlotList m_slots;
132 
135  bool m_isExecuting;
136 
140  typename SlotList::iterator m_currentSlot;
141 
144  void
145  disconnect(typename SlotList::iterator it);
146 };
147 
148 template<typename Owner, typename ...TArgs>
150  : m_isExecuting(false)
151 {
152 }
153 
154 template<typename Owner, typename ...TArgs>
156 {
157  BOOST_ASSERT(!m_isExecuting);
158 }
159 
160 template<typename Owner, typename ...TArgs>
163 {
164  auto it = m_slots.insert(m_slots.end(), {std::move(handler), nullptr});
165  it->disconnect = make_shared<DisconnectFunction>([=] { disconnect(it); });
166 
167  return signal::Connection(it->disconnect);
168 }
169 
170 template<typename Owner, typename ...TArgs>
173 {
174  auto it = m_slots.insert(m_slots.end(), {nullptr, nullptr});
175  it->disconnect = make_shared<DisconnectFunction>([=] { disconnect(it); });
176  signal::Connection conn(it->disconnect);
177 
178  it->handler = [conn, handler = std::move(handler)] (const TArgs&... args) mutable {
179  handler(args...);
180  conn.disconnect();
181  };
182 
183  return conn;
184 }
185 
186 template<typename Owner, typename ...TArgs>
187 void
188 Signal<Owner, TArgs...>::disconnect(typename SlotList::iterator it)
189 {
190  if (m_isExecuting) {
191  // during signal emission, only the currently executing handler can be disconnected
192  BOOST_ASSERT_MSG(it == m_currentSlot, "cannot disconnect another handler from a handler");
193 
194  // this serves to indicate that the current slot needs to be erased from the list
195  // after it finishes executing; we cannot do it here because of bug #2333
196  m_currentSlot = m_slots.end();
197 
198  // expire all weak_ptrs, to prevent double disconnections
199  it->disconnect.reset();
200  }
201  else {
202  m_slots.erase(it);
203  }
204 }
205 
206 template<typename Owner, typename ...TArgs>
207 bool
208 Signal<Owner, TArgs...>::isEmpty() const
209 {
210  return !m_isExecuting && m_slots.empty();
211 }
212 
213 template<typename Owner, typename ...TArgs>
214 void
215 Signal<Owner, TArgs...>::operator()(const TArgs&... args)
216 {
217  BOOST_ASSERT_MSG(!m_isExecuting, "cannot emit signal from a handler");
218 
219  if (m_slots.empty()) {
220  return;
221  }
222 
223  auto guard = make_scope_exit([this] { m_isExecuting = false; });
224  m_isExecuting = true;
225 
226  auto it = m_slots.begin();
227  auto last = std::prev(m_slots.end());
228  bool isLast = false;
229  while (!isLast) {
230  m_currentSlot = it;
231  isLast = it == last;
232 
233  m_currentSlot->handler(args...);
234 
235  if (m_currentSlot == m_slots.end())
236  it = m_slots.erase(it);
237  else
238  ++it;
239  }
240 }
241 
242 template<typename Owner, typename ...TArgs>
243 void
244 Signal<Owner, TArgs...>::operator()(const TArgs&... args, const DummyExtraArg&)
245 {
246  this->operator()(args...);
247 }
248 
249 } // namespace signal
250 
251 // expose as ndn::util::Signal
252 using signal::Signal;
253 
254 } // namespace util
255 } // namespace ndn
256 
257 #endif // NDN_CXX_UTIL_SIGNAL_SIGNAL_HPP
Represents a connection to a signal.
Definition: connection.hpp:37
void disconnect()
Disconnects from the signal.
Definition: connection.cpp:36
(implementation detail) a filler for extra argument
Definition: emit.hpp:43
Provides a lightweight signal / event system.
Definition: signal.hpp:53
function< void(const TArgs &...)> Handler
Represents a function that can connect to the signal.
Definition: signal.hpp:57
Connection connect(Handler handler)
Connects a handler to the signal.
Definition: signal.hpp:162
Connection connectSingleShot(Handler handler)
Connects a single-shot handler to the signal.
Definition: signal.hpp:172
Definition: data.cpp:25