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