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