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