userver: userver/concurrent/background_task_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
background_task_storage.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/concurrent/background_task_storage.hpp
4/// @brief @copybrief concurrent::BackgroundTaskStorage
5
6#include <cstdint>
7#include <utility>
8
9#include <userver/engine/impl/detached_tasks_sync_block.hpp>
10#include <userver/engine/task/task_processor_fwd.hpp>
11#include <userver/utils/async.hpp>
12
13USERVER_NAMESPACE_BEGIN
14
15namespace concurrent {
16
17/// @ingroup userver_concurrency userver_containers
18///
19/// A version of concurrent::BackgroundTaskStorage for advanced use cases (e.g.
20/// driver internals) that can take the ownership of any kind of task.
21class BackgroundTaskStorageCore final {
22 public:
23 /// Creates an empty BTS.
25
26 BackgroundTaskStorageCore(BackgroundTaskStorageCore&&) = delete;
27 BackgroundTaskStorageCore& operator=(BackgroundTaskStorageCore&&) = delete;
28 ~BackgroundTaskStorageCore();
29
30 /// Explicitly cancel and wait for the tasks. New tasks must not be launched
31 /// after this call returns. Should be called no more than once.
32 void CancelAndWait() noexcept;
33
34 /// Explicitly wait for execution tasks in the store.
35 /// Should be called no more than once.
36 void CloseAndWaitDebug() noexcept;
37
38 /// @brief Detaches task, allowing it to continue execution out of scope. It
39 /// will be cancelled and waited for on BTS destruction.
40 /// @note After detach, Task becomes invalid
41 void Detach(engine::Task&& task);
42
43 /// Approximate number of currently active tasks
44 std::int64_t ActiveTasksApprox() const noexcept;
45
46 private:
47 std::optional<engine::impl::DetachedTasksSyncBlock> sync_block_;
48};
49
50/// @ingroup userver_concurrency userver_containers
51///
52/// A storage that allows one to start detached tasks; cancels and waits for
53/// unfinished tasks completion at the destructor. Provides CancelAndWait to
54/// explicitly cancel tasks (recommended).
55///
56/// Usable for detached tasks that capture references to resources with a
57/// limited lifetime. You must guarantee that the resources are available while
58/// the BackgroundTaskStorage is alive.
59///
60/// ## Usage synopsis
61///
62/// @snippet concurrent/background_task_storage_test.cpp Sample
63///
64/// ## Lifetime of task's captures
65///
66/// All the advice from utils::Async is applicable here.
67///
68/// BackgroundTaskStorage is always stored as a class field. Tasks that are
69/// launched inside it (or moved inside it, for BackgroundTaskStorageCore)
70/// can safely access fields declared before it, but not after it:
71///
72/// @code
73/// class Frobnicator {
74/// // ...
75///
76/// private:
77/// void Launch(const Dependencies& stuff);
78///
79/// // ...
80/// Foo foo_;
81/// concurrent::BackgroundTaskStorage bts_;
82/// Bar bar_;
83/// // ...
84/// };
85///
86/// void Frobnicator::Launch(const Dependencies& stuff) {
87/// int x{};
88/// bts_.AsyncDetach([this, &stuff, &x] {
89/// // BUG! All local variables will be gone.
90/// // They should be captured by move or by copy.
91/// Use(x);
92///
93/// // OK, because foo_ will be destroyed after bts_.
94/// Use(foo_);
95///
96/// // BUG, because bar_ will be destroyed before bts_.
97/// Use(bar_);
98///
99/// // Most likely a BUG! Unless `stuff` is contained within other fields,
100/// // there is probably no guarantee that it outlives `bts_`.
101/// // It should have been captured by move or by copy instead.
102/// Use(stuff);
103/// });
104/// }
105/// @endcode
106///
107/// Generally, it's a good idea to declare `bts_` after most other fields
108/// to avoid lifetime bugs. An example of fool-proof code:
109///
110/// @code
111/// private:
112/// Foo foo_;
113/// Bar bar_;
114///
115/// // bts_ must be the last field for lifetime reasons.
116/// concurrent::BackgroundTaskStorage bts_;
117/// };
118/// @endcode
119///
120/// Components and their clients can always be safely captured by reference:
121///
122/// @see @ref scripts/docs/en/userver/component_system.md
123///
124/// So for a BackgroundTaskStorage stored in a component, its tasks can only
125/// safely use the fields declared before the BTS field, as well as everything
126/// from the components, on which the current component depends.
127class BackgroundTaskStorage final {
128 public:
129 /// Creates a BTS that launches tasks in the engine::TaskProcessor used at the
130 /// BTS creation.
132
133 /// Creates a BTS that launches tasks in the specified engine::TaskProcessor.
134 explicit BackgroundTaskStorage(engine::TaskProcessor& task_processor);
135
136 BackgroundTaskStorage(const BackgroundTaskStorage&) = delete;
137 BackgroundTaskStorage& operator=(const BackgroundTaskStorage&) = delete;
138
139 /// Explicitly cancel and wait for the tasks. New tasks must not be launched
140 /// after this call returns. Should be called no more than once.
141 void CancelAndWait() noexcept;
142
143 /// Explicitly stop accepting new tasks and wait for execution tasks in the
144 /// store. Should be called no more than once.
145 void CloseAndWaitDebug() noexcept;
146
147 /// @brief Launch a task that will be cancelled and waited for in the BTS
148 /// destructor.
149 ///
150 /// The task is started as non-Critical, it may be cancelled due to
151 /// `TaskProcessor` overload. engine::TaskInheritedVariable instances are not
152 /// inherited from the caller except baggage::Baggage. See
153 /// utils::AsyncBackground for details.
154 template <typename... Args>
155 void AsyncDetach(std::string name, Args&&... args) {
156 core_.Detach(utils::AsyncBackground(std::move(name), task_processor_,
157 std::forward<Args>(args)...));
158 }
159
160 /// @brief Launch a task that will be cancelled and waited for in the BTS
161 /// destructor.
162 ///
163 /// Execution of function is guaranteed to start regardless
164 /// of engine::TaskProcessor load limits.
165 /// engine::TaskInheritedVariable instances are not
166 /// inherited from the caller except baggage::Baggage. See
167 /// utils::CriticalAsyncBackground for details.
168 template <typename... Args>
169 void CriticalAsyncDetach(std::string name, Args&&... args) {
170 core_.Detach(utils::CriticalAsyncBackground(
171 std::move(name), task_processor_, std::forward<Args>(args)...));
172 }
173
174 /// Approximate number of currently active tasks
175 std::int64_t ActiveTasksApprox() const noexcept;
176
177 private:
178 BackgroundTaskStorageCore core_;
179 engine::TaskProcessor& task_processor_;
180};
181
182} // namespace concurrent
183
184USERVER_NAMESPACE_END