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