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
23namespace pw::async2 {
24
26
27template <typename... Pendables>
28class Selector;
29
33template <size_t I, typename T>
35 static constexpr size_t kIndex = I;
36 T value;
37
38 private:
39 template <typename... Pendables>
40 friend class Selector;
41
42 explicit SelectResult(T&& val) : value(std::move(val)) {}
43};
44
47 static constexpr size_t kIndex = std::numeric_limits<size_t>::max();
48 std::nullopt_t value = std::nullopt;
49};
50
52
53namespace internal {
54
55template <typename Is, typename... Pendables>
57
58template <size_t... Is, typename... Pendables>
59struct SelectVariantType<std::index_sequence<Is...>, Pendables...> {
60 using type = std::variant<SelectResult<Is, PendOutputOf<Pendables>>...,
62};
63
64template <typename ResultVariant,
65 typename AllPendablesCompletedHandler,
66 typename ReadyHandlers,
67 std::size_t... Is>
68void VisitSelectResult(ResultVariant&& variant,
69 AllPendablesCompletedHandler&& all_completed,
70 ReadyHandlers&& ready_handlers,
71 std::index_sequence<Is...>) {
72 std::visit(
73 [&](auto&& arg) {
74 using ArgType = std::decay_t<decltype(arg)>;
75 if constexpr (std::is_same_v<ArgType, AllPendablesCompleted>) {
76 std::invoke(std::forward<AllPendablesCompletedHandler>(all_completed),
78 } else {
79 std::invoke(std::get<ArgType::kIndex>(
80 std::forward<ReadyHandlers>(ready_handlers)),
81 std::move(arg.value));
82 }
83 },
84 std::forward<ResultVariant>(variant));
85}
86
87} // namespace internal
88
90
129template <typename... Pendables>
130class Selector {
131 public:
132 static_assert(sizeof...(Pendables) > 0,
133 "Cannot select over an empty set of pendables");
134
135 using ResultVariant = typename internal::SelectVariantType<
136 std::make_index_sequence<sizeof...(Pendables)>,
137 Pendables...>::type;
138
139 public:
141 explicit Selector(Pendables&&... pendables)
142 : pendables_(std::move(pendables)...) {}
143
146 Poll<ResultVariant> Pend(Context& cx) { return PendFrom<0>(cx, false); }
147
148 private:
152 template <size_t kTupleIndex>
153 Poll<ResultVariant> PendFrom(Context& cx, bool has_active_pendable) {
154 if constexpr (kTupleIndex < sizeof...(Pendables)) {
155 auto& pendable = std::get<kTupleIndex>(pendables_);
156
157 if (pendable.completed()) {
158 return PendFrom<kTupleIndex + 1>(cx, has_active_pendable);
159 }
160
161 using value_type =
162 PendOutputOf<std::tuple_element_t<kTupleIndex, decltype(pendables_)>>;
163 Poll<value_type> result = pendable.Pend(cx);
164
165 if (result.IsReady()) {
166 return Ready(ResultVariant(
167 std::in_place_index<kTupleIndex>,
168 SelectResult<kTupleIndex, value_type>(std::move(*result))));
169 }
170 return PendFrom<kTupleIndex + 1>(cx, true);
171 } else {
172 // All pendables have been polled and none were ready.
173
174 if (!has_active_pendable) {
175 return Ready(AllPendablesCompleted{});
176 }
177 return Pending();
178 }
179 }
180
181 std::tuple<Pendables...> pendables_;
182};
183
184template <typename... Pendables>
185Selector(Pendables&&...) -> Selector<Pendables...>;
186
191template <typename... Pendables>
192auto Select(Context& cx, Pendables&&... pendables) {
193 Selector selector(std::forward<Pendables>(pendables)...);
194 return selector.Pend(cx);
195}
196
199template <typename ResultVariant,
200 typename AllPendablesCompletedHandler,
201 typename... ReadyHandler>
203 ResultVariant&& variant,
204 AllPendablesCompletedHandler&& on_all_pendables_completed,
205 ReadyHandler&&... on_ready) {
206 static_assert(std::variant_size_v<std::decay_t<ResultVariant>> - 1 ==
207 sizeof...(ReadyHandler),
208 "Number of handlers must match the number of pendables.");
209 internal::VisitSelectResult(
210 std::forward<ResultVariant>(variant),
211 std::forward<AllPendablesCompletedHandler>(on_all_pendables_completed),
212 std::forward_as_tuple(std::forward<ReadyHandler>(on_ready)...),
213 std::make_index_sequence<sizeof...(ReadyHandler)>{});
214}
215
217
218} // namespace pw::async2
Definition: context.h:55
Definition: poll.h:60
Definition: select.h:130
Poll< ResultVariant > Pend(Context &cx)
Definition: select.h:146
void VisitSelectResult(ResultVariant &&variant, AllPendablesCompletedHandler &&on_all_pendables_completed, ReadyHandler &&... on_ready)
Definition: select.h:202
auto Select(Context &cx, Pendables &&... pendables)
Definition: select.h:192
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:46
Definition: select.h:34