2#include <asyncpp/ref.h>
9#include <unordered_map>
14 using mutex_type = std::mutex;
19 constexpr void lock()
noexcept {}
20 constexpr void unlock()
noexcept {}
21 constexpr bool try_lock()
noexcept {
return true; }
27 template<
typename,
typename = signal_traits_mt>
32 virtual ~signal_node_base() noexcept = default;
33 std::atomic<
size_t> counter;
35 static constexpr
size_t signal_removed_counter = 0;
40 template<
typename,
typename>
45 explicit operator bool()
const noexcept {
return valid(); }
46 [[nodiscard]]
bool operator!()
const noexcept {
return !valid(); }
47 [[nodiscard]]
bool valid()
const noexcept {
48 return m_node && m_node->counter != detail::signal_removed_counter;
50 void disconnect()
noexcept {
51 if (m_node) m_node->counter = detail::signal_removed_counter;
56 return lhs.m_node.
get() <=> rhs.m_node.
get();
59 return lhs.m_node.
get() == rhs.m_node.
get();
62 return lhs.m_node.
get() != rhs.m_node.
get();
75 explicit operator bool()
const noexcept {
return valid(); }
76 [[nodiscard]]
bool operator!()
const noexcept {
return !valid(); }
77 [[nodiscard]]
bool valid()
const noexcept {
return m_handle.valid(); }
78 void disconnect()
noexcept { m_handle.disconnect(); }
81 [[nodiscard]]
constexpr operator signal_handle&()
noexcept {
return m_handle; }
83 [[nodiscard]]
constexpr operator const signal_handle&()
const noexcept {
return m_handle; }
87 return lhs.m_handle <=> rhs.m_handle;
91 return lhs.m_handle == rhs.m_handle;
95 return lhs.m_handle != rhs.m_handle;
99 template<
typename... TParams,
typename TTraits>
100 class signal<void(TParams...), TTraits> {
101 struct node : detail::signal_node_base {
102 ~node()
noexcept override =
default;
103 virtual void invoke(
const TParams&...) = 0;
108 template<
typename FN>
109 struct node_impl final : node {
110 ~node_impl()
noexcept =
default;
111 void invoke(
const TParams&... params)
override { m_fn(params...); }
112 [[no_unique_address]] FN m_fn;
113 explicit node_impl(FN&& fncb) : m_fn(std::move(fncb)) {}
117 using traits_type = TTraits;
129 [[nodiscard]]
size_t size()
const noexcept;
130 [[nodiscard]]
bool empty()
const noexcept {
return size() == 0; }
132 template<
typename FN>
134 template<
typename FN>
135 handle prepend(FN&& fncb);
137 bool remove(
const handle& hdl);
138 [[nodiscard]]
bool owns_handle(
const handle& hdl)
const;
140 size_t operator()(
const TParams&... params)
const;
142 template<
typename FN>
143 handle operator+=(FN&& fncb) {
144 return append(std::forward<
decltype(fncb)>(fncb));
147 void operator-=(
const handle& hdl) { remove(hdl); }
150 mutable typename TTraits::mutex_type m_mutex{};
152 mutable node* m_tail{};
153 std::atomic<size_t> m_current_counter{1};
155 size_t get_next_counter() {
156 const auto result = m_current_counter.fetch_add(1, std::memory_order::seq_cst);
159 std::lock_guard lck(m_mutex);
166 return m_current_counter.fetch_add(1, std::memory_order::seq_cst);
171 void free_node(
ref<node>& node)
const noexcept {
172 if (node->next) { node->next->previous = node->previous; }
173 if (node->previous) { node->previous->next = node->next; }
174 node->counter = detail::signal_removed_counter;
175 if (m_head == node) m_head = node->next;
176 if (m_tail == node) m_tail = node->previous;
185 template<
typename,
typename,
typename = signal_traits_mt>
188 template<
typename TEventType,
typename... TParams,
typename TTraits>
191 using event_type = TEventType;
193 using traits_type = TTraits;
204 template<
typename FN>
205 handle append(event_type event, FN&& fncb) {
206 auto iter = find_or_create_slot(event);
207 assert(iter != m_mapping.end());
208 return iter->second.append(std::forward<
decltype(fncb)>(fncb));
210 template<
typename FN>
211 handle prepend(event_type event, FN&& fncb) {
212 auto iter = find_or_create_slot(event);
213 assert(iter != m_mapping.end());
214 return iter->second.prepend(std::forward<
decltype(fncb)>(fncb));
217 bool remove(event_type event,
const handle& hdl) {
218 std::shared_lock lck{m_mutex};
219 auto iter = m_mapping.find(event);
220 return iter != m_mapping.end() && iter->second.remove(hdl);
223 bool owns_handle(event_type event,
const handle& hdl)
const {
224 std::shared_lock lck{m_mutex};
225 auto iter = m_mapping.find(event);
226 return iter != m_mapping.end() && iter->second.owns_handle(hdl);
229 size_t invoke(event_type event,
const TParams&... params)
const {
230 std::shared_lock lck{m_mutex};
231 auto iter = m_mapping.find(event);
232 if (iter == m_mapping.end())
return 0;
233 return iter->second(params...);
236 size_t operator()(event_type event,
const TParams&... params)
const {
return invoke(event, params...); }
238 size_t shrink_to_fit() {
239 std::unique_lock lck{m_mutex};
241 for (
auto it = m_mapping.begin(); it != m_mapping.end();) {
242 if (it->second.empty()) {
244 it = m_mapping.erase(it);
252 mutable std::shared_mutex m_mutex{};
253 std::unordered_map<event_type, signal_type> m_mapping;
255 auto find_or_create_slot(event_type evt) {
256 std::shared_lock lck{m_mutex};
257 auto iter = m_mapping.find(evt);
258 if (iter == m_mapping.end()) {
260 std::unique_lock unique{m_mutex};
261 iter = m_mapping.find(evt);
262 if (iter != m_mapping.end())
return iter;
263 iter = m_mapping.emplace(evt,
signal_type{}).first;
269 template<
typename... TParams,
typename TTraits>
271 inline signal<void(TParams...), TTraits>::signal(
signal&& other) {
272 std::scoped_lock lck{m_mutex, other.m_mutex};
273 m_head = std::exchange(other.m_head,
nullptr);
274 m_tail = std::exchange(other.m_tail,
nullptr);
275 m_current_counter.store(other.m_current_counter.exchange(1));
278 template<
typename... TParams,
typename TTraits>
280 inline signal<void(TParams...), TTraits>& signal<void(TParams...), TTraits>::operator=(signal&& other) {
281 std::scoped_lock lck{m_mutex, other.m_mutex};
285 auto next = node->next;
286 node->previous =
nullptr;
291 m_head = std::exchange(other.m_head,
nullptr);
292 m_tail = std::exchange(other.m_tail,
nullptr);
293 m_current_counter.store(other.m_current_counter.exchange(1));
296 template<
typename... TParams,
typename TTraits>
297 inline signal<void(TParams...), TTraits>::~signal() {
301 auto next = node->next;
302 node->previous =
nullptr;
309 template<
typename... TParams,
typename TTraits>
310 inline size_t signal<void(TParams...), TTraits>::size() const noexcept {
311 std::lock_guard lck{m_mutex};
315 if (node->counter != detail::signal_removed_counter) res++;
321 template<
typename... TParams,
typename TTraits>
322 template<
typename FN>
323 inline typename signal<void(TParams...), TTraits>::handle signal<void(TParams...), TTraits>::append(FN&& fncb) {
324 ref<node> new_node(
new node_impl<FN>(std::forward<
decltype(fncb)>(fncb)));
325 new_node->counter = get_next_counter();
326 if (std::lock_guard lck{m_mutex}; m_head) {
327 new_node->previous = m_tail;
328 m_tail->next = new_node;
329 m_tail = new_node.get();
332 m_tail = new_node.get();
334 return handle(static_ref_cast<detail::signal_node_base>(new_node));
337 template<
typename... TParams,
typename TTraits>
338 template<
typename FN>
339 inline typename signal<void(TParams...), TTraits>::handle signal<void(TParams...), TTraits>::prepend(FN&& fncb) {
340 ref<node> new_node(
new node_impl<FN>(std::forward<
decltype(fncb)>(fncb)));
341 new_node->counter = get_next_counter();
342 if (std::lock_guard lck{m_mutex}; m_head) {
343 new_node->next = m_head;
344 m_head->previous = new_node.get();
350 return handle(static_ref_cast<detail::signal_node_base>(new_node));
353 template<
typename... TParams,
typename TTraits>
354 inline bool signal<void(TParams...), TTraits>::remove(
const handle& hdl) {
355 auto node = static_ref_cast<signal::node>(hdl.m_node);
356 if (!node)
return false;
357 std::lock_guard lck{m_mutex};
362 template<
typename... TParams,
typename TTraits>
363 inline bool signal<void(TParams...), TTraits>::owns_handle(
const handle& hdl)
const {
364 auto node = static_ref_cast<signal::node>(hdl.m_node);
365 if (!node || node->counter == detail::signal_removed_counter)
return false;
366 std::lock_guard lck{m_mutex};
367 auto handle = m_head;
368 while (handle && handle != node) {
369 handle = handle->next;
371 return node == handle;
374 template<
typename... TParams,
typename TTraits>
375 inline size_t signal<void(TParams...), TTraits>::operator()(
const TParams&... params)
const {
379 std::lock_guard lck(m_mutex);
384 if (!node)
return ninvoked;
386 const auto counter = m_current_counter.load(std::memory_order_acquire);
388 if (node->counter != detail::signal_removed_counter && counter >= node->counter) {
389 node->invoke(params...);
393 std::lock_guard lck(m_mutex);
394 if (node->counter == detail::signal_removed_counter) { free_node(node); }
Intrusive refcounting base class.
Definition ref.h:63
Reference count handle.
Definition ref.h:126
constexpr T * get() const noexcept
Get the contained value.
Definition ref.h:208
void reset(T *ptr) noexcept(noexcept(refcounted_add_ref(std::declval< T * >())) &&noexcept(refcounted_remove_ref(std::declval< T * >())))
Reset the handle with a new value.
Definition ref.h:181
Threadsafe refcount policy.
Definition ref.h:27
Thread unsafe refcount policy.
Definition ref.h:40