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) : 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