Bitcoin Core 32.99.0
P2P Digital Currency
Loading...
Searching...
No Matches
btcsignals.h
Go to the documentation of this file.
1// Copyright (c) The Bitcoin Core developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5#ifndef BITCOIN_UTIL_BTCSIGNALS_H
6#define BITCOIN_UTIL_BTCSIGNALS_H
7
8#include <sync.h>
9
10#include <algorithm>
11#include <atomic>
12#include <functional>
13#include <memory>
14#include <optional>
15#include <type_traits>
16#include <utility>
17#include <vector>
18
37namespace btcsignals {
38
41{
42public:
44};
45
47class any_of
48{
49public:
50 // This is the only supported combiner with a non-void return type. As
51 // such, its behavior is embedded into the signal functor.
53};
54
55template <typename Signature, typename Combiner = null_value>
56class signal;
57
58namespace detail {
59#ifdef __cpp_lib_atomic_shared_ptr
60template <typename T>
61using atomic_shared_ptr = std::atomic<std::shared_ptr<T>>;
62#else
68template <typename T>
70{
71 std::shared_ptr<T> m_ptr;
72
73public:
74 explicit atomic_shared_ptr(std::shared_ptr<T> ptr) noexcept : m_ptr{std::move(ptr)} {}
77 std::shared_ptr<T> load() const noexcept { return std::atomic_load(&m_ptr); }
78 void store(std::shared_ptr<T> ptr) noexcept { std::atomic_store(&m_ptr, std::move(ptr)); }
79};
80#endif
81} // namespace detail
82
83/*
84 * State object representing the liveness of a registered callback.
85 * signal::connect() returns an enabled connection which can be held and
86 * disabled in the future.
87 */
89{
90 template <typename Signature, typename Combiner>
91 friend class signal;
96 {
97 friend class connection;
98
104
105 void disconnect() { m_callback.store(nullptr); }
106
107 protected:
108 explicit liveness(std::shared_ptr<const void> callback) : m_callback{std::move(callback)} {}
109
110 std::shared_ptr<const void> load() const { return m_callback.load(); }
111
112 public:
113 bool connected() const { return load() != nullptr; }
114 };
115
119 std::shared_ptr<liveness> m_state{};
120
124 explicit connection(std::shared_ptr<liveness>&& state) : m_state{std::move(state)}{}
125
126public:
130 constexpr connection() noexcept = default;
131
145 {
146 if (m_state) {
147 m_state->disconnect();
148 }
149 }
150
155 bool connected() const
156 {
157 return m_state && m_state->connected();
158 }
159};
160
161/*
162 * RAII-style connection management
163 */
165{
167
168public:
169 explicit scoped_connection(connection rhs) noexcept : m_conn{std::move(rhs)} {}
170
172
179
181 {
183 }
184
186 {
187 disconnect();
188 }
189};
190
191/*
192 * Functor for calling zero or more connected callbacks
193 */
194template <typename Signature, typename Combiner>
196{
197 using function_type = std::function<Signature>;
198
199 /*
200 * Helper struct for maintaining a callback and its associated connection liveness
201 */
203 template <typename Callable>
208
211 std::shared_ptr<const function_type> callback() const
212 {
213 return std::static_pointer_cast<const function_type>(load());
214 }
215 };
216
217 mutable Mutex m_mutex;
218
219 std::vector<std::shared_ptr<connection_holder>> m_connections GUARDED_BY(m_mutex){};
220
221public:
222 using result_type = Combiner::result_type;
223
224 constexpr signal() noexcept = default;
226
227 /*
228 * For simplicity, disable all moving/copying/assigning.
229 */
232 signal& operator=(const signal&) = delete;
233 signal& operator=(signal&&) = delete;
234
235 /*
236 * Execute all enabled callbacks for the signal. Rather than allowing for
237 * custom combiners, the behavior of any_of is hard-coded here.
238 *
239 * Callbacks which return void require special handling.
240 *
241 * In order to avoid locking during the callbacks, the list of callbacks is
242 * cached before they are called. This allows a callback to call connect(),
243 * but the newly connected callback will not be run during the current
244 * signal invocation.
245 *
246 * Note that the parameters are accepted as universal references, though
247 * they are not perfectly forwarded as that could cause a use-after-move if
248 * more than one callback is enabled.
249 */
250 template <typename... Args>
252 {
253 std::vector<std::shared_ptr<connection_holder>> connections;
254 {
255 LOCK(m_mutex);
256 connections = m_connections;
257 }
258 if constexpr (std::is_void_v<result_type>) {
259 static_assert(std::is_same_v<result_type, typename function_type::result_type>,
260 "Callback result type must be equal to the combiner result type (void).");
261 for (const auto& connection : connections) {
262 // Holding the callback pointer keeps the callback alive until
263 // the call returns, even if it is disconnected concurrently.
264 if (const auto callback{connection->callback()}) {
265 (*callback)(args...);
266 }
267 }
268 } else {
269 static_assert(std::is_same_v<Combiner, any_of>,
270 "only the any_of combiner is supported and hard-coded into this functor.");
271 static_assert(std::is_same_v<result_type, typename function_type::result_type>,
272 "Callback result type must be equal to the combiner result type (bool).");
273 result_type ret{false};
274 for (const auto& connection : connections) {
275 if (const auto callback{connection->callback()}) {
276 ret |= (*callback)(args...);
277 }
278 }
279 return ret;
280 }
281 }
282
283 /*
284 * Connect a new callback to the signal. A forwarding callable accepts
285 * anything that can be stored in a std::function.
286 */
287 template <typename Callable>
289 {
290 LOCK(m_mutex);
291
292 // Garbage-collect disconnected connections to prevent unbounded growth
293 std::erase_if(m_connections, [](const auto& holder) { return !holder->connected(); });
294
295 const auto& entry = m_connections.emplace_back(std::make_shared<connection_holder>(std::forward<Callable>(func)));
296 return connection(entry);
297 }
298
299 /*
300 * Returns true if there are no enabled callbacks
301 */
303 {
304 LOCK(m_mutex);
305 return std::ranges::none_of(m_connections, [](const auto& holder) {
306 return holder->connected();
307 });
308 }
309};
310
311} // namespace btcsignals
312
313#endif // BITCOIN_UTIL_BTCSIGNALS_H
int ret
ArgsManager & args
Definition bitcoind.cpp:280
A combiner, which checks if at least one callback returned true.
Definition btcsignals.h:48
detail::atomic_shared_ptr< const void > m_callback
Callback object, type-erased so connection does not depend on the signal type, and set to null by dis...
Definition btcsignals.h:103
liveness(std::shared_ptr< const void > callback)
Definition btcsignals.h:108
std::shared_ptr< const void > load() const
Definition btcsignals.h:110
void disconnect()
If a callback is associated with this connection, prevent it from being called in the future.
Definition btcsignals.h:144
std::shared_ptr< liveness > m_state
connections have shared_ptr-like copy and move semantics.
Definition btcsignals.h:119
connection(std::shared_ptr< liveness > &&state)
Only a signal can create an enabled connection.
Definition btcsignals.h:124
constexpr connection() noexcept=default
The default constructor creates a connection with no associated signal.
bool connected() const
Returns true if this connection was created by a signal and has not been disabled.
Definition btcsignals.h:155
Substitute for the C++20 std::atomic<std::shared_ptr<T>> specialization on standard libraries that do...
Definition btcsignals.h:70
atomic_shared_ptr(std::shared_ptr< T > ptr) noexcept
Definition btcsignals.h:74
atomic_shared_ptr & operator=(const atomic_shared_ptr &)=delete
void store(std::shared_ptr< T > ptr) noexcept
Definition btcsignals.h:78
atomic_shared_ptr(const atomic_shared_ptr &)=delete
std::shared_ptr< T > load() const noexcept
Definition btcsignals.h:77
The default combiner, which only returns void.
Definition btcsignals.h:41
scoped_connection(scoped_connection &&) noexcept=default
scoped_connection(connection rhs) noexcept
Definition btcsignals.h:169
std::function< Signature > function_type
Definition btcsignals.h:197
std::vector< std::shared_ptr< connection_holder > > m_connections GUARDED_BY(m_mutex)
Definition btcsignals.h:219
connection connect(Callable &&func) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Definition btcsignals.h:288
Combiner::result_type result_type
Definition btcsignals.h:222
constexpr signal() noexcept=default
bool empty() const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Definition btcsignals.h:302
btcsignals is a simple mechanism for signaling events to multiple subscribers.
Definition handler.h:11
connection_holder(Callable &&callback)
Definition btcsignals.h:204
std::shared_ptr< const function_type > callback() const
Return the callback, or null if disconnected.
Definition btcsignals.h:211
#define LOCK(cs)
Definition sync.h:268
#define EXCLUSIVE_LOCKS_REQUIRED(...)
constexpr auto Ticks(Dur2 d)
Helper to count the seconds of a duration/time_point.
Definition time.h:82