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/// ## 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 {
128public:
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_, std::forward<Args>(args)...));
157 }
158
159 /// @brief Launch a task that will be cancelled and waited for in the BTS
160 /// destructor.
161 ///
162 /// Execution of function is guaranteed to start regardless
163 /// of engine::TaskProcessor load limits.
164 /// engine::TaskInheritedVariable instances are not
165 /// inherited from the caller except baggage::Baggage. See
166 /// utils::CriticalAsyncBackground for details.
167 template <typename... Args>
168 void CriticalAsyncDetach(std::string name, Args&&... args) {
169 core_.Detach(utils::CriticalAsyncBackground(std::move(name), task_processor_, std::forward<Args>(args)...));
170 }
171
172 /// Approximate number of currently active tasks
173 std::int64_t ActiveTasksApprox() const noexcept;
174
175private:
176 BackgroundTaskStorageCore core_;
177 engine::TaskProcessor& task_processor_;
178};
179
180} // namespace concurrent
181
182USERVER_NAMESPACE_END