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_async2/poll.h"
24#include "pw_containers/optional_tuple.h"
25
26namespace pw::async2 {
27
29
30template <typename... Pendables>
31class Selector;
32
33template <typename... Futures>
34class SelectFuture;
35
39template <size_t I, typename T>
41 static constexpr size_t kIndex = I;
42 T value;
43
44 private:
45 template <typename... Pendables>
46 friend class Selector;
47 template <typename... Futures>
48 friend class SelectFuture;
49
50 explicit SelectResult(T&& val) : value(std::move(val)) {}
51};
52
55 static constexpr size_t kIndex = std::numeric_limits<size_t>::max();
56 std::nullopt_t value = std::nullopt;
57};
58
60
61namespace internal {
62
63template <typename Is, typename... Pendables>
65
66template <size_t... Is, typename... Pendables>
67struct SelectVariantType<std::index_sequence<Is...>, Pendables...> {
68 using type = std::variant<SelectResult<Is, PendOutputOf<Pendables>>...,
70};
71
72template <typename ResultVariant,
73 typename AllPendablesCompletedHandler,
74 typename ReadyHandlers,
75 std::size_t... Is>
76void VisitSelectResult(ResultVariant&& variant,
77 AllPendablesCompletedHandler&& all_completed,
78 ReadyHandlers&& ready_handlers,
79 std::index_sequence<Is...>) {
80 std::visit(
81 [&](auto&& arg) {
82 using ArgType = std::decay_t<decltype(arg)>;
83 if constexpr (std::is_same_v<ArgType, AllPendablesCompleted>) {
84 std::invoke(std::forward<AllPendablesCompletedHandler>(all_completed),
86 } else {
87 std::invoke(std::get<ArgType::kIndex>(
88 std::forward<ReadyHandlers>(ready_handlers)),
89 std::move(arg.value));
90 }
91 },
92 std::forward<ResultVariant>(variant));
93}
94
95} // namespace internal
96
98
137template <typename... Pendables>
138class [[deprecated("Use future-based Select instead")]] Selector {
139 public:
140 static_assert(sizeof...(Pendables) > 0,
141 "Cannot select over an empty set of pendables");
142
143 using ResultVariant = typename internal::SelectVariantType<
144 std::make_index_sequence<sizeof...(Pendables)>,
145 Pendables...>::type;
146
147 public:
149 explicit Selector(Pendables&&... pendables)
150 : pendables_(std::move(pendables)...) {}
151
154 Poll<ResultVariant> Pend(Context& cx) { return PendFrom<0>(cx, false); }
155
156 private:
160 template <size_t kTupleIndex>
161 Poll<ResultVariant> PendFrom(Context& cx, bool has_active_pendable) {
162 if constexpr (kTupleIndex < sizeof...(Pendables)) {
163 auto& pendable = std::get<kTupleIndex>(pendables_);
164
165 if (pendable.completed()) {
166 return PendFrom<kTupleIndex + 1>(cx, has_active_pendable);
167 }
168
169 using value_type = internal::PendOutputOf<
170 std::tuple_element_t<kTupleIndex, decltype(pendables_)>>;
171 Poll<value_type> result = pendable.Pend(cx);
172
173 if (result.IsReady()) {
174 return Ready(ResultVariant(
175 std::in_place_index<kTupleIndex>,
176 SelectResult<kTupleIndex, value_type>(std::move(*result))));
177 }
178 return PendFrom<kTupleIndex + 1>(cx, true);
179 } else {
180 // All pendables have been polled and none were ready.
181
182 if (!has_active_pendable) {
183 return Ready(AllPendablesCompleted{});
184 }
185 return Pending();
186 }
187 }
188
189 std::tuple<Pendables...> pendables_;
190};
191
192template <typename... Pendables>
193Selector(Pendables&&...) -> Selector<Pendables...>;
194
199template <typename... Pendables>
200[[deprecated("Use future-based Select instead")]]
201auto SelectPendable(Context& cx, Pendables&&... pendables) {
202 Selector selector(std::forward<Pendables>(pendables)...);
203 return selector.Pend(cx);
204}
205
208template <typename ResultVariant,
209 typename AllPendablesCompletedHandler,
210 typename... ReadyHandler>
212 ResultVariant&& variant,
213 AllPendablesCompletedHandler&& on_all_pendables_completed,
214 ReadyHandler&&... on_ready) {
215 static_assert(std::variant_size_v<std::decay_t<ResultVariant>> - 1 ==
216 sizeof...(ReadyHandler),
217 "Number of handlers must match the number of pendables.");
218 internal::VisitSelectResult(
219 std::forward<ResultVariant>(variant),
220 std::forward<AllPendablesCompletedHandler>(on_all_pendables_completed),
221 std::forward_as_tuple(std::forward<ReadyHandler>(on_ready)...),
222 std::make_index_sequence<sizeof...(ReadyHandler)>{});
223}
224template <typename... Futures>
226 : public Future<SelectFuture<Futures...>,
227 OptionalTuple<typename Futures::value_type...>> {
228 public:
229 static_assert(sizeof...(Futures) > 0,
230 "Cannot select over an empty set of futures");
231
232 using ResultTuple = OptionalTuple<typename Futures::value_type...>;
233 using Base = Future<SelectFuture<Futures...>, ResultTuple>;
234
235 explicit SelectFuture(Futures&&... futures)
236 : futures_(std::move(futures)...) {}
237
238 private:
239 friend Base;
240
241 static constexpr auto kTupleIndexSequence =
242 std::make_index_sequence<sizeof...(Futures)>();
243
244 Poll<ResultTuple> DoPend(Context& cx) {
245 ResultTuple tuple;
246 PendAll(cx, kTupleIndexSequence, tuple);
247 if (!tuple.empty()) {
248 return tuple;
249 }
250 return Pending();
251 }
252
253 void DoMarkComplete() {}
254 bool DoIsComplete() const { return IsComplete<0>(); }
255
256 template <size_t kTupleIndex>
257 bool IsComplete() const {
258 if constexpr (kTupleIndex < sizeof...(Futures)) {
259 auto& future = std::get<kTupleIndex>(futures_);
260 if (future.is_complete()) {
261 return true;
262 }
263 return IsComplete<kTupleIndex + 1>();
264 } else {
265 return false;
266 }
267 }
268
271 template <size_t... Is>
272 void PendAll(Context& cx, std::index_sequence<Is...>, ResultTuple& result) {
273 (PendFuture<Is>(cx, result), ...);
274 }
275
276 template <size_t kTupleIndex>
277 void PendFuture(Context& cx, ResultTuple& result) {
278 auto& future = std::get<kTupleIndex>(futures_);
279 if (future.is_complete()) {
280 return;
281 }
282
283 auto poll = future.Pend(cx);
284 if (poll.IsReady()) {
285 result.template emplace<kTupleIndex>(std::move(*poll));
286 }
287 }
288
289 std::tuple<Futures...> futures_;
290};
291
292template <typename... Futures>
293SelectFuture(Futures&&...) -> SelectFuture<Futures...>;
294
300template <typename... Futures>
301SelectFuture<Futures...> Select(Futures&&... futures) {
302 static_assert(std::conjunction_v<is_future<Futures>...>,
303 "All arguments to Select must be Future types");
304 return SelectFuture(std::forward<Futures>(futures)...);
305}
306
308
309} // 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:54
Definition: future.h:62
Definition: poll.h:60
Definition: select.h:227
Definition: select.h:138
Poll< ResultVariant > Pend(Context &cx)
Definition: select.h:154
void VisitSelectResult(ResultVariant &&variant, AllPendablesCompletedHandler &&on_all_pendables_completed, ReadyHandler &&... on_ready)
Definition: select.h:211
SelectFuture< Futures... > Select(Futures &&... futures)
Definition: select.h:301
auto SelectPendable(Context &cx, Pendables &&... pendables)
Definition: select.h:201
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:54
Definition: select.h:40