8#include <userver/engine/impl/task_context_holder.hpp>
9#include <userver/engine/task/task.hpp>
10#include <userver/utils/fast_scope_guard.hpp>
11#include <userver/utils/impl/wrapped_call.hpp>
13USERVER_NAMESPACE_BEGIN
15namespace engine::impl {
17std::size_t GetTaskContextSize()
noexcept;
20inline constexpr std::size_t kTaskContextAlignment = 16;
22struct TaskConfig
final {
23 engine::TaskProcessor& task_processor;
26 engine::Deadline deadline;
29[[nodiscard]] TaskContext& PlacementNewTaskContext(
30 std::byte* storage, TaskConfig config,
31 utils::impl::WrappedCallBase& payload);
34std::byte* AllocateFusedTaskContext(std::size_t total_size);
36void DeleteFusedTaskContext(std::byte* storage)
noexcept;
46template <
typename Function,
typename... Args>
47TaskContextHolder MakeTask(TaskConfig config, Function&& f, Args&&... args) {
48 using WrappedCallType = utils::impl::WrappedCallImplType<Function, Args...>;
50 constexpr auto kPayloadSize =
sizeof(WrappedCallType);
51 constexpr auto kPayloadAlignment =
alignof(WrappedCallType);
55 static_assert(kPayloadAlignment <= kTaskContextAlignment);
57 const auto task_context_size = GetTaskContextSize();
58 const auto total_size = task_context_size + kPayloadSize;
60 std::byte*
const storage = AllocateFusedTaskContext(total_size);
61 utils::FastScopeGuard delete_guard{
62 [&]()
noexcept { DeleteFusedTaskContext(storage); }};
64 std::byte*
const payload_storage = storage + task_context_size;
66 auto& payload = utils::impl::PlacementNewWrapCall(
67 payload_storage, std::forward<Function>(f), std::forward<Args>(args)...);
68 utils::FastScopeGuard destroy_payload_guard{
69 [&]()
noexcept { std::destroy_at(&payload); }};
71 auto& context = PlacementNewTaskContext(storage, config, payload);
73 destroy_payload_guard.Release();
74 delete_guard.Release();
75 return TaskContextHolder::Adopt(context);