userver: userver/concurrent/background_task_storage.hpp Source File
Loading...
Searching...
No Matches
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 {
22public:
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
46private:
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/// @note The implementation is optimized for spawning tasks. Waiting for remaining tasks may be slow CPU-wise.
61/// As a guideline, do not create `BackgroundTaskStorage` instances for each request, use `std::vector<Task>`
62/// for storing per-request child tasks instead.
63///
64/// ## Usage synopsis
65///
66/// @snippet concurrent/background_task_storage_test.cpp Sample
67///
68/// ## Lifetime of task's captures
69///
70/// All the advice from utils::Async is applicable here.
71///
72/// BackgroundTaskStorage is always stored as a class field. Tasks that are
73/// launched inside it (or moved inside it, for BackgroundTaskStorageCore)
74/// can safely access fields declared before it, but not after it:
75///
76/// @code
77/// class Frobnicator {
78/// // ...
79///
80/// private:
81/// void Launch(const Dependencies& stuff);
82///
83/// // ...
84/// Foo foo_;
85/// concurrent::BackgroundTaskStorage bts_;
86/// Bar bar_;
87/// // ...
88/// };
89///
90/// void Frobnicator::Launch(const Dependencies& stuff) {
91/// int x{};
92/// bts_.AsyncDetach([this, &stuff, &x] {
93/// // BUG! All local variables will be gone.
94/// // They should be captured by move or by copy.
95/// Use(x);
96///
97/// // OK, because foo_ will be destroyed after bts_.
98/// Use(foo_);
99///
100/// // BUG, because bar_ will be destroyed before bts_.
101/// Use(bar_);
102///
103/// // Most likely a BUG! Unless `stuff` is contained within other fields,
104/// // there is probably no guarantee that it outlives `bts_`.
105/// // It should have been captured by move or by copy instead.
106/// Use(stuff);
107/// });
108/// }
109/// @endcode
110///
111/// Generally, it's a good idea to declare `bts_` after most other fields
112/// to avoid lifetime bugs. An example of fool-proof code:
113///
114/// @code
115/// private:
116/// Foo foo_;
117/// Bar bar_;
118///
119/// // bts_ must be the last field for lifetime reasons.
120/// concurrent::BackgroundTaskStorage bts_;
121/// };
122/// @endcode
123///
124/// Components and their clients can always be safely captured by reference:
125///
126/// @see @ref scripts/docs/en/userver/component_system.md
127///
128/// So for a BackgroundTaskStorage stored in a component, its tasks can only
129/// safely use the fields declared before the BTS field, as well as everything
130/// from the components, on which the current component depends.
131class BackgroundTaskStorage final {
132public:
133 /// Creates a BTS that launches tasks in the engine::TaskProcessor used at the
134 /// BTS creation.
136
137 /// Creates a BTS that launches tasks in the specified engine::TaskProcessor.
138 explicit BackgroundTaskStorage(engine::TaskProcessor& task_processor);
139
140 BackgroundTaskStorage(const BackgroundTaskStorage&) = delete;
141 BackgroundTaskStorage& operator=(const BackgroundTaskStorage&) = delete;
142
143 /// Explicitly cancel and wait for the tasks. New tasks must not be launched
144 /// after this call returns. Should be called no more than once.
145 void CancelAndWait() noexcept;
146
147 /// Explicitly stop accepting new tasks and wait for execution tasks in the
148 /// store. Should be called no more than once.
149 void CloseAndWaitDebug() noexcept;
150
151 /// @brief Launch a task that will be cancelled and waited for in the BTS
152 /// destructor.
153 ///
154 /// The task is started as non-Critical, it may be cancelled due to
155 /// `TaskProcessor` overload. engine::TaskInheritedVariable instances are not
156 /// inherited from the caller except baggage::Baggage. See
157 /// utils::AsyncBackground for details.
158 template <typename... Args>
159 void AsyncDetach(std::string name, Args&&... args) {
160 core_.Detach(utils::AsyncBackground(std::move(name), task_processor_, std::forward<Args>(args)...));
161 }
162
163 /// @brief Launch a task that will be cancelled and waited for in the BTS
164 /// destructor.
165 ///
166 /// Execution of function is guaranteed to start regardless
167 /// of engine::TaskProcessor load limits.
168 /// engine::TaskInheritedVariable instances are not
169 /// inherited from the caller except baggage::Baggage. See
170 /// utils::CriticalAsyncBackground for details.
171 template <typename... Args>
172 void CriticalAsyncDetach(std::string name, Args&&... args) {
173 core_.Detach(utils::CriticalAsyncBackground(std::move(name), task_processor_, std::forward<Args>(args)...));
174 }
175
176 /// Approximate number of currently active tasks
177 std::int64_t ActiveTasksApprox() const noexcept;
178
179private:
180 BackgroundTaskStorageCore core_;
181 engine::TaskProcessor& task_processor_;
182};
183
184} // namespace concurrent
185
186USERVER_NAMESPACE_END