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 <functional>
22 #include <list>
23 #include <map>
24 #include <memory>
25 #include <mutex>
26 #include <utility>
27 
28 #include <gz/common/config.hh>
29 #include <gz/common/events/Export.hh>
31 
32 namespace gz
33 {
34  namespace common
35  {
38  class GZ_COMMON_EVENTS_VISIBLE Event
39  {
41  public: Event();
42 
44  public: virtual ~Event();
45 
48  public: virtual void Disconnect(int _id) = 0;
49 
52  public: bool Signaled() const;
53 
56  public: void SetSignaled(bool _sig);
57 
59  private: bool signaled;
60  };
61 
63  class GZ_COMMON_EVENTS_VISIBLE Connection
64  {
68  public: Connection(Event *_e, int _i);
69 
71  public: ~Connection();
72 
75  public: int Id() const;
76 
78  private: Event *event = nullptr;
79 
81  private: int id = -1;
82 
83 #ifdef _WIN32
84 // Disable warning C4251
85 #pragma warning(push)
86 #pragma warning(disable: 4251)
87 #endif
90 #ifdef _WIN32
91 #pragma warning(pop)
92 #endif
93 
95  public: template<typename T, typename N> friend class EventT;
96  };
97 
102  template<typename T, typename N = void>
103  class EventT : public Event
104  {
105  public: using CallbackT = std::function<T>;
107  "Event callback must have void return type");
108 
110  public: EventT();
111 
113  public: virtual ~EventT();
114 
119  public: ConnectionPtr Connect(const CallbackT &_subscriber);
120 
123  public: virtual void Disconnect(int _id);
124 
127  public: unsigned int ConnectionCount() const;
128 
130  public: template<typename ... Args>
131  void operator()(Args && ... args)
132  {
133  this->Signal(std::forward<Args>(args)...);
134  }
ConnectionCount() const;
128 
130  public: template<typename ... Args>
131  void operator()(Args && ... args)
132  {
133  this->Signal(std::forward<Args>(args)...);
134  }
135 
137  public: template <typename ... Args>
138  void Signal(Args && ... args)
139  {
140  this->Cleanup();
141 
142  this->SetSignaled(true);
143  for (const auto &iter : this->connections)
144  {
145  if (iter.second->on)
146  iter.second->callback(std::forward<Args>(args)...);
147  }
148  }
149 
153  private: void Cleanup();
154 
156  private: class EventConnection
157  {
159  public: EventConnection(bool _on, const std::function<T> &_cb,
160  const ConnectionPtr &_publicConn)
161  : callback(_cb), publicConnection(_publicConn)
162  {
163  // Windows Visual Studio 2012 does not have atomic_bool constructor,
164  // so we have to set "on" using operator=
165  this->on = _on;
166  }
167 
169  public: std::atomic_bool on;
170 
172  public: std::function<T> callback;
173 
177  public: std::weak_ptr<Connection> publicConnection;
178  };
179 
182  typedef std::map<int, std::unique_ptr<EventConnection>> EvtConnectionMap;
183 
185  private: EvtConnectionMap connections;
186 
188  private: std::mutex mutex;
189 
192  connectionsToRemove;
193  };
194 
196  template<typename T, typename N>
198  : Event()
199  {
200  }
201 
203  template<typename T, typename N>
205  {
206  // Clear the Event pointer on all connections so that they are not
207  // accessed after this Event is destructed.
208  for (auto &conn : this->connections)
209  {
210  auto publicCon = conn.second->publicConnection.lock();
211  if (publicCon)
212  {
213  publicCon->event = nullptr;
214  }
215  }
216  this->connections.clear();
217  }
218 
221  template<typename T, typename N>
223  {
224  int index = 0;
225  if (!this->connections.empty())
226  {
227  auto const &iter = this->connections.rbegin();
228  index = iter->first + 1;
229  }
230  auto connection = ConnectionPtr(new Connection(this, index));
231  this->connections[index].reset(
232  new EventConnection(true, _subscriber, connection));
233  return connection;
234  }
235 
238  template<typename T, typename N>
239  unsigned int EventT<T, N>::ConnectionCount() const
240  {
241  return this->connections.size();
242  }
243 
246  template<typename T, typename N>
248  {
249  // Find the connection
250  auto const &it = this->connections.find(_id);
251 
252  if (it != this->connections.end())
253  {
254  it->second->on = false;
255  // The destructor of std::function seems to crashes if the function it
256  // points to is in a shared library and has been unloaded by the time
257  // the destructor is invoked. It's not clear whether this is a bug in
258  // the implementation of std::function or not. To avoid the crash,
259  // we call the destructor here by setting `callback = nullptr` because
260  // it is likely that EventT::Disconnect is called before the shared
261  // library is unloaded via Connection::~Connection.
262  it->second->callback = nullptr;
263  this->connectionsToRemove.push_back(it);
264  }
265  }
266 
268  template<typename T, typename N>
269  void EventT<T, N>::Cleanup()
270  {
271  std::lock_guard<std::mutex> lock(this->mutex);
272  // Remove all queue connections.
273  for (auto &conn : this->connectionsToRemove)
274  this->connections.erase(conn);
275  this->connectionsToRemove.clear();
276  }
277  }
278 }
279 #endif