• 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_TRY_SEQ_H
16 #define GRPC_SRC_CORE_LIB_PROMISE_TRY_SEQ_H
17 
18 #include <grpc/support/port_platform.h>
19 #include <stdlib.h>
20 
21 #include <type_traits>
22 #include <utility>
23 
24 #include "absl/log/check.h"
25 #include "absl/meta/type_traits.h"
26 #include "absl/status/status.h"
27 #include "absl/status/statusor.h"
28 #include "src/core/lib/promise/detail/basic_seq.h"
29 #include "src/core/lib/promise/detail/promise_like.h"
30 #include "src/core/lib/promise/detail/seq_state.h"
31 #include "src/core/lib/promise/detail/status.h"
32 #include "src/core/lib/promise/poll.h"
33 #include "src/core/lib/promise/status_flag.h"
34 
35 namespace grpc_core {
36 
37 namespace promise_detail {
38 
39 template <typename T, typename Ignored = void>
40 struct TrySeqTraitsWithSfinae {
41   using UnwrappedType = T;
42   using WrappedType = absl::StatusOr<T>;
43   template <typename Next>
CallFactoryTrySeqTraitsWithSfinae44   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static auto CallFactory(Next* next,
45                                                                T&& value) {
46     return next->Make(std::forward<T>(value));
47   }
IsOkTrySeqTraitsWithSfinae48   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static bool IsOk(const T&) {
49     return true;
50   }
ErrorStringTrySeqTraitsWithSfinae51   static const char* ErrorString(const T&) { abort(); }
52   template <typename R>
ReturnValueTrySeqTraitsWithSfinae53   static R ReturnValue(T&&) {
54     abort();
55   }
56   template <typename Result, typename RunNext>
57   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static Poll<Result>
CheckResultAndRunNextTrySeqTraitsWithSfinae58   CheckResultAndRunNext(T prior, RunNext run_next) {
59     return run_next(std::move(prior));
60   }
61 };
62 
63 template <typename T>
64 struct TrySeqTraitsWithSfinae<absl::StatusOr<T>> {
65   using UnwrappedType = T;
66   using WrappedType = absl::StatusOr<T>;
67   template <typename Next>
68   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static auto CallFactory(
69       Next* next, absl::StatusOr<T>&& status) {
70     return next->Make(std::move(*status));
71   }
72   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static bool IsOk(
73       const absl::StatusOr<T>& status) {
74     return status.ok();
75   }
76   static std::string ErrorString(const absl::StatusOr<T>& status) {
77     return status.status().ToString();
78   }
79   template <typename R>
80   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static R ReturnValue(
81       absl::StatusOr<T>&& status) {
82     return FailureStatusCast<R>(status.status());
83   }
84   template <typename Result, typename RunNext>
85   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static Poll<Result>
86   CheckResultAndRunNext(absl::StatusOr<T> prior, RunNext run_next) {
87     if (!prior.ok()) return FailureStatusCast<Result>(prior.status());
88     return run_next(std::move(prior));
89   }
90 };
91 
92 template <typename T>
93 struct AllowGenericTrySeqTraits {
94   static constexpr bool value = true;
95 };
96 
97 template <>
98 struct AllowGenericTrySeqTraits<absl::Status> {
99   static constexpr bool value = false;
100 };
101 
102 template <typename T>
103 struct AllowGenericTrySeqTraits<absl::StatusOr<T>> {
104   static constexpr bool value = false;
105 };
106 
107 template <typename T, typename AnyType = void>
108 struct TakeValueExists {
109   static constexpr bool value = false;
110 };
111 template <typename T>
112 struct TakeValueExists<T,
113                        absl::void_t<decltype(TakeValue(std::declval<T>()))>> {
114   static constexpr bool value = true;
115 };
116 // If there exists a function 'IsStatusOk(const T&) -> bool' then we assume that
117 // T is a status type for the purposes of promise sequences, and a non-OK T
118 // should terminate the sequence and return.
119 template <typename T>
120 struct TrySeqTraitsWithSfinae<
121     T, absl::enable_if_t<
122            std::is_same<decltype(IsStatusOk(std::declval<T>())), bool>::value &&
123                !TakeValueExists<T>::value && AllowGenericTrySeqTraits<T>::value,
124            void>> {
125   using UnwrappedType = void;
126   using WrappedType = T;
127   template <typename Next>
128   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static auto CallFactory(Next* next,
129                                                                T&&) {
130     return next->Make();
131   }
132   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static bool IsOk(const T& status) {
133     return IsStatusOk(status);
134   }
135   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static std::string ErrorString(
136       const T& status) {
137     return IsStatusOk(status) ? "OK" : "FAILED";
138   }
139   template <typename R>
140   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static R ReturnValue(T&& status) {
141     return FailureStatusCast<R>(std::move(status));
142   }
143   template <typename Result, typename RunNext>
144   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static Poll<Result>
145   CheckResultAndRunNext(T prior, RunNext run_next) {
146     if (!IsStatusOk(prior)) return Result(std::move(prior));
147     return run_next(std::move(prior));
148   }
149 };
150 template <typename T>
151 struct TrySeqTraitsWithSfinae<
152     T, absl::enable_if_t<
153            std::is_same<decltype(IsStatusOk(std::declval<T>())), bool>::value &&
154                TakeValueExists<T>::value && AllowGenericTrySeqTraits<T>::value,
155            void>> {
156   using UnwrappedType = decltype(TakeValue(std::declval<T>()));
157   using WrappedType = T;
158   template <typename Next>
159   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static auto CallFactory(Next* next,
160                                                                T&& status) {
161     return next->Make(TakeValue(std::forward<T>(status)));
162   }
163   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static bool IsOk(const T& status) {
164     return IsStatusOk(status);
165   }
166   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static std::string ErrorString(
167       const T& status) {
168     return IsStatusOk(status) ? "OK" : "FAILED";
169   }
170   template <typename R>
171   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static R ReturnValue(T&& status) {
172     DCHECK(!IsStatusOk(status));
173     return FailureStatusCast<R>(status.status());
174   }
175   template <typename Result, typename RunNext>
176   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static Poll<Result>
177   CheckResultAndRunNext(T prior, RunNext run_next) {
178     if (!IsStatusOk(prior)) return Result(std::move(prior));
179     return run_next(std::move(prior));
180   }
181 };
182 template <>
183 struct TrySeqTraitsWithSfinae<absl::Status> {
184   using UnwrappedType = void;
185   using WrappedType = absl::Status;
186   template <typename Next>
187   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static auto CallFactory(Next* next,
188                                                                absl::Status&&) {
189     return next->Make();
190   }
191   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static bool IsOk(
192       const absl::Status& status) {
193     return status.ok();
194   }
195   static std::string ErrorString(const absl::Status& status) {
196     return status.ToString();
197   }
198   template <typename R>
199   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static R ReturnValue(
200       absl::Status&& status) {
201     return FailureStatusCast<R>(std::move(status));
202   }
203   template <typename Result, typename RunNext>
204   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static Poll<Result>
205   CheckResultAndRunNext(absl::Status prior, RunNext run_next) {
206     if (!prior.ok()) return StatusCast<Result>(std::move(prior));
207     return run_next(std::move(prior));
208   }
209 };
210 
211 template <typename T>
212 using TrySeqTraits = TrySeqTraitsWithSfinae<T>;
213 
214 template <typename P, typename... Fs>
215 class TrySeq {
216  public:
217   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION explicit TrySeq(P&& promise,
218                                                        Fs&&... factories,
219                                                        DebugLocation whence)
220       : state_(std::forward<P>(promise), std::forward<Fs>(factories)...,
221                whence) {}
222 
223   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION auto operator()() {
224     return state_.PollOnce();
225   }
226 
227  private:
228   SeqState<TrySeqTraits, P, Fs...> state_;
229 };
230 
231 template <typename Iter, typename Factory, typename Argument>
232 using TrySeqIter = BasicSeqIter<TrySeqTraits, Iter, Factory, Argument>;
233 
234 template <typename Container, typename Factory, typename Argument>
235 struct TrySeqContainerResultTraits {
236   using BaseResult =
237       TrySeqIter<typename Container::iterator, Factory, Argument>;
238   class Result {
239    public:
240     Result(Container container, Factory factory, Argument argument)
241         : container_(std::move(container)),
242           base_result_(container_.begin(), container_.end(), std::move(factory),
243                        std::move(argument)) {}
244     Result(const Result&) = delete;
245     Result& operator=(const Result&) = delete;
246     Result(Result&&) = default;
247     Result& operator=(Result&&) = default;
248 
249     auto operator()() { return base_result_(); }
250 
251    private:
252     Container container_;
253     BaseResult base_result_;
254   };
255 };
256 
257 }  // namespace promise_detail
258 
259 // Try a sequence of operations.
260 // * Run the first functor as a promise.
261 // * Feed its success result into the second functor to create a promise,
262 //   then run that.
263 // * ...
264 // * Feed the second-final success result into the final functor to create a
265 //   promise, then run that, with the overall success result being that
266 //   promises success result.
267 // If any step fails, fail everything.
268 // Functors can return StatusOr<> to signal that a value is fed forward, or
269 // Status to indicate only success/failure. In the case of returning Status,
270 // the construction functors take no arguments.
271 template <typename F>
272 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline F TrySeq(F functor) {
273   return functor;
274 }
275 
276 template <typename F0, typename F1>
277 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline promise_detail::TrySeq<F0, F1>
278 TrySeq(F0 f0, F1 f1, DebugLocation whence = {}) {
279   return promise_detail::TrySeq<F0, F1>(std::move(f0), std::move(f1), whence);
280 }
281 
282 template <typename F0, typename F1, typename F2>
283 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline promise_detail::TrySeq<F0, F1, F2>
284 TrySeq(F0 f0, F1 f1, F2 f2, DebugLocation whence = {}) {
285   return promise_detail::TrySeq<F0, F1, F2>(std::move(f0), std::move(f1),
286                                             std::move(f2), whence);
287 }
288 
289 template <typename F0, typename F1, typename F2, typename F3>
290 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline promise_detail::TrySeq<F0, F1, F2,
291                                                                    F3>
292 TrySeq(F0 f0, F1 f1, F2 f2, F3 f3, DebugLocation whence = {}) {
293   return promise_detail::TrySeq<F0, F1, F2, F3>(
294       std::move(f0), std::move(f1), std::move(f2), std::move(f3), whence);
295 }
296 
297 template <typename F0, typename F1, typename F2, typename F3, typename F4>
298 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline promise_detail::TrySeq<F0, F1, F2,
299                                                                    F3, F4>
300 TrySeq(F0 f0, F1 f1, F2 f2, F3 f3, F4 f4, DebugLocation whence = {}) {
301   return promise_detail::TrySeq<F0, F1, F2, F3, F4>(
302       std::move(f0), std::move(f1), std::move(f2), std::move(f3), std::move(f4),
303       whence);
304 }
305 
306 template <typename F0, typename F1, typename F2, typename F3, typename F4,
307           typename F5>
308 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION
309     promise_detail::TrySeq<F0, F1, F2, F3, F4, F5>
310     TrySeq(F0 f0, F1 f1, F2 f2, F3 f3, F4 f4, F5 f5,
311            DebugLocation whence = {}) {
312   return promise_detail::TrySeq<F0, F1, F2, F3, F4, F5>(
313       std::move(f0), std::move(f1), std::move(f2), std::move(f3), std::move(f4),
314       std::move(f5), whence);
315 }
316 
317 template <typename F0, typename F1, typename F2, typename F3, typename F4,
318           typename F5, typename F6>
319 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION
320     promise_detail::TrySeq<F0, F1, F2, F3, F4, F5, F6>
321     TrySeq(F0 f0, F1 f1, F2 f2, F3 f3, F4 f4, F5 f5, F6 f6,
322            DebugLocation whence = {}) {
323   return promise_detail::TrySeq<F0, F1, F2, F3, F4, F5, F6>(
324       std::move(f0), std::move(f1), std::move(f2), std::move(f3), std::move(f4),
325       std::move(f5), std::move(f6), whence);
326 }
327 
328 template <typename F0, typename F1, typename F2, typename F3, typename F4,
329           typename F5, typename F6, typename F7>
330 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION
331     promise_detail::TrySeq<F0, F1, F2, F3, F4, F5, F6, F7>
332     TrySeq(F0 f0, F1 f1, F2 f2, F3 f3, F4 f4, F5 f5, F6 f6, F7 f7,
333            DebugLocation whence = {}) {
334   return promise_detail::TrySeq<F0, F1, F2, F3, F4, F5, F6, F7>(
335       std::move(f0), std::move(f1), std::move(f2), std::move(f3), std::move(f4),
336       std::move(f5), std::move(f6), std::move(f7), whence);
337 }
338 
339 // Try a sequence of operations of unknown length.
340 // Asynchronously:
341 //   for (element in (begin, end)) {
342 //     auto r = wait_for factory(element, argument);
343 //     if (!r.ok()) return r;
344 //     argument = *r;
345 //   }
346 //   return argument;
347 template <typename Iter, typename Factory, typename Argument>
348 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION auto TrySeqIter(Iter begin, Iter end,
349                                                      Argument argument,
350                                                      Factory factory) {
351   return promise_detail::TrySeqIter<Iter, Factory, Argument>(
352       begin, end, std::move(factory), std::move(argument));
353 }
354 
355 template <typename Container, typename Factory, typename Argument>
356 auto TrySeqContainer(Container container, Argument argument, Factory factory) {
357   using Result =
358       typename promise_detail::TrySeqContainerResultTraits<Container, Factory,
359                                                            Argument>::Result;
360   return Result(std::move(container), std::move(factory), std::move(argument));
361 }
362 
363 }  // namespace grpc_core
364 
365 #endif  // GRPC_SRC_CORE_LIB_PROMISE_TRY_SEQ_H
366