xref: /aosp_15_r20/external/pigweed/pw_async2/public/pw_async2/join.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2024 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 "pw_async2/dispatcher.h"
17 
18 namespace pw::async2 {
19 
20 /// A pendable value which joins together several separate pendable values.
21 ///
22 /// It will only return ``Ready`` once all of the individual pendables have
23 /// returned ``Ready``. The resulting ``Ready`` value contains a tuple of
24 /// the results of joined pendable values.
25 template <typename... Pendables>
26 class Join {
27  private:
28   static constexpr auto kTupleIndexSequence =
29       std::make_index_sequence<sizeof...(Pendables)>();
30   using TupleOfOutputRvalues = std::tuple<PendOutputOf<Pendables>&&...>;
31 
32  public:
33   /// Creates a ``Join`` from a series of pendable values.
Join(Pendables &&...pendables)34   explicit Join(Pendables&&... pendables)
35       : pendables_(std::move(pendables)...),
36         outputs_(Poll<PendOutputOf<Pendables>>(Pending())...) {}
37 
38   /// Attempts to complete all of the pendables, returning ``Ready``
39   /// with their results if all are complete.
Pend(Context & cx)40   Poll<TupleOfOutputRvalues> Pend(Context& cx) {
41     if (!PendElements(cx, kTupleIndexSequence)) {
42       return Pending();
43     }
44     return TakeOutputs(kTupleIndexSequence);
45   }
46 
47  private:
48   /// Pends all non-completed sub-pendables at indices ``Is...`.
49   ///
50   /// Returns whether or not all sub-pendables have completed.
51   template <size_t... Is>
PendElements(Context & cx,std::index_sequence<Is...>)52   bool PendElements(Context& cx, std::index_sequence<Is...>) {
53     return (... && PendElement<Is>(cx));
54   }
55 
56   /// Takes the results of all sub-pendables at indices ``Is...`.
57   template <size_t... Is>
TakeOutputs(std::index_sequence<Is...>)58   Poll<TupleOfOutputRvalues> TakeOutputs(std::index_sequence<Is...>) {
59     return Poll<TupleOfOutputRvalues>(
60         std::forward_as_tuple<PendOutputOf<Pendables>...>(TakeOutput<Is>()...));
61   }
62 
63   /// For pendable at `TupleIndex`, if it has not already returned
64   /// a ``Ready`` result, attempts to complete it and store the result in
65   /// ``outputs_``.
66   ///
67   /// Returns whether the sub-pendable has completed.
68   template <size_t kTupleIndex>
PendElement(Context & cx)69   bool PendElement(Context& cx) {
70     auto& output = std::get<kTupleIndex>(outputs_);
71     if (output.IsReady()) {
72       return true;
73     }
74     output = std::get<kTupleIndex>(pendables_).Pend(cx);
75     return output.IsReady();
76   }
77 
78   /// Takes the result of the sub-pendable at index ``kTupleIndex``.
79   template <size_t kTupleIndex>
80   PendOutputOf<typename std::tuple_element<kTupleIndex,
81                                            std::tuple<Pendables...>>::type>&&
TakeOutput()82   TakeOutput() {
83     return std::move(std::get<kTupleIndex>(outputs_).value());
84   }
85 
86   std::tuple<Pendables...> pendables_;
87   std::tuple<Poll<PendOutputOf<Pendables>>...> outputs_;
88 };
89 
90 }  // namespace pw::async2
91