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 /// @brief Explicitly cancel and wait for the tasks.
31 /// New tasks must not be launched after this call returns.
32 ///
33 /// More precisely, new tasks must not be launched once this call is completed, but can be launched in the process
34 /// of waiting for the remaining tasks. This means that one detached task is allowed to spawn another detached
35 /// task into the same BTS, which will immediately be cancelled and for which this call will wait as well.
36 ///
37 /// Can only be called from a coroutine.
38 void CancelAndWait() noexcept;
39
40 /// @brief Wait for the remaining tasks to complete without cancelling them.
41 /// New tasks must not be launched after this call returns.
42 ///
43 /// More precisely, new tasks must not be launched once this call is completed, but can be launched in the process
44 /// of waiting for the remaining tasks. This means that one detached task is allowed to spawn another detached
45 /// task into the same BTS, for which this call will wait as well.
46 ///
47 /// @note Running this method burns some CPU time (a few microseconds). Awaiting the tasks once on service
48 /// shutdown, or even periodically, is okay, but don't use `BackgroundTaskStorage` as a substitute
49 /// for `std::vector<Task>` in handlers, because it is less efficient.
50 ///
51 /// Can only be called from a coroutine.
52 void WaitAndDisposeSlow() noexcept;
53
54 /// @brief Detaches task, allowing it to continue execution out of scope. It
55 /// will be cancelled and waited for on BTS destruction.
56 /// @note After detach, Task becomes invalid
57 ///
58 /// Can be called from a coroutine or a non-coroutine thread.
59 /// Cannot be called after @ref CancelAndWait or @ref WaitAndDisposeSlow has been called.
60 void Detach(engine::Task&& task);
61
62 /// @returns Approximate number of currently active tasks.
63 ///
64 /// Can be called from a coroutine or a non-coroutine thread.
65 /// Cannot be called after @ref CancelAndWait or @ref WaitAndDisposeSlow has been called.
66 std::int64_t ActiveTasksApprox() const noexcept;
67
68private:
69 std::optional<engine::impl::DetachedTasksSyncBlock> sync_block_;
70};
71
72/// @ingroup userver_concurrency userver_containers
73///
74/// A storage that allows one to start detached tasks; cancels and waits for
75/// unfinished tasks completion at the destructor. Provides CancelAndWait to
76/// explicitly cancel tasks (recommended).
77///
78/// Usable for detached tasks that capture references to resources with a
79/// limited lifetime. You must guarantee that the resources are available while
80/// the BackgroundTaskStorage is alive.
81///
82/// ## Performance considerations
83///
84/// Tasks remove themselves from the BackgroundTaskStorage on completion, so there is no memory leak.
85///
86/// @warning The implementation is optimized for spawning tasks. Waiting for remaining tasks may be slow CPU-wise.
87/// As a guideline, do not create `BackgroundTaskStorage` instances for each request, use `std::vector<Task>`
88/// for storing per-request child tasks instead.
89///
90/// ## Usage synopsis
91///
92/// @snippet core/src/concurrent/background_task_storage_test.cpp Sample
93///
94/// ## Lifetime of task's captures
95///
96/// All the advice from utils::Async is applicable here.
97///
98/// BackgroundTaskStorage is always stored as a class field. Tasks that are
99/// launched inside it (or moved inside it, for BackgroundTaskStorageCore)
100/// can safely access fields declared before it, but not after it:
101///
102/// @snippet core/src/concurrent/background_task_storage_test.cpp BtsLifetimeCapturesPitfalls
103///
104/// Generally, it's a good idea to declare `bts_` after most other fields
105/// to avoid lifetime bugs. An example of fool-proof code:
106///
107/// @snippet core/src/concurrent/background_task_storage_test.cpp Bts field ordering
108///
109/// Components and their clients can always be safely captured by reference:
110///
111/// @see @ref scripts/docs/en/userver/component_system.md
112///
113/// So for a BackgroundTaskStorage stored in a component, its tasks can only
114/// safely use the fields declared before the BTS field, as well as everything
115/// from the components, on which the current component depends.
116class BackgroundTaskStorage final {
117public:
118 /// Creates a BTS that launches tasks in the engine::TaskProcessor used at the
119 /// BTS creation.
121
122 /// Creates a BTS that launches tasks in the specified engine::TaskProcessor.
123 explicit BackgroundTaskStorage(engine::TaskProcessor& task_processor);
124
125 BackgroundTaskStorage(const BackgroundTaskStorage&) = delete;
126 BackgroundTaskStorage& operator=(const BackgroundTaskStorage&) = delete;
127
128 /// @brief Explicitly cancel and wait for the tasks.
129 /// New tasks must not be launched after this call returns.
130 ///
131 /// More precisely, new tasks must not be launched once this call is completed, but can be launched in the process
132 /// of waiting for the remaining tasks. This means that one detached task is allowed to spawn another detached
133 /// task into the same BTS, which will immediately be cancelled and for which this call will wait as well.
134 ///
135 /// Can only be called from a coroutine.
136 void CancelAndWait() noexcept;
137
138 /// @brief Wait for the remaining tasks to complete without cancelling them.
139 /// New tasks must not be launched after this call returns.
140 ///
141 /// More precisely, new tasks must not be launched once this call is completed, but can be launched in the process
142 /// of waiting for the remaining tasks. This means that one detached task is allowed to spawn another detached
143 /// task into the same BTS, for which this call will wait as well.
144 ///
145 /// @note Running this method burns some CPU time (a few microseconds). Awaiting the tasks once on service
146 /// shutdown, or even periodically, is okay, but don't use `BackgroundTaskStorage` as a substitute
147 /// for `std::vector<Task>` in handlers, because it is less efficient.
148 ///
149 /// Can only be called from a coroutine.
150 void WaitAndDisposeSlow() noexcept;
151
152 /// @brief Launch a task that will be cancelled and waited for in the BTS
153 /// destructor.
154 ///
155 /// The task is started as non-Critical, it may be cancelled due to
156 /// `TaskProcessor` overload. engine::TaskInheritedVariable instances are not
157 /// inherited from the caller except baggage::Baggage. See
158 /// utils::AsyncBackground for details.
159 ///
160 /// Can be called from a coroutine or a non-coroutine thread.
161 template <typename... Args>
162 void AsyncDetach(std::string name, Args&&... args) {
163 core_.Detach(utils::AsyncBackground(std::move(name), task_processor_, std::forward<Args>(args)...));
164 }
165
166 /// @brief Launch a task that will be cancelled and waited for in the BTS
167 /// destructor.
168 ///
169 /// Execution of function is guaranteed to start regardless
170 /// of engine::TaskProcessor load limits.
171 /// engine::TaskInheritedVariable instances are not
172 /// inherited from the caller except baggage::Baggage. See
173 /// utils::CriticalAsyncBackground for details.
174 ///
175 /// Can be called from a coroutine or a non-coroutine thread.
176 template <typename... Args>
177 void CriticalAsyncDetach(std::string name, Args&&... args) {
178 core_.Detach(utils::CriticalAsyncBackground(std::move(name), task_processor_, std::forward<Args>(args)...));
179 }
180
181 /// @returns Approximate number of currently active tasks.
182 ///
183 /// Can be called from a coroutine or a non-coroutine thread.
184 /// Cannot be called after @ref CancelAndWait or @ref WaitAndDisposeSlow has been called.
185 std::int64_t ActiveTasksApprox() const noexcept;
186
187private:
188 BackgroundTaskStorageCore core_;
189 engine::TaskProcessor& task_processor_;
190};
191
192} // namespace concurrent
193
194USERVER_NAMESPACE_END