1 // Copyright 2021 gRPC authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://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, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #ifndef GRPC_SRC_CORE_LIB_PROMISE_LOOP_H 16 #define GRPC_SRC_CORE_LIB_PROMISE_LOOP_H 17 18 #include <grpc/support/port_platform.h> 19 20 #include <utility> 21 22 #include "absl/status/status.h" 23 #include "absl/status/statusor.h" 24 #include "absl/types/variant.h" 25 26 #include "src/core/lib/gprpp/construct_destruct.h" 27 #include "src/core/lib/promise/detail/promise_factory.h" 28 #include "src/core/lib/promise/poll.h" 29 30 namespace grpc_core { 31 32 // Special type - signals to loop to take another iteration, instead of 33 // finishing 34 struct Continue {}; 35 36 // Result of polling a loop promise - either Continue looping, or return a value 37 // T 38 template <typename T> 39 using LoopCtl = absl::variant<Continue, T>; 40 41 namespace promise_detail { 42 43 template <typename T> 44 struct LoopTraits; 45 46 template <typename T> 47 struct LoopTraits<LoopCtl<T>> { 48 using Result = T; 49 static LoopCtl<T> ToLoopCtl(LoopCtl<T> value) { return value; } 50 }; 51 52 template <typename T> 53 struct LoopTraits<absl::StatusOr<LoopCtl<T>>> { 54 using Result = absl::StatusOr<T>; 55 static LoopCtl<Result> ToLoopCtl(absl::StatusOr<LoopCtl<T>> value) { 56 if (!value.ok()) return value.status(); 57 auto& inner = *value; 58 if (absl::holds_alternative<Continue>(inner)) return Continue{}; 59 return absl::get<T>(std::move(inner)); 60 } 61 }; 62 63 template <> 64 struct LoopTraits<absl::StatusOr<LoopCtl<absl::Status>>> { 65 using Result = absl::Status; 66 static LoopCtl<Result> ToLoopCtl( 67 absl::StatusOr<LoopCtl<absl::Status>> value) { 68 if (!value.ok()) return value.status(); 69 const auto& inner = *value; 70 if (absl::holds_alternative<Continue>(inner)) return Continue{}; 71 return absl::get<absl::Status>(inner); 72 } 73 }; 74 75 template <typename F> 76 class Loop { 77 private: 78 using Factory = promise_detail::RepeatedPromiseFactory<void, F>; 79 using PromiseType = decltype(std::declval<Factory>().Make()); 80 using PromiseResult = typename PromiseType::Result; 81 82 public: 83 using Result = typename LoopTraits<PromiseResult>::Result; 84 85 explicit Loop(F f) : factory_(std::move(f)) {} 86 ~Loop() { 87 if (started_) Destruct(&promise_); 88 } 89 90 Loop(Loop&& loop) noexcept 91 : factory_(std::move(loop.factory_)), started_(loop.started_) { 92 if (started_) Construct(&promise_, std::move(loop.promise_)); 93 } 94 95 Loop(const Loop& loop) = delete; 96 Loop& operator=(const Loop& loop) = delete; 97 98 Poll<Result> operator()() { 99 if (!started_) { 100 started_ = true; 101 Construct(&promise_, factory_.Make()); 102 } 103 while (true) { 104 // Poll the inner promise. 105 auto promise_result = promise_(); 106 // If it returns a value: 107 if (auto* p = promise_result.value_if_ready()) { 108 // - then if it's Continue, destroy the promise and recreate a new one 109 // from our factory. 110 auto lc = LoopTraits<PromiseResult>::ToLoopCtl(std::move(*p)); 111 if (absl::holds_alternative<Continue>(lc)) { 112 Destruct(&promise_); 113 Construct(&promise_, factory_.Make()); 114 continue; 115 } 116 // - otherwise there's our result... return it out. 117 return absl::get<Result>(std::move(lc)); 118 } else { 119 // Otherwise the inner promise was pending, so we are pending. 120 return Pending(); 121 } 122 } 123 } 124 125 private: 126 GPR_NO_UNIQUE_ADDRESS Factory factory_; 127 GPR_NO_UNIQUE_ADDRESS union { 128 GPR_NO_UNIQUE_ADDRESS PromiseType promise_; 129 }; 130 bool started_ = false; 131 }; 132 133 } // namespace promise_detail 134 135 // Looping combinator. 136 // Expects F returns LoopCtl<T> - if it's Continue, then run the loop again - 137 // otherwise yield the returned value as the result of the loop. 138 template <typename F> 139 promise_detail::Loop<F> Loop(F f) { 140 return promise_detail::Loop<F>(std::move(f)); 141 } 142 143 } // namespace grpc_core 144 145 #endif // GRPC_SRC_CORE_LIB_PROMISE_LOOP_H 146