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 {
25public:
26 virtual void RemoveListener(FunctionId, UnsubscribingKind) noexcept = 0;
27
28protected:
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 {
38public:
39 constexpr FunctionId() = default;
40
41 template <typename Class>
42 explicit FunctionId(Class* obj) : FunctionId(obj, 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
52private:
53 FunctionId(void* ptr, const std::type_info& type);
54
55 void* ptr_{nullptr};
56 const std::type_info* type_{nullptr};
57};
58
59enum class UnsubscribingKind { kManual, kAutomatic };
60
61/// @brief Manages the subscription to events from an AsyncEventSource
62///
63/// Removes the associated listener automatically on destruction.
64///
65/// The Scope is usually placed as a member in the subscribing object.
66/// `Unsubscribe` should be called manually in the objects destructor, before
67/// anything that the callback needs is destroyed.
68class [[nodiscard]] AsyncEventSubscriberScope final {
69public:
70 AsyncEventSubscriberScope() = default;
71
72 template <typename... Args>
73 AsyncEventSubscriberScope(AsyncEventSource<Args...>& channel, FunctionId id)
74 : AsyncEventSubscriberScope(static_cast<impl::AsyncEventSourceBase&>(channel), id) {}
75
76 AsyncEventSubscriberScope(AsyncEventSubscriberScope&& scope) noexcept;
77
78 AsyncEventSubscriberScope& operator=(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
86private:
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 {
101public:
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, void (Class::*func)(Args...)) {
128 return AddListener(FunctionId(obj), name, [obj, func](Args... args) { (obj->*func)(args...); });
129 }
130
131 /// @brief A type-erased version of AddListener
132 ///
133 /// @param id the unique identifier of the subscription
134 /// @param name the name of the subscriber, for diagnostic purposes
135 /// @param func the callback used for event notifications
136 AsyncEventSubscriberScope AddListener(FunctionId id, std::string_view name, Function&& func) {
137 return DoAddListener(id, name, std::move(func));
138 }
139
140 /// Used by AsyncEventSubscriberScope to cancel an event subscription
141 void RemoveListener(FunctionId, UnsubscribingKind) noexcept override = 0;
142
143protected:
144 virtual AsyncEventSubscriberScope DoAddListener(FunctionId id, std::string_view name, Function&& func) = 0;
145};
146
147} // namespace concurrent
148
149USERVER_NAMESPACE_END