GNode library (C++)
Loading...
Searching...
No Matches
event.hpp
Go to the documentation of this file.
1/* Copyright (c) 2026 Otto Link. Distributed under the terms of the GNU General
2 Public License. The full license is in the file LICENSE, distributed with
3 this software. */
4
10#pragma once
11
12#include <algorithm>
13#include <functional>
14#include <memory>
15#include <mutex>
16#include <utility>
17#include <vector>
18
19namespace gnode
20{
21
30{
31public:
32 EventConnection() = default;
33
38 EventConnection(std::function<void()> disconnect)
39 : disconnect_(std::move(disconnect))
40 {
41 }
42
45
48
51 : disconnect_(std::move(other.disconnect_))
52 {
53 other.disconnect_ = {};
54 }
55
58 {
59 if (this != &other)
60 {
61 disconnect();
62
63 disconnect_ = std::move(other.disconnect_);
64 other.disconnect_ = {};
65 }
66
67 return *this;
68 }
69
76 {
77 if (disconnect_)
78 {
80 disconnect_ = {};
81 }
82 }
83
84private:
85 std::function<void()> disconnect_;
86};
87
99template <typename... Args> class Event
100{
101public:
102 using callback_t = std::function<void(Args...)>;
103 using listener_id_t = std::size_t;
104
105 Event() = default;
106
107 Event(const Event &) = delete;
108 Event &operator=(const Event &) = delete;
109
110 Event(Event &&) noexcept = default;
111 Event &operator=(Event &&) noexcept = default;
112
120 {
121 std::lock_guard<std::recursive_mutex> lock(*mutex_);
122
123 const listener_id_t id = next_id_++;
124
125 listeners_.push_back({id, std::move(callback)});
126
127 // Capture a weak handle to the Event's lifetime token. If the Event is
128 // destroyed before the connection disconnects, the token is gone and the
129 // disconnect becomes a safe no-op (prevents UAF on listeners_).
130 std::weak_ptr<void> alive = alive_token_;
131
132 return EventConnection(
133 [this, id, alive]()
134 {
135 if (!alive.expired()) this->unsubscribe(id);
136 });
137 }
138
145 void notify(Args... args)
146 {
147 // held while the callbacks run, recursive so that they can subscribe or
148 // disconnect themselves
149 std::lock_guard<std::recursive_mutex> lock(*mutex_);
150
151 // copy to allow callbacks to disconnect themselves safely
152 auto listeners = listeners_;
153
154 for (auto &listener : listeners)
155 {
156 listener.callback(args...);
157 }
158 }
159
160private:
166
168 {
169 std::lock_guard<std::recursive_mutex> lock(*mutex_);
170
171 listeners_.erase(std::remove_if(listeners_.begin(),
172 listeners_.end(),
173 [id](const Listener &listener)
174 { return listener.id == id; }),
175 listeners_.end());
176 }
177
178 std::vector<Listener> listeners_;
179
181
182 // guards the listeners (a pointer, so that the Event stays movable)
183 std::unique_ptr<std::recursive_mutex> mutex_ =
184 std::make_unique<std::recursive_mutex>();
185
186 // Pure lifetime sentinel: destroyed together with the Event. A weak_ptr to
187 // this token lets an outliving EventConnection detect a dead Event and skip
188 // the unsubscribe (see subscribe()).
189 std::shared_ptr<void> alive_token_ = std::make_shared<char>();
190};
191
192} // namespace gnode
Manages the lifetime of a subscription to an Event.
Definition event.hpp:30
EventConnection & operator=(EventConnection &&other) noexcept
Move assignment operator.
Definition event.hpp:57
std::function< void()> disconnect_
Definition event.hpp:85
~EventConnection()
Destructor automatically triggers the disconnect action.
Definition event.hpp:44
EventConnection & operator=(const EventConnection &)=delete
EventConnection(EventConnection &&other) noexcept
Move constructor.
Definition event.hpp:50
EventConnection(std::function< void()> disconnect)
Construct a connection with a custom disconnect callback.
Definition event.hpp:38
void disconnect()
Manually disconnect the subscription.
Definition event.hpp:75
EventConnection(const EventConnection &)=delete
Thread-safe publisher-subscriber event stream.
Definition event.hpp:100
void notify(Args... args)
Notify all subscribers with the given arguments.
Definition event.hpp:145
EventConnection subscribe(callback_t callback)
Subscribe to the event.
Definition event.hpp:119
Event()=default
std::function< void(Args...)> callback_t
Definition event.hpp:102
Event & operator=(const Event &)=delete
void unsubscribe(listener_id_t id)
Definition event.hpp:167
Event(const Event &)=delete
Event(Event &&) noexcept=default
std::size_t listener_id_t
Definition event.hpp:103
std::vector< Listener > listeners_
Definition event.hpp:178
std::unique_ptr< std::recursive_mutex > mutex_
Definition event.hpp:183
std::shared_ptr< void > alive_token_
Definition event.hpp:189
listener_id_t next_id_
Definition event.hpp:180
Definition data.hpp:23
Definition event.hpp:162
callback_t callback
Definition event.hpp:164
listener_id_t id
Definition event.hpp:163