• 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_JOIN_H
16 #define GRPC_SRC_CORE_LIB_PROMISE_TRY_JOIN_H
17 
18 #include <grpc/support/port_platform.h>
19 
20 #include <tuple>
21 #include <variant>
22 
23 #include "absl/meta/type_traits.h"
24 #include "absl/status/status.h"
25 #include "absl/status/statusor.h"
26 
27 #include "src/core/lib/promise/detail/join_state.h"
28 #include "src/core/lib/promise/map.h"
29 #include "src/core/lib/promise/poll.h"
30 #include "src/core/lib/promise/status_flag.h"
31 
32 namespace grpc_core {
33 
34 namespace promise_detail {
35 
36 // Extract the T from a StatusOr<T>
37 template <typename T>
IntoResult(absl::StatusOr<T> * status)38 T IntoResult(absl::StatusOr<T>* status) {
39   return std::move(**status);
40 }
41 
42 // TryJoin returns a StatusOr<tuple<A,B,C>> for f()->Poll<StatusOr<A>>,
43 // g()->Poll<StatusOr<B>>, h()->Poll<StatusOr<C>>. If one of those should be a
44 // Status instead, we need a placeholder type to return, and this is it.
IntoResult(absl::Status *)45 inline Empty IntoResult(absl::Status*) { return Empty{}; }
46 
47 // Traits object to pass to BasicJoin
48 template <template <typename> class Result>
49 struct TryJoinTraits {
50   template <typename T>
51   using ResultType = Result<absl::remove_reference_t<T>>;
52   template <typename T>
IsOkTryJoinTraits53   static bool IsOk(const absl::StatusOr<T>& x) {
54     return x.ok();
55   }
IsOkTryJoinTraits56   static bool IsOk(const absl::Status& x) { return x.ok(); }
IsOkTryJoinTraits57   static bool IsOk(StatusFlag x) { return x.ok(); }
58   template <typename T>
IsOkTryJoinTraits59   static bool IsOk(const ValueOrFailure<T>& x) {
60     return x.ok();
61   }
62   template <typename T>
UnwrappedTryJoinTraits63   static T Unwrapped(absl::StatusOr<T> x) {
64     return std::move(*x);
65   }
66   template <typename T>
UnwrappedTryJoinTraits67   static T Unwrapped(ValueOrFailure<T> x) {
68     return std::move(*x);
69   }
UnwrappedTryJoinTraits70   static Empty Unwrapped(absl::Status) { return Empty{}; }
UnwrappedTryJoinTraits71   static Empty Unwrapped(StatusFlag) { return Empty{}; }
72   template <typename R, typename T>
EarlyReturnTryJoinTraits73   static R EarlyReturn(absl::StatusOr<T> x) {
74     return x.status();
75   }
76   template <typename R>
EarlyReturnTryJoinTraits77   static R EarlyReturn(absl::Status x) {
78     return FailureStatusCast<R>(std::move(x));
79   }
80   template <typename R>
EarlyReturnTryJoinTraits81   static R EarlyReturn(StatusFlag x) {
82     return FailureStatusCast<R>(x);
83   }
84   template <typename R, typename T>
EarlyReturnTryJoinTraits85   static R EarlyReturn(const ValueOrFailure<T>& x) {
86     GPR_ASSERT(!x.ok());
87     return FailureStatusCast<R>(Failure{});
88   }
89   template <typename... A>
FinalReturnTryJoinTraits90   static auto FinalReturn(A&&... a) {
91     return Result<std::tuple<A...>>(std::make_tuple(std::forward<A>(a)...));
92   }
93 };
94 
95 // Implementation of TryJoin combinator.
96 template <template <typename> class R, typename... Promises>
97 class TryJoin {
98  public:
TryJoin(Promises...promises)99   explicit TryJoin(Promises... promises) : state_(std::move(promises)...) {}
operator()100   auto operator()() { return state_.PollOnce(); }
101 
102  private:
103   JoinState<TryJoinTraits<R>, Promises...> state_;
104 };
105 
106 template <template <typename> class R>
107 struct WrapInStatusOrTuple {
108   template <typename T>
operatorWrapInStatusOrTuple109   R<std::tuple<T>> operator()(R<T> x) {
110     if (!x.ok()) return x.status();
111     return std::make_tuple(std::move(*x));
112   }
113 };
114 
115 }  // namespace promise_detail
116 
117 // Run all promises.
118 // If any fail, cancel the rest and return the failure.
119 // If all succeed, return Ok(tuple-of-results).
120 template <template <typename> class R, typename... Promises>
TryJoin(Promises...promises)121 promise_detail::TryJoin<R, Promises...> TryJoin(Promises... promises) {
122   return promise_detail::TryJoin<R, Promises...>(std::move(promises)...);
123 }
124 
125 template <template <typename> class R, typename F>
TryJoin(F promise)126 auto TryJoin(F promise) {
127   return Map(promise, promise_detail::WrapInStatusOrTuple<R>{});
128 }
129 
130 }  // namespace grpc_core
131 
132 #endif  // GRPC_SRC_CORE_LIB_PROMISE_TRY_JOIN_H
133