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, engine::WaitAnyUntil and engine::MakeWaitAny
5
6#include <chrono>
7#include <cstdint>
8#include <optional>
9#include <vector>
10
11#include <boost/smart_ptr/intrusive_ptr.hpp>
12
13#include <userver/engine/deadline.hpp>
14#include <userver/utils/fast_pimpl.hpp>
15#include <userver/utils/meta.hpp>
16#include <userver/utils/span.hpp>
17
18USERVER_NAMESPACE_BEGIN
19
20namespace engine {
21
22/// @ingroup userver_concurrency
23///
24/// @deprecated `WaitAny` is deprecated. Please, prefer @ref MakeWaitAny + @ref WaitAnyContext::Wait.
25/// @brief Waits for the completion of any of the specified tasks or the
26/// cancellation of the caller.
27///
28/// Could be used to get the ready HTTP requests ASAP:
29/// @snippet src/clients/http/client_wait_test.cpp HTTP Client - waitany
30///
31/// Works with different types of tasks and futures:
32/// @snippet src/engine/wait_any_test.cpp sample waitany
33///
34/// @param tasks either a single container, or a pack of future-like elements.
35/// @returns the index of the completed task, or `std::nullopt` if there are no
36/// completed tasks (possible if current task was cancelled).
37template <typename... Tasks>
38std::optional<std::size_t> WaitAny(Tasks&... tasks);
39
40/// @ingroup userver_concurrency
41///
42/// @deprecated `WaitAnyFor` is deprecated. Please, prefer @ref MakeWaitAny + @ref WaitAnyContext::WaitFor().
43/// @overload std::optional<std::size_t> WaitAny(Tasks&... tasks)
44template <typename... Tasks, typename Rep, typename Period>
45std::optional<std::size_t> WaitAnyFor(const std::chrono::duration<Rep, Period>& duration, Tasks&... tasks);
46
47/// @ingroup userver_concurrency
48///
49/// @deprecated `WaitAnyUntil` is deprecated. Please, prefer @ref MakeWaitAny + @ref WaitAnyContext::WaitUntil.
50/// @overload std::optional<std::size_t> WaitAny(Tasks&... tasks)
51template <typename... Tasks, typename Clock, typename Duration>
52std::optional<std::size_t> WaitAnyUntil(const std::chrono::time_point<Clock, Duration>& until, Tasks&... tasks);
53
54/// @ingroup userver_concurrency
55///
56/// @deprecated `WaitAnyUntil` is deprecated. Please, prefer @ref MakeWaitAny + @ref WaitAnyContext::WaitUntil.
57/// @overload std::optional<std::size_t> WaitAny(Tasks&... tasks)
58template <typename... Tasks>
59std::optional<std::size_t> WaitAnyUntil(Deadline, Tasks&... tasks);
60
61template <typename... Tasks>
62std::optional<std::size_t> WaitAny(Tasks&... tasks) {
63 return engine::WaitAnyUntil(Deadline{}, tasks...);
64}
65
66template <typename... Tasks, typename Rep, typename Period>
67std::optional<std::size_t> WaitAnyFor(const std::chrono::duration<Rep, Period>& duration, Tasks&... tasks) {
68 return engine::WaitAnyUntil(Deadline::FromDuration(duration), tasks...);
69}
70
71template <typename... Tasks, typename Clock, typename Duration>
72std::optional<std::size_t> WaitAnyUntil(const std::chrono::time_point<Clock, Duration>& until, Tasks&... tasks) {
73 return engine::WaitAnyUntil(Deadline::FromTimePoint(until), tasks...);
74}
75
76namespace impl {
77
78class ContextAccessor;
79
80std::optional<std::size_t> DoWaitAny(utils::span<ContextAccessor*> targets, Deadline deadline);
81
82template <typename Container>
83std::optional<std::size_t> WaitAnyFromContainer(Deadline deadline, Container& tasks) {
84 const auto size = std::size(tasks);
85 std::vector<ContextAccessor*> targets;
86 targets.reserve(size);
87
88 for (auto& task : tasks) {
89 targets.push_back(task.TryGetContextAccessor());
90 }
91
92 return DoWaitAny(targets, deadline);
93}
94
95template <typename... Tasks>
96std::optional<std::size_t> WaitAnyFromTasks(Deadline deadline, Tasks&... tasks) {
97 ContextAccessor* wa_elements[]{tasks.TryGetContextAccessor()...};
98 return DoWaitAny(wa_elements, deadline);
99}
100
101inline std::optional<std::size_t> WaitAnyFromTasks(Deadline) { return {}; }
102
103} // namespace impl
104
105template <typename... Tasks>
106std::optional<std::size_t> WaitAnyUntil(Deadline deadline, Tasks&... tasks) {
107 if constexpr (meta::impl::IsSingleRange<Tasks...>()) {
108 return impl::WaitAnyFromContainer(deadline, tasks...);
109 } else {
110 return impl::WaitAnyFromTasks(deadline, tasks...);
111 }
112}
113
114/// @ingroup userver_concurrency
115///
116/// @brief Stores a set of awaitables and allows waiting for completion of any of the stored awaitables.
117///
118/// Works with different types of awaitables:
119/// @snippet src/engine/wait_any_test.cpp sample MakeWaitAny
120///
121/// No methods (except .dtor) should be called on a moved-out instance.
122class WaitAnyContext final {
123public:
124 WaitAnyContext();
125 ~WaitAnyContext() noexcept;
126 WaitAnyContext(WaitAnyContext&&) noexcept;
127 WaitAnyContext& operator=(WaitAnyContext&& other) noexcept;
128 WaitAnyContext(const WaitAnyContext&) = delete;
129 WaitAnyContext& operator=(const WaitAnyContext&) = delete;
130
131 /// @brief Append the given awaitables and sequences of awaitables to the context.
132 ///
133 /// Each passed awaitable could be either a single awaitable or a container of awaitables.
134 /// In the latter case all awaitables from the container are appended to the context.
135 /// The appended awaitables will have indexes [GetNextIndex() before the call, GetNextIndex() after the call - 1].
136 template <typename... Awaitables>
137 void Append(Awaitables&... awaitables);
138
139 /// @brief Waits either for the completion of any of the awaitables stored in the context
140 /// or for the cancellation of the caller.
141 ///
142 /// @returns the index of the completed awaitable, or `std::nullopt` if there are no
143 /// completed awaitables (possible if current task was cancelled).
144 std::optional<std::uint64_t> Wait();
145
146 /// @overload std::optional<std::size_t> Wait()
147 std::optional<std::uint64_t> WaitUntil(Deadline deadline);
148
149 /// @overload std::optional<std::size_t> Wait()
150 template <typename Rep, typename Period>
151 std::optional<std::uint64_t> WaitFor(const std::chrono::duration<Rep, Period>& duration) {
152 return WaitUntil(Deadline::FromDuration(duration));
153 }
154
155 /// @overload std::optional<std::size_t> Wait()
156 template <typename Clock, typename Duration>
157 std::optional<std::uint64_t> WaitUntil(const std::chrono::time_point<Clock, Duration>& until) {
158 return WaitUntil(Deadline::FromTimePoint(until));
159 }
160
161 /// @brief Returns the number of awaitables actually stored in the context.
162 ///
163 /// It consists of actively awaited and pending subscription awaitables.
164 /// Already notified awaitables are dropped out.
165 std::size_t GetSize() const noexcept;
166
167 /// @brief Returns the next awaitable index.
168 ///
169 /// It could be used to calculate indexes of awaitables appended via Append call.
170 std::uint64_t GetNextIndex() const noexcept;
171
172private:
173 class Impl;
174
175 template <typename Container>
176 void AppendFromContainer(Container& awaitables);
177
178 template <typename Awaitable>
179 void AppendSingle(Awaitable& awaitable);
180
181 void AppendAccessor(impl::ContextAccessor* awaitable);
182
183 boost::intrusive_ptr<Impl> impl_;
184};
185
186template <typename Container>
187void WaitAnyContext::AppendFromContainer(Container& awaitables) {
188 for (auto& awaitable : awaitables) {
189 AppendAccessor(awaitable.TryGetContextAccessor());
190 }
191}
192
193template <typename Awaitable>
194void WaitAnyContext::AppendSingle(Awaitable& awaitable) {
195 if constexpr (meta::impl::IsSingleRange<Awaitable>()) {
196 AppendFromContainer(awaitable);
197 } else {
198 AppendAccessor(awaitable.TryGetContextAccessor());
199 }
200}
201
202template <typename... Awaitables>
203void WaitAnyContext::Append(Awaitables&... awaitables) {
204 (AppendSingle(awaitables), ...);
205}
206
207/// @ingroup userver_concurrency
208///
209/// @brief Produces a WaitAnyContext for the given awaitables and sequences of awaitables.
210///
211/// Each passed awaitable could be either a single awaitable or a container of awaitables.
212/// In the latter case all awaitables from the container are appended to the context.
213/// The stored awaitables will have indexes [0, GetNextIndex() - 1].
214template <typename... Awaitables>
215WaitAnyContext MakeWaitAny(Awaitables&... awaitables) {
216 auto context = WaitAnyContext();
217 if constexpr (sizeof...(Awaitables) > 0) {
218 context.Append(awaitables...);
219 }
220 return context;
221}
222
223} // namespace engine
224
225USERVER_NAMESPACE_END