userver: userver/utils/task_builder.hpp Source File
Loading...
Searching...
No Matches
task_builder.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/utils/task_builder.hpp
4/// @brief @copybrief utils::TaskBuilder
5
6#include <userver/compiler/impl/lifetime.hpp>
7#include <userver/engine/impl/task_context_factory.hpp>
8#include <userver/engine/task/shared_task_with_result.hpp>
9#include <userver/engine/task/task_with_result.hpp>
10#include <userver/utils/impl/span_wrap_call.hpp>
11
12USERVER_NAMESPACE_BEGIN
13
14namespace utils {
15
16namespace impl {
17
18struct TaskBuilderOptions final {
19 engine::TaskProcessor* task_processor{nullptr};
20 engine::Task::Importance importance{engine::Task::Importance::kNormal};
21 engine::Deadline deadline;
22 bool inherit_variables{true};
23};
24
25struct TaskBuilderWithoutSelectedSpanOptions final {};
26
27struct TaskBuilderWithSpanOptions final {
28 std::string span_name;
29};
30
31struct TaskBuilderHideSpanOptions final {};
32
33struct TaskBuilderNoTracingOptions final {};
34
35template <typename Task>
36engine::impl::TaskConfig MakeTaskConfig(const TaskBuilderOptions& options) {
37 return {
38 .task_processor = options.task_processor,
39 .importance = options.importance,
40 .wait_mode = Task::kWaitMode,
41 .deadline = options.deadline,
42 .inherited_variables_priority =
43 options.inherit_variables
46 };
47}
48
49template <typename Task, typename Function, typename... Args>
50Task BuildTask(
51 const TaskBuilderOptions&,
52 const TaskBuilderWithoutSelectedSpanOptions&,
53 const utils::impl::SourceLocation&,
54 Function&&,
55 Args&&...
56) {
57 static_assert(
58 !sizeof(Task),
59 "Exactly one of the following methods of TaskBuilder must be called: SpanName(), NoTracing(), HideSpan()"
60 );
61}
62
63template <typename Task, typename Function, typename... Args>
64Task BuildTask(
65 const TaskBuilderOptions& options,
66 const TaskBuilderWithSpanOptions& options_ext,
67 const utils::impl::SourceLocation& source_location,
68 Function&& f,
69 Args&&... args
70) {
71 return Task{engine::impl::MakeTask(
72 impl::MakeTaskConfig<Task>(options),
73 utils::impl::SpanLazyPrvalue(
74 std::string{options_ext.span_name},
75 utils::impl::SpanWrapCall::HideSpan::kNo,
76 source_location
77 ),
78 std::forward<Function>(f),
79 std::forward<Args>(args)...
80 )};
81}
82
83template <typename Task, typename Function, typename... Args>
84Task BuildTask(
85 const TaskBuilderOptions& options,
86 const TaskBuilderHideSpanOptions&,
87 const utils::impl::SourceLocation& source_location,
88 Function&& f,
89 Args&&... args
90) {
91 return Task{engine::impl::MakeTask(
92 impl::MakeTaskConfig<Task>(options),
93 utils::impl::SpanLazyPrvalue(std::string{}, utils::impl::SpanWrapCall::HideSpan::kYes, source_location),
94 std::forward<Function>(f),
95 std::forward<Args>(args)...
96 )};
97}
98
99template <typename Task, typename Function, typename... Args>
100Task BuildTask(
101 const TaskBuilderOptions& options,
102 const TaskBuilderNoTracingOptions&,
103 const utils::impl::SourceLocation&,
104 Function&& f,
105 Args&&... args
106) {
107 // TODO support NoTracing + inherited variables.
108 UINVARIANT(!options.inherit_variables, "Task-inherited variables without span are not supported at the moment");
109
110 auto config = impl::MakeTaskConfig<Task>(options);
111 config.inherited_variables_priority = engine::TaskInheritedVariablePriority::kNoTracing;
112
113 return Task{engine::impl::MakeTask(std::move(config), std::forward<Function>(f), std::forward<Args>(args)...)};
114}
115
116} // namespace impl
117
118template <typename OptionsImpl>
119class TaskBuilder;
120
121/// @brief A @ref TaskBuilder with a set span name, see @ref TaskBuilder::SpanName.
122using TaskBuilderWithSpan = TaskBuilder<impl::TaskBuilderWithSpanOptions>;
123
124/// @brief A @ref TaskBuilder with a hidden span, see @ref TaskBuilder::HideSpan.
125using TaskBuilderHideSpan = TaskBuilder<impl::TaskBuilderHideSpanOptions>;
126
127/// @brief A @ref TaskBuilder without a span, see @ref TaskBuilder::NoTracing.
128using TaskBuilderNoTracing = TaskBuilder<impl::TaskBuilderNoTracingOptions>;
129
130/// @brief A @ref TaskBuilder for which span options have not been selected yet.
131using TaskBuilderBase = TaskBuilder<impl::TaskBuilderWithoutSelectedSpanOptions>;
132
133/// @brief Builder class for @ref engine::Task and @ref engine::TaskWithResult.
134///
135/// Use it if you want to build a task with complex properties or even the property values are determined at runtime.
136/// If you just want to start a task, use @ref utils::Async or @ref engine::AsyncNoTracing.
137///
138/// To use, first default-construct as `utils::TaskBuilder{}`.
139///
140/// Then select span mode using one of:
141/// * @ref TaskBuilder::SpanName
142/// * @ref TaskBuilder::HideSpan
143/// * @ref TaskBuilder::NoTracing
144///
145/// Then spawn a task using one of:
146/// * @ref TaskBuilder::Build
147/// * @ref TaskBuilder::BuildShared
148///
149/// Example:
150/// @snippet core/src/utils/task_builder_test.cpp snippet
151///
152/// @see @ref intro_tasks
153template <typename OptionsImpl>
154class TaskBuilder final {
155public:
156 TaskBuilder()
157 requires std::same_as<TaskBuilder, TaskBuilderBase>
158 = default;
159
160 TaskBuilder(const TaskBuilder&) = default;
161 TaskBuilder(TaskBuilder&&) = default;
162 TaskBuilder& operator=(const TaskBuilder&) = default;
163 TaskBuilder& operator=(TaskBuilder&&) = default;
164
165 /// The following call to @ref Build will spawn a task with
166 /// @ref tracing::Span with the passed name.
167 [[nodiscard]] TaskBuilderWithSpan SpanName(std::string&& name)
168 requires std::same_as<TaskBuilder, TaskBuilderBase>
169 {
170 return TaskBuilderWithSpan{*this, impl::TaskBuilderWithSpanOptions{.span_name = std::move(name)}};
171 }
172
173 /// The following call to @ref Build will spawn a task with @ref tracing::Span,
174 /// but with @ref logging::Level::kNone log level (it hides the span log
175 /// itself, but not logs within the span). Logs will then be linked
176 /// to the nearest span that is written out.
178 requires std::same_as<TaskBuilder, TaskBuilderBase>
179 {
180 return TaskBuilderHideSpan{*this, impl::TaskBuilderHideSpanOptions{}};
181 }
182
183 /// The following call to @ref Build will spawn a task without
184 /// any @ref tracing::Span.
185 /// @see @ref engine::AsyncNoTracing()
187 requires std::same_as<TaskBuilder, TaskBuilderBase>
188 {
189 return TaskBuilderNoTracing{*this, impl::TaskBuilderNoTracingOptions{}};
190 }
191
192 /// Set "critical" flag for the new task.
193 /// @see @ref engine::TaskBase::Importance
194 TaskBuilder& Critical() USERVER_IMPL_LIFETIME_BOUND {
195 options_.importance = engine::Task::Importance::kCritical;
196 return *this;
197 }
198
199 /// The following call to @ref Build will spawn a task inside
200 /// the defined task processor. If not called,
201 /// @ref engine::current_task::GetTaskProcessor is used by default.
202 TaskBuilder& TaskProcessor(engine::TaskProcessor& tp) USERVER_IMPL_LIFETIME_BOUND {
203 options_.task_processor = &tp;
204 return *this;
205 }
206
207 /// The following call to @ref Build will spawn a task that has a defined deadline.
208 /// If the deadline expires, the task is cancelled.
209 /// See `*Async*` function signatures for details.
210 TaskBuilder& Deadline(engine::Deadline deadline) USERVER_IMPL_LIFETIME_BOUND {
211 options_.deadline = deadline;
212 return *this;
213 }
214
215 /// The following call to @ref Build will spawn a background task
216 /// without propagating @ref engine::TaskInheritedVariable.
217 /// @ref tracing::Span and @ref baggage::Baggage are inherited.
218 TaskBuilder& Background() USERVER_IMPL_LIFETIME_BOUND {
219 options_.inherit_variables = false;
220 return *this;
221 }
222
223 /// Setup and return the task. It doesn't drop the previous settings,
224 /// so it can be called multiple times.
225 ///
226 /// By default, arguments are copied or moved inside the resulting
227 /// `TaskWithResult`, like `std::thread` does. To pass an argument by reference,
228 /// wrap it in `std::ref / std::cref` or capture the arguments using a lambda.
229 /// @returns engine::TaskWithResult
230 template <typename Function, typename... Args>
231 [[nodiscard]] auto Build(Function&& f, Args&&... args) const;
232
233 /// Setup and return the task. It doesn't drop the previous settings,
234 /// so it can be called multiple times.
235 ///
236 /// By default, arguments are copied or moved inside the resulting
237 /// `TaskWithResult`, like `std::thread` does. To pass an argument by reference,
238 /// wrap it in `std::ref / std::cref` or capture the arguments using a lambda.
239 /// @returns engine::SharedTaskWithResult
240 template <typename Function, typename... Args>
241 [[nodiscard]] auto BuildShared(Function&& f, Args&&... args) const;
242
243private:
244 template <typename OtherOptions>
245 friend class TaskBuilder;
246
247 template <typename OtherOptions>
248 TaskBuilder(const TaskBuilder<OtherOptions>& other, OptionsImpl&& options_ext)
249 : options_(other.options_),
250 options_ext_(std::move(options_ext))
251 {}
252
253 impl::TaskBuilderOptions options_{};
254 [[no_unique_address]] OptionsImpl options_ext_{};
255};
256
257/// Ensures that `TaskBuilder{}` produces a @ref TaskBuilderBase.
258TaskBuilder() -> TaskBuilder<impl::TaskBuilderWithoutSelectedSpanOptions>;
259
260template <typename OptionsImpl>
261template <typename Function, typename... Args>
262[[nodiscard]] auto TaskBuilder<OptionsImpl>::Build(Function&& f, Args&&... args) const {
263 using Task = engine::TaskWithResult<std::invoke_result_t<Function, Args...>>;
264 return impl::BuildTask<Task>(
265 options_,
266 options_ext_,
267 utils::impl::SourceLocation::Current(),
268 std::forward<Function>(f),
269 std::forward<Args>(args)...
270 );
271}
272
273template <typename OptionsImpl>
274template <typename Function, typename... Args>
275[[nodiscard]] auto TaskBuilder<OptionsImpl>::BuildShared(Function&& f, Args&&... args) const {
276 using Task = engine::SharedTaskWithResult<std::invoke_result_t<Function, Args...>>;
277 return impl::BuildTask<Task>(
278 options_,
279 options_ext_,
280 utils::impl::SourceLocation::Current(),
281 std::forward<Function>(f),
282 std::forward<Args>(args)...
283 );
284}
285
286extern template class TaskBuilder<impl::TaskBuilderWithoutSelectedSpanOptions>;
287extern template class TaskBuilder<impl::TaskBuilderWithSpanOptions>;
288extern template class TaskBuilder<impl::TaskBuilderHideSpanOptions>;
289extern template class TaskBuilder<impl::TaskBuilderNoTracingOptions>;
290
291} // namespace utils
292
293USERVER_NAMESPACE_END