Pigweed
C/C++ API Reference
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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
25template <typename... Pendables>
26class Selector;
27
31template <size_t I, typename T>
33 static constexpr size_t kIndex = I;
34 T value;
35
36 private:
37 template <typename... Pendables>
38 friend class Selector;
39
40 explicit SelectResult(T&& val) : value(std::move(val)) {}
41};
42
45 static constexpr size_t kIndex = std::numeric_limits<size_t>::max();
46 std::nullopt_t value = std::nullopt;
47};
48
49namespace internal {
50
51template <typename Is, typename... Pendables>
53
54template <size_t... Is, typename... Pendables>
55struct SelectVariantType<std::index_sequence<Is...>, Pendables...> {
56 using type = std::variant<SelectResult<Is, PendOutputOf<Pendables>>...,
58};
59
60template <typename ResultVariant,
61 typename AllPendablesCompletedHandler,
62 typename ReadyHandlers,
63 std::size_t... Is>
64void VisitSelectResult(ResultVariant&& variant,
65 AllPendablesCompletedHandler&& all_completed,
66 ReadyHandlers&& ready_handlers,
67 std::index_sequence<Is...>) {
68 std::visit(
69 [&](auto&& arg) {
70 using ArgType = std::decay_t<decltype(arg)>;
71 if constexpr (std::is_same_v<ArgType, AllPendablesCompleted>) {
72 std::invoke(std::forward<AllPendablesCompletedHandler>(all_completed),
74 } else {
75 std::invoke(std::get<ArgType::kIndex>(
76 std::forward<ReadyHandlers>(ready_handlers)),
77 std::move(arg.value));
78 }
79 },
80 std::forward<ResultVariant>(variant));
81}
82
83} // namespace internal
84
123template <typename... Pendables>
124class Selector {
125 public:
126 static_assert(sizeof...(Pendables) > 0,
127 "Cannot select over an empty set of pendables");
128
129 using ResultVariant = typename internal::SelectVariantType<
130 std::make_index_sequence<sizeof...(Pendables)>,
131 Pendables...>::type;
132
133 public:
135 explicit Selector(Pendables&&... pendables)
136 : pendables_(std::move(pendables)...) {}
137
140 Poll<ResultVariant> Pend(Context& cx) { return PendFrom<0>(cx, false); }
141
142 private:
146 template <size_t kTupleIndex>
147 Poll<ResultVariant> PendFrom(Context& cx, bool has_active_pendable) {
148 if constexpr (kTupleIndex < sizeof...(Pendables)) {
149 auto& pendable = std::get<kTupleIndex>(pendables_);
150
151 if (pendable.completed()) {
152 return PendFrom<kTupleIndex + 1>(cx, has_active_pendable);
153 }
154
155 using OutputType =
156 PendOutputOf<std::tuple_element_t<kTupleIndex, decltype(pendables_)>>;
157 Poll<OutputType> result = pendable.Pend(cx);
158
159 if (result.IsReady()) {
160 return Ready(ResultVariant(
161 std::in_place_index<kTupleIndex>,
162 SelectResult<kTupleIndex, OutputType>(std::move(*result))));
163 }
164 return PendFrom<kTupleIndex + 1>(cx, true);
165 } else {
166 // All pendables have been polled and none were ready.
167
168 if (!has_active_pendable) {
169 return Ready(AllPendablesCompleted{});
170 }
171 return Pending();
172 }
173 }
174
175 std::tuple<Pendables...> pendables_;
176};
177
178template <typename... Pendables>
179Selector(Pendables&&...) -> Selector<Pendables...>;
180
185template <typename... Pendables>
186auto Select(Context& cx, Pendables&&... pendables) {
187 Selector selector(std::forward<Pendables>(pendables)...);
188 return selector.Pend(cx);
189}
190
193template <typename ResultVariant,
194 typename AllPendablesCompletedHandler,
195 typename... ReadyHandler>
196void VisitSelectResult(
197 ResultVariant&& variant,
198 AllPendablesCompletedHandler&& on_all_pendables_completed,
199 ReadyHandler&&... on_ready) {
200 static_assert(std::variant_size_v<std::decay_t<ResultVariant>> - 1 ==
201 sizeof...(ReadyHandler),
202 "Number of handlers must match the number of pendables.");
203 internal::VisitSelectResult(
204 std::forward<ResultVariant>(variant),
205 std::forward<AllPendablesCompletedHandler>(on_all_pendables_completed),
206 std::forward_as_tuple(std::forward<ReadyHandler>(on_ready)...),
207 std::make_index_sequence<sizeof...(ReadyHandler)>{});
208}
209
210} // namespace pw::async2
Definition: context.h:49
Definition: poll.h:54
Definition: select.h:124
Poll< ResultVariant > Pend(Context &cx)
Definition: select.h:140
Indicates that every pendable within a Selector has completed.
Definition: select.h:44
Definition: select.h:32