userver: userver/engine/impl/task_local_storage.hpp Source File
Loading...
Searching...
No Matches
task_local_storage.hpp
1#pragma once
2
3#include <atomic>
4#include <cstddef>
5#include <memory>
6#include <type_traits>
7#include <typeinfo>
8
9#include <userver/utils/fast_pimpl.hpp>
10
11USERVER_NAMESPACE_BEGIN
12
13namespace engine::impl::task_local {
14
15using Key = std::size_t;
16
17enum class VariableKind { kNormal, kInherited };
18
19class DataBase {
20 public:
21 using Deleter = void (*)(DataBase&) noexcept;
22
23 void DeleteSelf() noexcept;
24
25 protected:
26 explicit DataBase(Deleter deleter);
27
28 private:
29 const Deleter deleter_;
30};
31
32class NormalDataBase : public DataBase {
33 protected:
34 template <typename Derived>
35 NormalDataBase(std::in_place_type_t<Derived>, Key /*key*/)
36 : DataBase([](DataBase& base) noexcept {
37 delete &static_cast<Derived&>(base);
38 }) {}
39};
40
41class InheritedDataBase : public DataBase {
42 public:
43 void AddRef() noexcept;
44
45 Key GetKey() const noexcept;
46
47 protected:
48 template <typename Derived>
49 InheritedDataBase(std::in_place_type_t<Derived>, Key key)
50 : DataBase([](DataBase& base) noexcept {
51 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-static-cast-downcast)
52 if (--static_cast<InheritedDataBase&>(base).ref_counter_ == 0) {
53 delete &static_cast<Derived&>(base);
54 }
55 }),
56 key_(key) {}
57
58 private:
59 std::atomic<std::size_t> ref_counter_{1};
60 const Key key_{};
61};
62
63template <VariableKind Kind>
64using ConditionalDataBase =
65 std::conditional_t<Kind == VariableKind::kInherited, InheritedDataBase,
66 NormalDataBase>;
67
68template <typename T, VariableKind Kind>
69class DataImpl final : public ConditionalDataBase<Kind> {
70 using Base = ConditionalDataBase<Kind>;
71
72 public:
73 template <typename... Args>
74 explicit DataImpl(Key key, Args&&... args)
75 : Base(std::in_place_type<DataImpl>, key),
76 variable_(std::forward<Args>(args)...) {}
77
78 T& Get() noexcept { return variable_; }
79
80 private:
81 T variable_;
82};
83
84[[noreturn]] void ReportVariableNotSet(const std::type_info& type);
85
86class Storage final {
87 public:
88 Storage();
89
90 Storage(Storage&&) = delete;
91 Storage& operator=(Storage&&) = delete;
92 ~Storage();
93
94 // Copies pointers to inherited variables from 'other'
95 // 'this' must not contain any variables
96 void InheritFrom(Storage& other);
97
98 // Moves other's variables into 'this', leaving 'other' in an empty state
99 // 'this' must not contain any variables
100 void InitializeFrom(Storage&& other) noexcept;
101
102 // Variable accessors must be called with the same T, Kind, key.
103 // Otherwise it is UB.
104 template <typename T, VariableKind Kind>
105 T& GetOrEmplace(Key key) {
106 DataBase* const old_data = GetGeneric(key);
107 if (!old_data) {
108 const bool has_existing_variable = false;
109 return DoEmplace<T, Kind>(key, has_existing_variable);
110 }
111 return static_cast<DataImpl<T, Kind>&>(*old_data).Get();
112 }
113
114 template <typename T, VariableKind Kind>
115 T* GetOptional(Key key) noexcept {
116 DataBase* const data = GetGeneric(key);
117 if (!data) return nullptr;
118 return &static_cast<DataImpl<T, Kind>&>(*data).Get();
119 }
120
121 template <typename T, VariableKind Kind>
122 T& Get(Key key) {
123 T* const result = GetOptional<T, Kind>(key);
124 if (!result) ReportVariableNotSet(typeid(T));
125 return *result;
126 }
127
128 template <typename T, VariableKind Kind, typename... Args>
129 T& Emplace(Key key, Args&&... args) {
130 DataBase* const old_data = GetGeneric(key);
131 const bool has_existing_variable = old_data != nullptr;
132 auto& result = DoEmplace<T, Kind>(key, has_existing_variable,
133 std::forward<Args>(args)...);
134 if (old_data) old_data->DeleteSelf();
135 return result;
136 }
137
138 template <typename T, VariableKind Kind>
139 void Erase(Key key) noexcept {
140 static_assert(Kind == VariableKind::kInherited);
141 EraseInherited(key);
142 }
143
144 private:
145 DataBase* GetGeneric(Key key) noexcept;
146
147 void SetGeneric(Key key, NormalDataBase& node, bool has_existing_variable);
148
149 void SetGeneric(Key key, InheritedDataBase& node, bool has_existing_variable);
150
151 void EraseInherited(Key key) noexcept;
152
153 // Provides strong exception guarantee. Does not delete the old data, if any.
154 template <typename T, VariableKind Kind, typename... Args>
155 T& DoEmplace(Key key, bool has_existing_variable, Args&&... args) {
156 auto new_data =
157 std::make_unique<DataImpl<T, Kind>>(key, std::forward<Args>(args)...);
158 SetGeneric(key, *new_data, has_existing_variable);
159 return new_data.release()->Get();
160 }
161
162 struct Impl;
163 utils::FastPimpl<Impl, 40, 8> impl_;
164};
165
166class Variable final {
167 public:
168 Variable();
169
170 Variable(const Variable&) = delete;
171 Variable(Variable&&) = delete;
172 Variable& operator=(const Variable&) = delete;
173 Variable& operator=(Variable&&) = delete;
174
175 Key GetKey() const noexcept;
176
177 private:
178 const Key key_;
179};
180
181Storage& GetCurrentStorage() noexcept;
182
183} // namespace engine::impl::task_local
184
185USERVER_NAMESPACE_END