Gazebo Common

API Reference

5.7.0
gz/common/Event.hh
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2016 Open Source Robotics Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16 */
17 #ifndef GZ_COMMON_EVENT_HH_
18 #define GZ_COMMON_EVENT_HH_
19 
20 #include <atomic>
21 #include <chrono>
22 #include <functional>
23 #include <list>
24 #include <map>
25 #include <memory>
26 #include <mutex>
27 #include <utility>
28 
29 #include <gz/common/config.hh>
30 #include <gz/common/events/Export.hh>
32 
33 namespace gz
34 {
35  namespace common
36  {
39  class GZ_COMMON_EVENTS_VISIBLE Event
40  {
42  public: Event();
43 
45  public: virtual ~Event();
46 
49  public: virtual void Disconnect(int _id) = 0;
50 
53  public: bool Signaled() const;
54 
57  public: void SetSignaled(bool _sig);
58 
60  private: bool signaled;
61  };
62 
64  class GZ_COMMON_EVENTS_VISIBLE Connection
65  {
69  public: Connection(Event *_e, int _i);
70 
72  public: ~Connection();
73 
76  public: int Id() const;
77 
79  private: Event *event = nullptr;
80 
82  private: int id = -1;
83 
84 #ifdef _WIN32
85 // Disable warning C4251
86 #pragma warning(push)
87 #pragma warning(disable: 4251)
88 #endif
91 #ifdef _WIN32
92 #pragma warning(pop)
93 #endif
94 
96  public: template<typename T, typename N> friend class EventT;
97  };
98 
103  template<typename T, typename N = void>
104  class EventT : public Event
105  {
106  public: using CallbackT = std::function<T>;
108  "Event callback must have void return type");
109 
111  public: EventT();
112 
114  public: virtual ~EventT();
115 
120  public: ConnectionPtr Connect(const CallbackT &_subscriber);
121 
124  public: virtual void Disconnect(int _id);
125 
128  public: unsigned int ConnectionCount() const;
129 
131  public: template<typename ... Args>
132  void operator()(Args && ... args)
133  {
134  this->Signal(std::forward<Args>(args)...);
135  }
136 
138  public: template <typename ... Args>
139  void Signal(Args && ... args)
140  {
141  this->Cleanup();
142 
143  this->SetSignaled(true);
144  for (const auto &iter : this->connections)
145  {
146  if (iter.second->on)
147  iter.second->callback(std::forward<Args>(args)...);
148  }
149  }
150 
154  private: void Cleanup();
155 
157  private: class EventConnection
158  {
160  public: EventConnection(bool _on, const std::function<T> &_cb,
161  const ConnectionPtr &_publicConn)
162  : callback(_cb), publicConnection(_publicConn)
163  {
164  // Windows Visual Studio 2012 does not have atomic_bool constructor,
165  // so we have to set "on" using operator=
166  this->on = _on;
167  }
168 
170  public: std::atomic_bool on;
171 
173  public: std::function<T> callback;
174 
178  public: std::weak_ptr<Connection> publicConnection;
179  };
180 
183  typedef std::map<int, std::unique_ptr<EventConnection>> EvtConnectionMap;
184 
186  private: EvtConnectionMap connections;
187 
189  private: std::mutex mutex;
190 
193  connectionsToRemove;
194  };
195 
197  template<typename T, typename N>
199  : Event()
200  {
201  }
202 
204  template<typename T, typename N>
206  {
207  // Clear the Event pointer on all connections so that they are not
208  // accessed after this Event is destructed.
209  for (auto &conn : this->connections)
210  {
211  auto publicCon = conn.second->publicConnection.lock();
212  if (publicCon)
213  {
214  publicCon->event = nullptr;
215  }
216  }
217  this->connections.clear();
218  }
219 
222  template<typename T, typename N>
224  {
225  int index = 0;
226  if (!this->connections.empty())
227  {
228  auto const &iter = this->connections.rbegin();
229  index = iter->first + 1;
230  }
231  auto connection = ConnectionPtr(new Connection(this, index));
232  this->connections[index].reset(
233  new EventConnection(true, _subscriber, connection));
234  return connection;
235  }
236 
239  template<typename T, typename N>
240  unsigned int EventT<T, N>::ConnectionCount() const
241  {
242  return this->connections.size();
243  }
244 
247  template<typename T, typename N>
249  {
250  // Find the connection
251  auto const &it = this->connections.find(_id);
252 
253  if (it != this->connections.end())
254  {
255  it->second->on = false;
256  // The destructor of std::function seems to crashes if the function it
257  // points to is in a shared library and has been unloaded by the time
258  // the destructor is invoked. It's not clear whether this is a bug in
259  // the implementation of std::function or not. To avoid the crash,
260  // we call the destructor here by setting `callback = nullptr` because
261  // it is likely that EventT::Disconnect is called before the shared
262  // library is unloaded via Connection::~Connection.
263  it->second->callback = nullptr;
264  this->connectionsToRemove.push_back(it);
265  }
266  }
267 
269  template<typename T, typename N>
270  void EventT<T, N>::Cleanup()
271  {
272  std::lock_guard<std::mutex> lock(this->mutex);
273  // Remove all queue connections.
274  for (auto &conn : this->connectionsToRemove)
275  this->connections.erase(conn);
276  this->connectionsToRemove.clear();
277  }
278  }
279 }
280 #endif