Clio  develop
The XRP Ledger API server.
Loading...
Searching...
No Matches
TrackableSignal.hpp
1#pragma once
2
3#include "util/Mutex.hpp"
4
5#include <boost/signals2.hpp>
6#include <boost/signals2/connection.hpp>
7#include <boost/signals2/variadic_signal.hpp>
8
9#include <cstddef>
10#include <functional>
11#include <memory>
12#include <mutex>
13#include <unordered_map>
14
15namespace feed::impl {
16
25template <typename Session, typename... Args>
27 using ConnectionPtr = Session*;
28 using ConnectionSharedPtr = std::shared_ptr<Session>;
29
30 // map of connection and signal connection, key is the pointer of the connection object
31 // allow disconnect to be called in the destructor of the connection
32 using ConnectionsMap = std::unordered_map<ConnectionPtr, boost::signals2::connection>;
33 util::Mutex<ConnectionsMap> connections_;
34
35 using SignalType = boost::signals2::signal<void(Args...)>;
36 SignalType signal_;
37
38public:
49 bool
50 connectTrackableSlot(ConnectionSharedPtr const& trackable, std::function<void(Args...)> slot)
51 {
52 auto connections = connections_.template lock<std::scoped_lock>();
53 if (connections->contains(trackable.get())) {
54 return false;
55 }
56
57 // This class can't hold the trackable's shared_ptr, because disconnect should be able to be
58 // called in the the trackable's destructor. However, the trackable can not be destroyed
59 // when the slot is being called either. `track_foreign` is racey when one shared_ptr is
60 // tracked by multiple signals. Therefore we are storing a weak_ptr of the trackable and
61 // using weak_ptr::lock() to atomically check existence and acquire a shared_ptr during slot
62 // invocation. This guarantees to keep the trackable alive for the duration of the slot call
63 // and avoids potential race conditions.
64 connections->emplace(
65 trackable.get(),
66 signal_.connect([slot, weakTrackable = std::weak_ptr(trackable)](Args&&... args) {
67 if (auto lifeExtender = weakTrackable.lock(); lifeExtender)
68 std::invoke(slot, std::forward<Args...>(args)...);
69 })
70 );
71 return true;
72 }
73
83 bool
84 disconnect(ConnectionPtr trackablePtr)
85 {
86 if (auto connections = connections_.template lock<std::scoped_lock>();
87 connections->contains(trackablePtr)) {
88 connections->operator[](trackablePtr).disconnect();
89 connections->erase(trackablePtr);
90 return true;
91 }
92 return false;
93 }
94
100 void
101 emit(Args const&... args) const
102 {
103 signal_(args...);
104 }
105
109 std::size_t
110 count() const
111 {
112 return connections_.template lock<std::scoped_lock>()->size();
113 }
114};
115} // namespace feed::impl
A thread-safe class to manage a signal and its tracking connections.
Definition TrackableSignal.hpp:26
bool disconnect(ConnectionPtr trackablePtr)
Disconnect a slot to the signal.
Definition TrackableSignal.hpp:84
std::size_t count() const
Get the number of connections.
Definition TrackableSignal.hpp:110
void emit(Args const &... args) const
Calling all slots.
Definition TrackableSignal.hpp:101
bool connectTrackableSlot(ConnectionSharedPtr const &trackable, std::function< void(Args...)> slot)
Connect a slot to the signal, the slot will be called when the signal is emitted and trackable is sti...
Definition TrackableSignal.hpp:50
A container for data that is protected by a mutex. Inspired by Mutex in Rust.
Definition Mutex.hpp:82