userver
C++ Async Framework
Toggle main menu visibility
Loading...
Searching...
No Matches
resource_scopes.hpp
Go to the documentation of this file.
1
#
pragma
once
2
3
/// @file userver/utils/resource_scopes.hpp
4
/// @brief @copybrief utils::ResourceScopeStorage
5
6
#
include
<
concepts
>
7
#
include
<
cstdint
>
8
#
include
<
memory
>
9
#
include
<
optional
>
10
#
include
<
vector
>
11
12
#
include
<
userver
/
compiler
/
impl
/
lifetime
.
hpp
>
13
#
include
<
userver
/
components
/
component_fwd
.
hpp
>
14
#
include
<
userver
/
utils
/
assert
.
hpp
>
15
#
include
<
userver
/
utils
/
impl
/
internal_tag
.
hpp
>
16
#
include
<
userver
/
utils
/
move_only_function
.
hpp
>
17
18
USERVER_NAMESPACE_BEGIN
19
20
namespace
utils
{
21
22
namespace
impl {
23
class
ScopeBase {
24
public
:
25
virtual
~ScopeBase() =
default
;
26
27
virtual
void
AfterConstruction() = 0;
28
};
29
30
template
<
typename
Handle>
31
class
Scope
final
:
public
ScopeBase {
32
public
:
33
using
AfterConstructionCallback =
utils
::move_only_function<Handle()>;
34
35
explicit
Scope(AfterConstructionCallback after_construction)
36
: after_construction_(std::move(after_construction))
37
{}
38
39
void
AfterConstruction()
override
{ before_destruction_.emplace(after_construction_()); }
40
41
private
:
42
AfterConstructionCallback after_construction_;
43
std::optional<Handle> before_destruction_;
44
};
45
46
template
<>
47
class
Scope<
void
>
final
:
public
ScopeBase {
48
public
:
49
using
AfterConstructionCallback =
utils
::move_only_function<
void
()>;
50
51
explicit
Scope(AfterConstructionCallback after_construction)
52
: after_construction_(std::move(after_construction))
53
{}
54
55
void
AfterConstruction()
override
{ after_construction_(); }
56
57
private
:
58
AfterConstructionCallback after_construction_;
59
};
60
61
/// @brief An object of ScopePtr defines actions to do after
62
/// a component is constructed and just before it is destroyed.
63
///
64
/// @see @ref components::ComponentContext::Scopes
65
using
ScopePtr = std::unique_ptr<impl::ScopeBase>;
66
67
}
// namespace impl
68
69
/// @brief Defers subscription and callback registration until the object is fully constructed.
70
///
71
/// Components often register external subscriptions (statistics writers, config listeners,
72
/// and similar) that capture `this` and run later on another thread. Registering them
73
/// directly in the constructor is unsafe: the callback may fire before the constructor
74
/// finishes and observe partially initialized fields. Unregistering in the destructor
75
/// is equally unsafe if the callback can still run while members are already being
76
/// destroyed.
77
///
78
/// During construction, call @ref Register to queue a functor that performs the actual
79
/// registration. The component system calls @ref AfterConstruction when the constructor
80
/// (including derived classes) has completed, and @ref BeforeDestruction before the
81
/// destructor body runs. That way registration callbacks see a fully built object, and
82
/// unregistration runs before members used by the callback are torn down.
83
///
84
/// The same storage is available from @ref components::ComponentContext::Scopes in
85
/// components, or as a standalone helper in unit tests and @ref WithResourceScopes.
86
///
87
/// @warning Do not store @ref ResourceScopeStorage as a field of the object that
88
/// registers subscriptions on it. The storage would share that object's constructor
89
/// and destructor, so @ref AfterConstruction cannot run after the object is complete
90
/// and @ref BeforeDestruction cannot run before its members are destroyed. Wrap the
91
/// object in @ref WithResourceScopes instead.
92
///
93
/// @snippet core/src/components/resource_scopes_test.cpp ResourceScopeStorage - HappyPathOrder
94
class
ResourceScopeStorage
final
{
95
public
:
96
ResourceScopeStorage() =
default
;
97
98
ResourceScopeStorage(ResourceScopeStorage&& other)
noexcept
=
default
;
99
ResourceScopeStorage& operator=(ResourceScopeStorage&& other)
noexcept
=
default
;
100
101
/// @brief Registers a functor to register some resource that will be
102
/// called after the component is successfully created (including all
103
/// class descendants) or after the component creation is emulated in
104
/// unit tests. The functor must return a RAII-style handle object
105
/// that unregisters the previously registered resource. The returned handle's
106
/// destructor is called just before the component destructor is called.
107
///
108
/// @note callback is not called if the component is not created OR
109
/// any previously registered callback throws an exception.
110
/// @note if you don't have an existing RAII-ish class, but still want
111
/// to do a cleanup, you might want to use @ref utils::FastScopeGuard
112
/// to wrap the cleanup function.
113
template
<
std
::invocable<> AfterConstructionCallback>
114
void
Register
(AfterConstructionCallback after_construction)
115
{
116
Register(
utils
::impl::InternalTag{}, Priority{0}, std::move(after_construction));
117
}
118
119
/// @cond
120
// For internal use only.
121
// Lower values run earlier in AfterConstruction and later in BeforeDestruction.
122
using
Priority = std::int32_t;
123
124
template
<std::invocable<> AfterConstructionCallback>
125
void
Register(
utils
::impl::InternalTag, Priority priority, AfterConstructionCallback after_construction)
126
{
127
using
Handle = std::invoke_result_t<AfterConstructionCallback>;
128
auto
scope = std::make_unique<impl::Scope<Handle>>(std::move(after_construction));
129
DoRegister(std::move(scope), priority);
130
}
131
/// @endcond
132
133
/// @brief Call all registered functors.
134
///
135
/// If a functor throws, already constructed resources are unregistered via
136
/// @ref BeforeDestruction and the exception is rethrown.
137
void
AfterConstruction
();
138
139
/// @brief Unregister all previously registered resources.
140
///
141
/// Also drops factories that have not run @ref AfterConstruction yet,
142
/// so captured RAII handles unregister immediately.
143
void
BeforeDestruction
()
noexcept
;
144
145
private
:
146
struct
ScopeWithPriority {
147
Priority priority{0};
148
impl::ScopePtr scope;
149
};
150
151
void
DoRegister(impl::ScopePtr resource_scope, Priority priority);
152
static
void
SortByPriority(std::vector<ScopeWithPriority>& scopes)
noexcept
;
153
154
std::vector<ScopeWithPriority> registered_scopes_;
155
std::vector<impl::ScopePtr> initialized_scopes_;
156
bool
scope_registration_finished_{
false
};
157
};
158
159
/// @brief A wrapper that provides @ref utils::ResourceScopeStorage for the wrapped object.
160
///
161
/// The wrapped object is passed `utils::ResourceScopeStorage&` as the first argument to the constructor.
162
/// Prefer this over storing @ref ResourceScopeStorage as a field of the wrapped object itself.
163
template
<
typename
Wrapped>
164
class
WithResourceScopes
final
{
165
public
:
166
/// @brief Constructs the wrapped object and passes the embedded @ref utils::ResourceScopeStorage to it
167
/// as the first argument.
168
template
<
typename
... Args>
169
explicit
WithResourceScopes
(std::in_place_t, Args&&... args)
170
: wrapped_(resource_scope_storage_, std::forward<Args>(args)...)
171
{
172
resource_scope_storage_
.
AfterConstruction
(
)
;
173
}
174
175
WithResourceScopes(WithResourceScopes&& other)
noexcept
=
default
;
176
WithResourceScopes& operator=(WithResourceScopes&& other)
noexcept
=
default
;
177
178
~WithResourceScopes() { resource_scope_storage_
.
BeforeDestruction
(
)
; }
179
180
/// @brief Returns the wrapped object.
181
Wrapped&
operator
*() &
noexcept
USERVER_IMPL_LIFETIME_BOUND {
return
wrapped_; }
182
/// @overload
183
const
Wrapped&
operator
*()
const
&
noexcept
USERVER_IMPL_LIFETIME_BOUND {
return
wrapped_; }
184
185
/// @brief Returns the wrapped object.
186
Wrapped*
operator
->()
noexcept
USERVER_IMPL_LIFETIME_BOUND {
return
&wrapped_; }
187
/// @overload
188
const
Wrapped*
operator
->()
const
noexcept
USERVER_IMPL_LIFETIME_BOUND {
return
&wrapped_; }
189
190
private
:
191
ResourceScopeStorage resource_scope_storage_;
192
Wrapped wrapped_;
193
};
194
195
ResourceScopeStorage&
196
LocateDependency(
components
::
WithType
<ResourceScopeStorage>,
const
components
::ComponentConfig& config,
const
components
::ComponentContext&);
197
198
}
// namespace utils
199
200
USERVER_NAMESPACE_END
userver
utils
resource_scopes.hpp
Generated on
for userver by
Doxygen
1.17.0