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