• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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