userver: userver/concurrent/async_event_source.hpp Source File
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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) : 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
52 private:
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 {
69 public:
70 AsyncEventSubscriberScope() = default;
71
72 template <typename... Args>
73 AsyncEventSubscriberScope(AsyncEventSource<Args...>& channel, FunctionId id)
74 : AsyncEventSubscriberScope(
75 static_cast<impl::AsyncEventSourceBase&>(channel), id) {}
76
77 AsyncEventSubscriberScope(AsyncEventSubscriberScope&& scope) noexcept;
78
79 AsyncEventSubscriberScope& operator=(
80 AsyncEventSubscriberScope&& other) noexcept;
81
82 ~AsyncEventSubscriberScope();
83
84 /// Unsubscribes manually. The subscription should be cancelled before
85 /// anything that the callback needs is destroyed.
86 void Unsubscribe() noexcept;
87
88 private:
89 AsyncEventSubscriberScope(impl::AsyncEventSourceBase& channel, FunctionId id);
90
91 impl::AsyncEventSourceBase* channel_{nullptr};
92 FunctionId id_;
93};
94
95/// @ingroup userver_concurrency
96///
97/// @brief The read-only side of an event channel. Events are delivered to
98/// listeners in a strict FIFO order, i.e. only after the event was processed
99/// a new event may appear for processing, same listener is never called
100/// concurrently.
101template <typename... Args>
102class AsyncEventSource : public impl::AsyncEventSourceBase {
103 public:
104 using Function = std::function<void(Args... args)>;
105
106 virtual ~AsyncEventSource() = default;
107
108 /// @brief Subscribes to updates from this event source
109 ///
110 /// The listener won't be called immediately. To process the current value and
111 /// then listen to updates, use `UpdateAndListen` of specific event channels.
112 ///
113 /// @warning Listeners should not be added or removed while processing the
114 /// event inside another listener.
115 ///
116 /// Example usage:
117 /// @snippet concurrent/async_event_channel_test.cpp AddListener sample
118 ///
119 /// @param obj the subscriber, which is the owner of the listener method, and
120 /// is also used as the unique identifier of the subscription for this
121 /// AsyncEventSource
122 /// @param name the name of the subscriber, for diagnostic purposes
123 /// @param func the listener method, usually called `On<DataName>Update`, e.g.
124 /// `OnConfigUpdate` or `OnCacheUpdate`
125 /// @returns a AsyncEventSubscriberScope controlling the subscription, which
126 /// should be stored as a member in the subscriber; `Unsubscribe` should be
127 /// called explicitly
128 template <class Class>
129 AsyncEventSubscriberScope AddListener(Class* obj, std::string_view name,
130 void (Class::*func)(Args...)) {
131 return AddListener(FunctionId(obj), name,
132 [obj, func](Args... args) { (obj->*func)(args...); });
133 }
134
135 /// @brief A type-erased version of AddListener
136 ///
137 /// @param id the unique identifier of the subscription
138 /// @param name the name of the subscriber, for diagnostic purposes
139 /// @param func the callback used for event notifications
140 AsyncEventSubscriberScope AddListener(FunctionId id, std::string_view name,
141 Function&& func) {
142 return DoAddListener(id, name, std::move(func));
143 }
144
145 /// Used by AsyncEventSubscriberScope to cancel an event subscription
146 void RemoveListener(FunctionId, UnsubscribingKind) noexcept override = 0;
147
148 protected:
149 virtual AsyncEventSubscriberScope DoAddListener(FunctionId id,
150 std::string_view name,
151 Function&& func) = 0;
152};
153
154} // namespace concurrent
155
156USERVER_NAMESPACE_END