27#include <seastar/core/coroutine.hh>
29namespace seastar::coroutine {
31template <
typename Future>
32constexpr inline bool is_future_v = is_future<Future>::value;
34template <
typename Future>
35concept future_type = is_future_v<Future>;
43template <
typename IndexSequence,
size_t current,
typename... Futures>
44struct index_sequence_for_non_void_futures_helper;
47template <
typename IndexSequence,
size_t current>
48struct index_sequence_for_non_void_futures_helper<IndexSequence, current> {
49 using type = IndexSequence;
53template <
size_t... Existing,
size_t current,
typename T,
typename... Futures>
54struct index_sequence_for_non_void_futures_helper<
std::integer_sequence<size_t, Existing...>, current, future<T>, Futures...> {
55 using type =
typename index_sequence_for_non_void_futures_helper<std::integer_sequence<size_t, Existing..., current>, current + 1, Futures...>::type;
59template <
size_t... Existing,
size_t current,
typename... Futures>
60struct index_sequence_for_non_void_futures_helper<
std::integer_sequence<size_t, Existing...>, current, future<>, Futures...> {
61 using type =
typename index_sequence_for_non_void_futures_helper<std::integer_sequence<size_t, Existing...>, current + 1, Futures...>::type;
65template <
typename... Futures>
66using index_sequence_for_non_void_futures =
typename index_sequence_for_non_void_futures_helper<std::integer_sequence<size_t>, 0, Futures...>::type;
69template <
typename IndexSequence,
typename FutureTuple>
70struct value_tuple_for_non_void_futures_helper;
72template <
size_t... Idx,
typename FutureTuple>
73struct value_tuple_for_non_void_futures_helper<
std::integer_sequence<size_t, Idx...>, FutureTuple> {
74 using type = std::tuple<typename std::tuple_element_t<Idx, FutureTuple>::value_type...>;
78template <
typename... Futures>
79using value_tuple_for_non_void_futures =
typename value_tuple_for_non_void_futures_helper<index_sequence_for_non_void_futures<Futures...>, std::tuple<Futures...>>::type;
115template <
typename... Futures>
116requires (
sizeof ...(Futures) > 0)
117class [[nodiscard("must co_await an
all() object")]]
all {
118 using tuple = std::tuple<Futures...>;
119 using value_tuple =
typename internal::value_tuple_for_non_void_futures<Futures...>;
121 template <
size_t idx>
122 struct intermediate_task final : continuation_base_from_future_t<std::tuple_element_t<idx, tuple>> {
124 explicit intermediate_task(awaiter& container) : container(container) {}
125 virtual void run_and_dispose()
noexcept {
126 using value_type =
typename std::tuple_element_t<idx, tuple>::value_type;
127 if (__builtin_expect(this->_state.failed(),
false)) {
131 if constexpr (std::same_as<std::tuple_element_t<idx, tuple>,
future<>>) {
132 std::get<idx>(container.state._futures) = make_ready_future<>();
134 std::get<idx>(container.state._futures) = make_ready_future<value_type>(std::move(this->_state).get());
137 awaiter& c = container;
138 this->~intermediate_task();
139 c.template process<idx+1>();
142 template <
typename IndexSequence>
143 struct generate_aligned_union;
144 template <
size_t... idx>
145 struct generate_aligned_union<
std::integer_sequence<size_t, idx...>> {
146 static constexpr std::size_t alignment_value = std::max({
alignof(intermediate_task<idx>)...});
147 using type = std::byte[std::max({
sizeof(intermediate_task<idx>)...})];
149 using continuation_storage = generate_aligned_union<std::make_index_sequence<std::tuple_size_v<tuple>>>;
150 using coroutine_handle_t = std::coroutine_handle<void>;
156 alignas(continuation_storage::alignment_value)
typename continuation_storage::type _continuation_storage;
157 coroutine_handle_t when_ready;
158 awaiter(
all& state) : state(state) {}
159 bool await_ready()
const {
160 return std::apply([] (
const Futures&... futures) {
161 return (... && futures.available());
164 void await_suspend(coroutine_handle_t h) {
168 value_tuple await_resume() {
169 std::apply([] (Futures&... futures) {
170 std::exception_ptr e;
173 (void)(..., (futures.failed() ? (e = futures.get_exception(), 0) : 0));
175 std::rethrow_exception(std::move(e));
180 return [&] <
size_t... Idx> (std::integer_sequence<size_t, Idx...>) {
181 return value_tuple(std::get<Idx>(state._futures).get()...);
182 } (internal::index_sequence_for_non_void_futures<Futures...>());
184 template <
unsigned idx>
186 if constexpr (idx ==
sizeof...(Futures)) {
189 if (!std::get<idx>(state._futures).available()) {
190 auto task =
new (&_continuation_storage) intermediate_task<idx>(*
this);
191 seastar::internal::set_callback(std::move(std::get<idx>(state._futures)),
task);
199 template <
typename... Func>
200 requires (... && std::invocable<Func>) && (... && future_type<std::invoke_result_t<Func>>)
201 explicit all(Func&&... funcs)
202 : _futures(futurize_invoke(funcs)...) {
204 awaiter
operator co_await() {
return awaiter{*
this}; }
207template <
typename FirstFunc,
typename... MoreFuncs>
209 std::invoke_result_t<MoreFuncs>...>;
A representation of a possibly not-yet-computed value.
Definition: future.hh:1240
future< T > make_exception_future(std::exception_ptr &&value) noexcept
Creates a future in an available, failed state.
Definition: future.hh:1949
Converts a type to a future type, if it isn't already.
Definition: future.hh:1853