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/// @snippet concurrent/background_task_storage_test.cpp Bts field ordering
88///
89/// Components and their clients can always be safely captured by reference:
90///
91/// @see @ref scripts/docs/en/userver/component_system.md
92///
93/// So for a BackgroundTaskStorage stored in a component, its tasks can only
94/// safely use the fields declared before the BTS field, as well as everything
95/// from the components, on which the current component depends.
96class BackgroundTaskStorage final {
97public:
98 /// Creates a BTS that launches tasks in the engine::TaskProcessor used at the
99 /// BTS creation.
101
102 /// Creates a BTS that launches tasks in the specified engine::TaskProcessor.
103 explicit BackgroundTaskStorage(engine::TaskProcessor& task_processor);
104
105 BackgroundTaskStorage(const BackgroundTaskStorage&) = delete;
106 BackgroundTaskStorage& operator=(const BackgroundTaskStorage&) = delete;
107
108 /// @brief Explicitly cancel and wait for the tasks. New tasks must not be launched
109 /// after this call returns. Should be called no more than once.
110 ///
111 /// More precisely, new tasks must not be launched once this call is completed, but can be launched in the process
112 /// of waiting for the remaining tasks. This means that one detached task is allowed to spawn another detached
113 /// task into the same BTS, which will immediately be cancelled and for which `CancelAndWait` will wait as well.
114 ///
115 /// Can only be called from a coroutine.
116 void CancelAndWait() noexcept;
117
118 /// @brief Explicitly stop accepting new tasks and wait for execution tasks in the
119 /// store. Should be called no more than once.
120 ///
121 /// Can only be called from a coroutine.
122 void CloseAndWaitDebug() noexcept;
123
124 /// @brief Launch a task that will be cancelled and waited for in the BTS
125 /// destructor.
126 ///
127 /// The task is started as non-Critical, it may be cancelled due to
128 /// `TaskProcessor` overload. engine::TaskInheritedVariable instances are not
129 /// inherited from the caller except baggage::Baggage. See
130 /// utils::AsyncBackground for details.
131 ///
132 /// Can be called from a coroutine or a non-coroutine thread.
133 template <typename... Args>
134 void AsyncDetach(std::string name, Args&&... args) {
135 core_.Detach(utils::AsyncBackground(std::move(name), task_processor_, std::forward<Args>(args)...));
136 }
137
138 /// @brief Launch a task that will be cancelled and waited for in the BTS
139 /// destructor.
140 ///
141 /// Execution of function is guaranteed to start regardless
142 /// of engine::TaskProcessor load limits.
143 /// engine::TaskInheritedVariable instances are not
144 /// inherited from the caller except baggage::Baggage. See
145 /// utils::CriticalAsyncBackground for details.
146 ///
147 /// Can be called from a coroutine or a non-coroutine thread.
148 template <typename... Args>
149 void CriticalAsyncDetach(std::string name, Args&&... args) {
150 core_.Detach(utils::CriticalAsyncBackground(std::move(name), task_processor_, std::forward<Args>(args)...));
151 }
152
153 /// Approximate number of currently active tasks
154 std::int64_t ActiveTasksApprox() const noexcept;
155
156private:
157 BackgroundTaskStorageCore core_;
158 engine::TaskProcessor& task_processor_;
159};
160
161} // namespace concurrent
162
163USERVER_NAMESPACE_END