Github   Telegram
Loading...
Searching...
No Matches
caching_component_base.hpp
Go to the documentation of this file.
1#pragma once
2
5
6#include <memory>
7#include <string>
8#include <utility>
9
16#include <userver/dump/meta.hpp>
17#include <userver/dump/operations.hpp>
19#include <userver/rcu/rcu.hpp>
20#include <userver/utils/impl/wait_token_storage.hpp>
23
24USERVER_NAMESPACE_BEGIN
25
26namespace components {
27
28// clang-format off
29
87
88// clang-format on
89
90template <typename T>
91// NOLINTNEXTLINE(fuchsia-multiple-inheritance)
93 protected cache::CacheUpdateTrait {
94 public:
96 ~CachingComponentBase() override;
97
99
100 using DataType = T;
101
105
108
111 template <class Class>
113 Class* obj, std::string name,
114 void (Class::*func)(const std::shared_ptr<const T>&));
115
117 GetEventChannel();
118
119 static yaml_config::Schema GetStaticConfigSchema();
120
121 protected:
122 void Set(std::unique_ptr<const T> value_ptr);
123 void Set(T&& value);
124
125 template <typename... Args>
126 void Emplace(Args&&... args);
127
128 void Clear();
129
133 virtual bool MayReturnNull() const;
134
137 virtual void WriteContents(dump::Writer& writer, const T& contents) const;
138
139 virtual std::unique_ptr<const T> ReadContents(dump::Reader& reader) const;
141
142 private:
143 void OnAllComponentsLoaded() final;
144
145 void Cleanup() final;
146
147 void GetAndWrite(dump::Writer& writer) const final;
148 void ReadAndSet(dump::Reader& reader) final;
149
150 rcu::Variable<std::shared_ptr<const T>> cache_;
151 concurrent::AsyncEventChannel<const std::shared_ptr<const T>&> event_channel_;
152 utils::impl::WaitTokenStorage wait_token_storage_;
153};
154
155template <typename T>
157 const ComponentContext& context)
158 : LoggableComponentBase(config, context),
159 cache::CacheUpdateTrait(config, context),
160 event_channel_(components::GetCurrentComponentName(config)) {
161 const auto initial_config = GetConfig();
162}
163
164template <typename T>
166 // Avoid a deadlock in WaitForAllTokens
167 cache_.Assign(nullptr);
168 // We must wait for destruction of all instances of T to finish, otherwise
169 // it's UB if T's destructor accesses dependent components
170 wait_token_storage_.WaitForAllTokens();
171}
172
173template <typename T>
175 auto ptr = GetUnsafe();
176 if (!ptr && !MayReturnNull()) {
178 }
179 return ptr;
180}
181
182template <typename T>
183template <typename Class>
185 Class* obj, std::string name,
186 void (Class::*func)(const std::shared_ptr<const T>&)) {
187 return event_channel_.DoUpdateAndListen(obj, std::move(name), func, [&] {
188 auto ptr = Get(); // TODO: extra ref
189 (obj->*func)(ptr);
190 });
191}
192
193template <typename T>
196 return event_channel_;
197}
198
199template <typename T>
201 return utils::SharedReadablePtr<T>(cache_.ReadCopy());
202}
203
204template <typename T>
205void CachingComponentBase<T>::Set(std::unique_ptr<const T> value_ptr) {
206 auto deleter = [token = wait_token_storage_.GetToken(),
207 &cache_task_processor =
208 GetCacheTaskProcessor()](const T* raw_ptr) mutable {
209 std::unique_ptr<const T> ptr{raw_ptr};
210
211 // Kill garbage asynchronously as T::~T() might be very slow
212 engine::CriticalAsyncNoSpan(cache_task_processor, [ptr = std::move(ptr),
213 token = std::move(
214 token)]() mutable {
215 // Make sure *ptr is deleted before token is destroyed
216 ptr.reset();
217 }).Detach();
218 };
219
220 const std::shared_ptr<const T> new_value(value_ptr.release(),
221 std::move(deleter));
222 cache_.Assign(new_value);
223 event_channel_.SendEvent(new_value);
225}
226
227template <typename T>
228void CachingComponentBase<T>::Set(T&& value) {
229 Emplace(std::move(value));
230}
231
232template <typename T>
233template <typename... Args>
234void CachingComponentBase<T>::Emplace(Args&&... args) {
235 Set(std::make_unique<T>(std::forward<Args>(args)...));
236}
237
238template <typename T>
239void CachingComponentBase<T>::Clear() {
240 cache_.Assign(std::make_unique<const T>());
241}
242
243template <typename T>
245 return false;
246}
247
248template <typename T>
250 const auto contents = GetUnsafe();
251 if (!contents) throw cache::EmptyCacheError(Name());
252 WriteContents(writer, *contents);
253}
254
255template <typename T>
256void CachingComponentBase<T>::ReadAndSet(dump::Reader& reader) {
257 Set(ReadContents(reader));
258}
259
260template <typename T>
262 const T& contents) const {
263 if constexpr (dump::kIsDumpable<T>) {
264 writer.Write(contents);
265 } else {
266 dump::ThrowDumpUnimplemented(Name());
267 }
268}
269
270template <typename T>
271std::unique_ptr<const T> CachingComponentBase<T>::ReadContents(
272 dump::Reader& reader) const {
273 if constexpr (dump::kIsDumpable<T>) {
274 // To avoid an extra move and avoid including common_containers.hpp
275 return std::unique_ptr<const T>{new T(reader.Read<T>())};
276 } else {
277 dump::ThrowDumpUnimplemented(Name());
278 }
279}
280
281template <typename T>
282void CachingComponentBase<T>::OnAllComponentsLoaded() {
283 AssertPeriodicUpdateStarted();
284}
285
286template <typename T>
287void CachingComponentBase<T>::Cleanup() {
288 cache_.Cleanup();
289}
290
291namespace impl {
292
293yaml_config::Schema GetCachingComponentBaseSchema();
294
295}
296
297template <typename T>
298yaml_config::Schema CachingComponentBase<T>::GetStaticConfigSchema() {
299 return impl::GetCachingComponentBaseSchema();
300}
301
302} // namespace components
303
304USERVER_NAMESPACE_END