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