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