• 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/log/check.h"
24 #include "absl/meta/type_traits.h"
25 #include "absl/status/status.h"
26 #include "absl/status/statusor.h"
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   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static bool IsOk(
54       const absl::StatusOr<T>& x) {
55     return x.ok();
56   }
IsOkTryJoinTraits57   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static bool IsOk(const absl::Status& x) {
58     return x.ok();
59   }
IsOkTryJoinTraits60   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static bool IsOk(StatusFlag x) {
61     return x.ok();
62   }
63   template <typename T>
IsOkTryJoinTraits64   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static bool IsOk(
65       const ValueOrFailure<T>& x) {
66     return x.ok();
67   }
68   template <typename T>
UnwrappedTryJoinTraits69   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static T Unwrapped(absl::StatusOr<T> x) {
70     return std::move(*x);
71   }
72   template <typename T>
UnwrappedTryJoinTraits73   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static T Unwrapped(ValueOrFailure<T> x) {
74     return std::move(*x);
75   }
UnwrappedTryJoinTraits76   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static Empty Unwrapped(absl::Status) {
77     return Empty{};
78   }
UnwrappedTryJoinTraits79   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static Empty Unwrapped(StatusFlag) {
80     return Empty{};
81   }
82   template <typename R, typename T>
EarlyReturnTryJoinTraits83   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static R EarlyReturn(
84       absl::StatusOr<T> x) {
85     return x.status();
86   }
87   template <typename R>
EarlyReturnTryJoinTraits88   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static R EarlyReturn(absl::Status x) {
89     return FailureStatusCast<R>(std::move(x));
90   }
91   template <typename R>
EarlyReturnTryJoinTraits92   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static R EarlyReturn(StatusFlag x) {
93     return FailureStatusCast<R>(x);
94   }
95   template <typename R, typename T>
EarlyReturnTryJoinTraits96   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static R EarlyReturn(
97       const ValueOrFailure<T>& x) {
98     CHECK(!x.ok());
99     return FailureStatusCast<R>(Failure{});
100   }
101   template <typename... A>
FinalReturnTryJoinTraits102   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static auto FinalReturn(A&&... a) {
103     return Result<std::tuple<A...>>(std::make_tuple(std::forward<A>(a)...));
104   }
105 };
106 
107 // Implementation of TryJoin combinator.
108 template <template <typename> class R, typename... Promises>
109 class TryJoin {
110  public:
TryJoin(Promises...promises)111   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION explicit TryJoin(Promises... promises)
112       : state_(std::move(promises)...) {}
operator()113   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION auto operator()() {
114     return state_.PollOnce();
115   }
116 
117  private:
118   JoinState<TryJoinTraits<R>, Promises...> state_;
119 };
120 
121 template <template <typename> class R>
122 struct WrapInStatusOrTuple {
123   template <typename T>
operatorWrapInStatusOrTuple124   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION R<std::tuple<T>> operator()(R<T> x) {
125     if (!x.ok()) return x.status();
126     return std::make_tuple(std::move(*x));
127   }
128 };
129 
130 }  // namespace promise_detail
131 
132 // Run all promises.
133 // If any fail, cancel the rest and return the failure.
134 // If all succeed, return Ok(tuple-of-results).
135 template <template <typename> class R, typename... Promises>
136 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline promise_detail::TryJoin<R,
137                                                                     Promises...>
TryJoin(Promises...promises)138 TryJoin(Promises... promises) {
139   return promise_detail::TryJoin<R, Promises...>(std::move(promises)...);
140 }
141 
142 template <template <typename> class R, typename F>
TryJoin(F promise)143 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline auto TryJoin(F promise) {
144   return Map(promise, promise_detail::WrapInStatusOrTuple<R>{});
145 }
146 
147 }  // namespace grpc_core
148 
149 #endif  // GRPC_SRC_CORE_LIB_PROMISE_TRY_JOIN_H
150