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
33namespace 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 {
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>
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