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 <variant>
7
8#include <userver/compiler/impl/lifetime.hpp>
9#include <userver/engine/async.hpp>
10#include <userver/engine/impl/task_context_factory.hpp>
11#include <userver/engine/task/shared_task_with_result.hpp>
12#include <userver/engine/task/task_with_result.hpp>
13#include <userver/utils/impl/span_wrap_call.hpp>
14#include <userver/utils/overloaded.hpp>
15
16USERVER_NAMESPACE_BEGIN
17
18namespace utils {
19
20/// @brief Builder class for @ref engine::Task and @ref engine::TaskWithResult.
21///
22/// Use it if you want to build a task with complex properties or even the property values are determined at runtime.
23/// If you just want to start a task, use @ref utils::Async or @ref engine::AsyncNoSpan.
24///
25/// @see @ref intro_tasks
26class TaskBuilder final {
27public:
28 TaskBuilder() = default;
29
30 TaskBuilder(const TaskBuilder&) = default;
31
32 TaskBuilder(TaskBuilder&&) = default;
33
34 TaskBuilder& operator=(const TaskBuilder&) = default;
35
36 TaskBuilder& operator=(TaskBuilder&&) = default;
37
38 /// Set "critical" flag for the new task.
39 /// @see @ref engine::TaskBase::Importance
40 TaskBuilder& Critical() USERVER_IMPL_LIFETIME_BOUND;
41
42 /// The following call to @ref Build will spawn a task with
43 /// @ref tracing::Span with the passed name.
44 TaskBuilder& SpanName(std::string&& name) USERVER_IMPL_LIFETIME_BOUND;
45
46 /// The following call to @ref Build will spawn a task with @ref tracing::Span,
47 /// but with @ref logging::Level::kNone log level (it hides the span log
48 /// itself, but not logs within the span). Logs will then be linked
49 /// to the nearest span that is written out.
50 TaskBuilder& HideSpan() USERVER_IMPL_LIFETIME_BOUND;
51
52 /// The following call to @ref Build will spawn a task without
53 /// any @ref tracing::Span.
54 /// @see @ref engine::AsyncNoSpan()
55 TaskBuilder& NoSpan() USERVER_IMPL_LIFETIME_BOUND;
56
57 /// The following call to @ref Build will spawn a task inside
58 /// the defined task processor. If not called,
59 /// @ref engine::current_task::GetTaskProcessor is used by default.
60 TaskBuilder& TaskProcessor(engine::TaskProcessor& tp) USERVER_IMPL_LIFETIME_BOUND;
61
62 /// The following call to @ref Build will spawn a task that has a defined deadline.
63 /// If the deadline expires, the task is cancelled.
64 /// See `*Async*` function signatures for details.
65 TaskBuilder& Deadline(engine::Deadline deadline) USERVER_IMPL_LIFETIME_BOUND;
66
67 /// The following call to @ref Build will spawn a background task
68 /// without propagating @ref engine::TaskInheritedVariable.
69 /// @ref tracing::Span and @ref baggage::Baggage are inherited.
70 TaskBuilder& Background() USERVER_IMPL_LIFETIME_BOUND;
71
72 /// Setup and return the task. It doesn't drop the previous settings,
73 /// so it can be called multiple times.
74 ///
75 /// By default, arguments are copied or moved inside the resulting
76 /// `TaskWithResult`, like `std::thread` does. To pass an argument by reference,
77 /// wrap it in `std::ref / std::cref` or capture the arguments using a lambda.
78 /// @returns engine::TaskWithResult
79 template <typename Function, typename... Args>
80 auto Build(Function&& f, Args&&... args);
81
82 /// Setup and return the task. It doesn't drop the previous settings,
83 /// so it can be called multiple times.
84 ///
85 /// By default, arguments are copied or moved inside the resulting
86 /// `TaskWithResult`, like `std::thread` does. To pass an argument by reference,
87 /// wrap it in `std::ref / std::cref` or capture the arguments using a lambda.
88 /// @returns engine::SharedTaskWithResult
89 template <typename Function, typename... Args>
90 auto BuildShared(Function&& f, Args&&... args);
91
92private:
93 template <typename Task>
94 engine::impl::TaskConfig MakeConfig() const;
95
96 auto MakeSpanFunctor(
97 std::string name,
98 utils::impl::SpanWrapCall::HideSpan hide_span,
99 const utils::impl::SourceLocation& location = utils::impl::SourceLocation::Current()
100 );
101
102 template <typename Task, typename Function, typename... Args>
103 Task BuildTask(Function&& f, Args&&... args);
104
105 struct NoSpanTag {};
106
107 struct HideSpanTag {};
108
109 // Note: do not use impl::TaskConfig as it stores a ref to TaskProcessor, which cannot be nullptr
110 engine::TaskProcessor* tp_{nullptr};
111 engine::Task::Importance importance_{engine::Task::Importance::kNormal};
112 engine::Deadline deadline_;
113 std::optional<std::variant<std::string, NoSpanTag, HideSpanTag>> span_;
114 utils::impl::SpanWrapCall::InheritVariables inherit_variables_{utils::impl::SpanWrapCall::InheritVariables::kYes};
115};
116
117inline auto TaskBuilder::MakeSpanFunctor(
118 std::string name,
119 utils::impl::SpanWrapCall::HideSpan hide_span,
120 const utils::impl::SourceLocation& location
121) {
122 return utils::impl::SpanLazyPrvalue(std::move(name), inherit_variables_, hide_span, location);
123}
124
125template <typename Task>
126engine::impl::TaskConfig TaskBuilder::MakeConfig() const {
127 auto& tp = tp_ ? *tp_ : engine::current_task::GetTaskProcessor();
128 return {tp, importance_, Task::kWaitMode, deadline_};
129}
130
131template <typename Function, typename... Args>
132auto TaskBuilder::Build(Function&& f, Args&&... args) {
133 using Task = engine::TaskWithResult<std::invoke_result_t<Function, Args...>>;
134 return BuildTask<Task>(std::forward<Function>(f), std::forward<Args>(args)...);
135}
136
137template <typename Function, typename... Args>
138auto TaskBuilder::BuildShared(Function&& f, Args&&... args) {
139 using Task = engine::SharedTaskWithResult<std::invoke_result_t<Function, Args...>>;
140 return BuildTask<Task>(std::forward<Function>(f), std::forward<Args>(args)...);
141}
142
143template <typename Task, typename Function, typename... Args>
144Task TaskBuilder::BuildTask(Function&& f, Args&&... args) {
146 span_,
147 "Exactly one of the following methods of TaskBuilder must be called: SpanName(), NoSpan(), HideSpan()"
148 );
149
150 using HideSpan = utils::impl::SpanWrapCall::HideSpan;
151
152 return utils::Visit(
153 *span_,
154 [&](const std::string& name) {
155 return Task{engine::impl::MakeTask(
156 MakeConfig<Task>(),
157 MakeSpanFunctor({name}, HideSpan::kNo),
158 std::forward<Function>(f),
159 std::forward<Args>(args)...
160 )};
161 },
162 [&](NoSpanTag) {
163 return Task{
164 engine::impl::MakeTask(MakeConfig<Task>(), std::forward<Function>(f), std::forward<Args>(args)...)
165 };
166 },
167 [&](HideSpanTag) {
168 return Task{engine::impl::MakeTask(
169 MakeConfig<Task>(),
170 MakeSpanFunctor({}, HideSpan::kYes),
171 std::forward<Function>(f),
172 std::forward<Args>(args)...
173 )};
174 }
175 );
176}
177
178} // namespace utils
179
180USERVER_NAMESPACE_END