C/C++ API Reference
Loading...
Searching...
No Matches
select.h
1// Copyright 2025 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14#pragma once
15
16#include <algorithm>
17#include <tuple>
18#include <utility>
19#include <variant>
20
21#include "pw_async2/dispatcher.h"
22#include "pw_async2/future.h"
23#include "pw_containers/optional_tuple.h"
24
25namespace pw::async2 {
26
28
29template <typename... Pendables>
30class Selector;
31
32namespace experimental {
33
34template <typename... Futures>
35class SelectFuture;
36
37} // namespace experimental
38
42template <size_t I, typename T>
44 static constexpr size_t kIndex = I;
45 T value;
46
47 private:
48 template <typename... Pendables>
49 friend class Selector;
50 template <typename... Futures>
51 friend class experimental::SelectFuture;
52
53 explicit SelectResult(T&& val) : value(std::move(val)) {}
54};
55
58 static constexpr size_t kIndex = std::numeric_limits<size_t>::max();
59 std::nullopt_t value = std::nullopt;
60};
61
63
64namespace internal {
65
66template <typename Is, typename... Pendables>
68
69template <size_t... Is, typename... Pendables>
70struct SelectVariantType<std::index_sequence<Is...>, Pendables...> {
71 using type = std::variant<SelectResult<Is, PendOutputOf<Pendables>>...,
73};
74
75template <typename ResultVariant,
76 typename AllPendablesCompletedHandler,
77 typename ReadyHandlers,
78 std::size_t... Is>
79void VisitSelectResult(ResultVariant&& variant,
80 AllPendablesCompletedHandler&& all_completed,
81 ReadyHandlers&& ready_handlers,
82 std::index_sequence<Is...>) {
83 std::visit(
84 [&](auto&& arg) {
85 using ArgType = std::decay_t<decltype(arg)>;
86 if constexpr (std::is_same_v<ArgType, AllPendablesCompleted>) {
87 std::invoke(std::forward<AllPendablesCompletedHandler>(all_completed),
89 } else {
90 std::invoke(std::get<ArgType::kIndex>(
91 std::forward<ReadyHandlers>(ready_handlers)),
92 std::move(arg.value));
93 }
94 },
95 std::forward<ResultVariant>(variant));
96}
97
98} // namespace internal
99
101
140template <typename... Pendables>
141class Selector {
142 public:
143 static_assert(sizeof...(Pendables) > 0,
144 "Cannot select over an empty set of pendables");
145
146 using ResultVariant = typename internal::SelectVariantType<
147 std::make_index_sequence<sizeof...(Pendables)>,
148 Pendables...>::type;
149
150 public:
152 explicit Selector(Pendables&&... pendables)
153 : pendables_(std::move(pendables)...) {}
154
157 Poll<ResultVariant> Pend(Context& cx) { return PendFrom<0>(cx, false); }
158
159 private:
163 template <size_t kTupleIndex>
164 Poll<ResultVariant> PendFrom(Context& cx, bool has_active_pendable) {
165 if constexpr (kTupleIndex < sizeof...(Pendables)) {
166 auto& pendable = std::get<kTupleIndex>(pendables_);
167
168 if (pendable.completed()) {
169 return PendFrom<kTupleIndex + 1>(cx, has_active_pendable);
170 }
171
172 using value_type =
173 PendOutputOf<std::tuple_element_t<kTupleIndex, decltype(pendables_)>>;
174 Poll<value_type> result = pendable.Pend(cx);
175
176 if (result.IsReady()) {
177 return Ready(ResultVariant(
178 std::in_place_index<kTupleIndex>,
179 SelectResult<kTupleIndex, value_type>(std::move(*result))));
180 }
181 return PendFrom<kTupleIndex + 1>(cx, true);
182 } else {
183 // All pendables have been polled and none were ready.
184
185 if (!has_active_pendable) {
186 return Ready(AllPendablesCompleted{});
187 }
188 return Pending();
189 }
190 }
191
192 std::tuple<Pendables...> pendables_;
193};
194
195template <typename... Pendables>
196Selector(Pendables&&...) -> Selector<Pendables...>;
197
202template <typename... Pendables>
203auto Select(Context& cx, Pendables&&... pendables) {
204 Selector selector(std::forward<Pendables>(pendables)...);
205 return selector.Pend(cx);
206}
207
210template <typename ResultVariant,
211 typename AllPendablesCompletedHandler,
212 typename... ReadyHandler>
214 ResultVariant&& variant,
215 AllPendablesCompletedHandler&& on_all_pendables_completed,
216 ReadyHandler&&... on_ready) {
217 static_assert(std::variant_size_v<std::decay_t<ResultVariant>> - 1 ==
218 sizeof...(ReadyHandler),
219 "Number of handlers must match the number of pendables.");
220 internal::VisitSelectResult(
221 std::forward<ResultVariant>(variant),
222 std::forward<AllPendablesCompletedHandler>(on_all_pendables_completed),
223 std::forward_as_tuple(std::forward<ReadyHandler>(on_ready)...),
224 std::make_index_sequence<sizeof...(ReadyHandler)>{});
225}
226
228
229namespace experimental {
230
231template <typename... Futures>
233 : public Future<SelectFuture<Futures...>,
234 OptionalTuple<typename Futures::value_type...>> {
235 public:
236 static_assert(sizeof...(Futures) > 0,
237 "Cannot select over an empty set of futures");
238
239 using ResultTuple = OptionalTuple<typename Futures::value_type...>;
240 using Base = Future<SelectFuture<Futures...>, ResultTuple>;
241
242 explicit SelectFuture(Futures&&... futures)
243 : futures_(std::move(futures)...) {}
244
245 private:
246 friend Base;
247
248 static constexpr auto kTupleIndexSequence =
249 std::make_index_sequence<sizeof...(Futures)>();
250
251 Poll<ResultTuple> DoPend(Context& cx) {
252 ResultTuple tuple;
253 PendAll(cx, kTupleIndexSequence, tuple);
254 if (!tuple.empty()) {
255 return tuple;
256 }
257 return Pending();
258 }
259
260 void DoMarkComplete() {}
261 bool DoIsComplete() const { return IsComplete<0>(); }
262
263 template <size_t kTupleIndex>
264 bool IsComplete() const {
265 if constexpr (kTupleIndex < sizeof...(Futures)) {
266 auto& future = std::get<kTupleIndex>(futures_);
267 if (future.is_complete()) {
268 return true;
269 }
270 return IsComplete<kTupleIndex + 1>();
271 } else {
272 return false;
273 }
274 }
275
278 template <size_t... Is>
279 void PendAll(Context& cx, std::index_sequence<Is...>, ResultTuple& result) {
280 (PendFuture<Is>(cx, result), ...);
281 }
282
283 template <size_t kTupleIndex>
284 void PendFuture(Context& cx, ResultTuple& result) {
285 auto& future = std::get<kTupleIndex>(futures_);
286 if (future.is_complete()) {
287 return;
288 }
289
290 auto poll = future.Pend(cx);
291 if (poll.IsReady()) {
292 result.template emplace<kTupleIndex>(std::move(*poll));
293 }
294 }
295
296 std::tuple<Futures...> futures_;
297};
298
299template <typename... Futures>
300SelectFuture(Futures&&...) -> SelectFuture<Futures...>;
301
307template <typename... Futures>
308SelectFuture<Futures...> Select(Futures&&... futures) {
309 static_assert(std::conjunction_v<is_future<Futures>...>,
310 "All arguments to Select must be Future types");
311 return SelectFuture(std::forward<Futures>(futures)...);
312}
313
314} // namespace experimental
315
316} // namespace pw::async2
Definition: optional_tuple.h:112
constexpr bool empty() const
Checks if the OptionalTuple contains no active elements.
Definition: optional_tuple.h:180
Definition: context.h:55
Definition: poll.h:60
Definition: select.h:141
Poll< ResultVariant > Pend(Context &cx)
Definition: select.h:157
Definition: future.h:60
void VisitSelectResult(ResultVariant &&variant, AllPendablesCompletedHandler &&on_all_pendables_completed, ReadyHandler &&... on_ready)
Definition: select.h:213
auto Select(Context &cx, Pendables &&... pendables)
Definition: select.h:203
constexpr PendingType Pending()
Returns a value indicating that an operation was not yet able to complete.
Definition: poll.h:271
constexpr Poll Ready()
Returns a value indicating completion.
Definition: poll.h:255
Indicates that every pendable within a Selector has completed.
Definition: select.h:57
Definition: select.h:43