userver: userver/engine/wait_any.hpp Source File
Loading...
Searching...
No Matches
wait_any.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/engine/wait_any.hpp
4/// @brief Provides engine::WaitAny, engine::WaitAnyFor and engine::WaitAnyUntil
5
6#include <chrono>
7#include <optional>
8#include <vector>
9
10#include <userver/engine/deadline.hpp>
11#include <userver/utils/meta.hpp>
12#include <userver/utils/span.hpp>
13
14USERVER_NAMESPACE_BEGIN
15
16namespace engine {
17
18/// @ingroup userver_concurrency
19///
20/// @brief Waits for the completion of any of the specified tasks or the
21/// cancellation of the caller.
22///
23/// Could be used to get the ready HTTP requests ASAP:
24/// @snippet src/clients/http/client_wait_test.cpp HTTP Client - waitany
25///
26/// Works with different types of tasks and futures:
27/// @snippet src/engine/wait_any_test.cpp sample waitany
28///
29/// @param tasks either a single container, or a pack of future-like elements.
30/// @returns the index of the completed task, or `std::nullopt` if there are no
31/// completed tasks (possible if current task was cancelled).
32template <typename... Tasks>
33std::optional<std::size_t> WaitAny(Tasks&... tasks);
34
35/// @ingroup userver_concurrency
36///
37/// @overload std::optional<std::size_t> WaitAny(Tasks&... tasks)
38template <typename... Tasks, typename Rep, typename Period>
39std::optional<std::size_t> WaitAnyFor(
40 const std::chrono::duration<Rep, Period>& duration, Tasks&... tasks);
41
42/// @ingroup userver_concurrency
43///
44/// @overload std::optional<std::size_t> WaitAny(Tasks&... tasks)
45template <typename... Tasks, typename Clock, typename Duration>
46std::optional<std::size_t> WaitAnyUntil(
47 const std::chrono::time_point<Clock, Duration>& until, Tasks&... tasks);
48
49/// @ingroup userver_concurrency
50///
51/// @overload std::optional<std::size_t> WaitAny(Tasks&... tasks)
52template <typename... Tasks>
53std::optional<std::size_t> WaitAnyUntil(Deadline, Tasks&... tasks);
54
55template <typename... Tasks>
56std::optional<std::size_t> WaitAny(Tasks&... tasks) {
57 return engine::WaitAnyUntil(Deadline{}, tasks...);
58}
59
60template <typename... Tasks, typename Rep, typename Period>
61std::optional<std::size_t> WaitAnyFor(
62 const std::chrono::duration<Rep, Period>& duration, Tasks&... tasks) {
63 return engine::WaitAnyUntil(Deadline::FromDuration(duration), tasks...);
64}
65
66template <typename... Tasks, typename Clock, typename Duration>
67std::optional<std::size_t> WaitAnyUntil(
68 const std::chrono::time_point<Clock, Duration>& until, Tasks&... tasks) {
69 return engine::WaitAnyUntil(Deadline::FromTimePoint(until), tasks...);
70}
71
72namespace impl {
73
74class ContextAccessor;
75
76std::optional<std::size_t> DoWaitAny(utils::span<ContextAccessor*> targets,
77 Deadline deadline);
78
79template <typename Container>
80std::optional<std::size_t> WaitAnyFromContainer(Deadline deadline,
81 Container& tasks) {
82 const auto size = std::size(tasks);
83 std::vector<ContextAccessor*> targets;
84 targets.reserve(size);
85
86 for (auto& task : tasks) {
87 targets.push_back(task.TryGetContextAccessor());
88 }
89
90 return DoWaitAny(targets, deadline);
91}
92
93template <typename... Tasks>
94std::optional<std::size_t> WaitAnyFromTasks(Deadline deadline,
95 Tasks&... tasks) {
96 ContextAccessor* wa_elements[]{tasks.TryGetContextAccessor()...};
97 return DoWaitAny(wa_elements, deadline);
98}
99
100inline std::optional<std::size_t> WaitAnyFromTasks(Deadline) { return {}; }
101
102} // namespace impl
103
104template <typename... Tasks>
105std::optional<std::size_t> WaitAnyUntil(Deadline deadline, Tasks&... tasks) {
106 if constexpr (meta::impl::IsSingleRange<Tasks...>()) {
107 return impl::WaitAnyFromContainer(deadline, tasks...);
108 } else {
109 return impl::WaitAnyFromTasks(deadline, tasks...);
110 }
111}
112
113} // namespace engine
114
115USERVER_NAMESPACE_END