userver: userver/concurrent/async_event_source.hpp Source File
Loading...
Searching...
No Matches
async_event_source.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/concurrent/async_event_source.hpp
4/// @brief @copybrief concurrent::AsyncEventSource
5
6#include <cstddef>
7#include <functional>
8#include <string_view>
9#include <typeinfo>
10#include <utility>
11
12USERVER_NAMESPACE_BEGIN
13
14namespace concurrent {
15
16class FunctionId;
17enum class UnsubscribingKind;
18
19template <typename... Args>
20class AsyncEventSource;
21
22namespace impl {
23
24class AsyncEventSourceBase {
25 public:
26 virtual void RemoveListener(FunctionId, UnsubscribingKind) noexcept = 0;
27
28 protected:
29 // disallow deletion via pointer to base
30 ~AsyncEventSourceBase();
31};
32
33} // namespace impl
34
35/// Uniquely identifies a subscription (usually the callback method owner) for
36/// AsyncEventSource
37class FunctionId final {
38 public:
39 constexpr FunctionId() = default;
40
41 template <typename Class>
42 explicit FunctionId(Class* obj) : ptr_(obj), type_(&typeid(Class)) {}
43
44 explicit operator bool() const;
45
46 bool operator==(const FunctionId& other) const;
47
48 struct Hash final {
49 std::size_t operator()(FunctionId id) const noexcept;
50 };
51
52 private:
53 void* ptr_{nullptr};
54 const std::type_info* type_{nullptr};
55};
56
57enum class UnsubscribingKind { kManual, kAutomatic };
58
59/// @brief Manages the subscription to events from an AsyncEventSource
60///
61/// Removes the associated listener automatically on destruction.
62///
63/// The Scope is usually placed as a member in the subscribing object.
64/// `Unsubscribe` should be called manually in the objects destructor, before
65/// anything that the callback needs is destroyed.
66class [[nodiscard]] AsyncEventSubscriberScope final {
67 public:
68 AsyncEventSubscriberScope() = default;
69
70 template <typename... Args>
71 AsyncEventSubscriberScope(AsyncEventSource<Args...>& channel, FunctionId id)
72 : AsyncEventSubscriberScope(
73 static_cast<impl::AsyncEventSourceBase&>(channel), id) {}
74
75 AsyncEventSubscriberScope(AsyncEventSubscriberScope&& scope) noexcept;
76
77 AsyncEventSubscriberScope& operator=(
78 AsyncEventSubscriberScope&& other) noexcept;
79
80 ~AsyncEventSubscriberScope();
81
82 /// Unsubscribes manually. The subscription should be cancelled before
83 /// anything that the callback needs is destroyed.
84 void Unsubscribe() noexcept;
85
86 private:
87 AsyncEventSubscriberScope(impl::AsyncEventSourceBase& channel, FunctionId id);
88
89 impl::AsyncEventSourceBase* channel_{nullptr};
90 FunctionId id_;
91};
92
93/// @ingroup userver_concurrency
94///
95/// @brief The read-only side of an event channel. Events are delivered to
96/// listeners in a strict FIFO order, i.e. only after the event was processed
97/// a new event may appear for processing, same listener is never called
98/// concurrently.
99template <typename... Args>
100class AsyncEventSource : public impl::AsyncEventSourceBase {
101 public:
102 using Function = std::function<void(Args... args)>;
103
104 virtual ~AsyncEventSource() = default;
105
106 /// @brief Subscribes to updates from this event source
107 ///
108 /// The listener won't be called immediately. To process the current value and
109 /// then listen to updates, use `UpdateAndListen` of specific event channels.
110 ///
111 /// @warning Listeners should not be added or removed while processing the
112 /// event inside another listener.
113 ///
114 /// Example usage:
115 /// @snippet concurrent/async_event_channel_test.cpp AddListener sample
116 ///
117 /// @param obj the subscriber, which is the owner of the listener method, and
118 /// is also used as the unique identifier of the subscription for this
119 /// AsyncEventSource
120 /// @param name the name of the subscriber, for diagnostic purposes
121 /// @param func the listener method, usually called `On<DataName>Update`, e.g.
122 /// `OnConfigUpdate` or `OnCacheUpdate`
123 /// @returns a AsyncEventSubscriberScope controlling the subscription, which
124 /// should be stored as a member in the subscriber; `Unsubscribe` should be
125 /// called explicitly
126 template <class Class>
127 AsyncEventSubscriberScope AddListener(Class* obj, std::string_view name,
128 void (Class::*func)(Args...)) {
129 return AddListener(FunctionId(obj), name,
130 [obj, func](Args... args) { (obj->*func)(args...); });
131 }
132
133 /// @brief A type-erased version of AddListener
134 ///
135 /// @param id the unique identifier of the subscription
136 /// @param name the name of the subscriber, for diagnostic purposes
137 /// @param func the callback used for event notifications
138 AsyncEventSubscriberScope AddListener(FunctionId id, std::string_view name,
139 Function&& func) {
140 return DoAddListener(id, name, std::move(func));
141 }
142
143 /// Used by AsyncEventSubscriberScope to cancel an event subscription
144 void RemoveListener(FunctionId, UnsubscribingKind) noexcept override = 0;
145
146 protected:
147 virtual AsyncEventSubscriberScope DoAddListener(FunctionId id,
148 std::string_view name,
149 Function&& func) = 0;
150};
151
152} // namespace concurrent
153
154USERVER_NAMESPACE_END