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