userver: userver/engine/impl/task_local_storage.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
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