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_ALL_OK_H
16 #define GRPC_SRC_CORE_LIB_PROMISE_ALL_OK_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 #include "src/core/lib/promise/detail/join_state.h"
27 #include "src/core/lib/promise/detail/promise_factory.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 // Traits object to pass to JoinState
37 template <typename Result>
38 struct AllOkTraits {
39 template <typename T>
40 using ResultType = Result;
41 template <typename T>
IsOkAllOkTraits42 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static bool IsOk(const T& x) {
43 return IsStatusOk(x);
44 }
UnwrappedAllOkTraits45 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static Empty Unwrapped(StatusFlag) {
46 return Empty{};
47 }
UnwrappedAllOkTraits48 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static Empty Unwrapped(absl::Status) {
49 return Empty{};
50 }
51 template <typename R, typename T>
EarlyReturnAllOkTraits52 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static R EarlyReturn(T&& x) {
53 return StatusCast<R>(std::forward<T>(x));
54 }
55 template <typename... A>
FinalReturnAllOkTraits56 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static Result FinalReturn(A&&...) {
57 return Result{};
58 }
59 };
60
61 // Implementation of AllOk combinator.
62 template <typename Result, typename... Promises>
63 class AllOk {
64 public:
AllOk(Promises...promises)65 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION explicit AllOk(Promises... promises)
66 : state_(std::move(promises)...) {}
operator()67 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION auto operator()() {
68 return state_.PollOnce();
69 }
70
71 private:
72 JoinState<AllOkTraits<Result>, Promises...> state_;
73 };
74
75 } // namespace promise_detail
76
77 // Run all promises.
78 // If any fail, cancel the rest and return the failure.
79 // If all succeed, return Ok.
80 template <typename Result, typename... Promises>
AllOk(Promises...promises)81 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline auto AllOk(Promises... promises) {
82 return promise_detail::AllOk<Result, Promises...>(std::move(promises)...);
83 }
84
85 // Construct a promise for each element of the set, then run them all.
86 // If any fail, cancel the rest and return the failure.
87 // If all succeed, return Ok.
88 template <typename Result, typename Iter, typename FactoryFn>
AllOkIter(Iter begin,Iter end,FactoryFn factory_fn)89 inline auto AllOkIter(Iter begin, Iter end, FactoryFn factory_fn) {
90 using Factory =
91 promise_detail::RepeatedPromiseFactory<decltype(*begin), FactoryFn>;
92 Factory factory(std::move(factory_fn));
93 using Promise = typename Factory::Promise;
94 std::vector<Promise> promises;
95 std::vector<bool> done;
96 for (auto it = begin; it != end; ++it) {
97 promises.emplace_back(factory.Make(*it));
98 done.push_back(false);
99 }
100 return [promises = std::move(promises),
101 done = std::move(done)]() mutable -> Poll<Result> {
102 using Traits = promise_detail::AllOkTraits<Result>;
103 bool still_working = false;
104 for (size_t i = 0; i < promises.size(); ++i) {
105 if (done[i]) continue;
106 auto p = promises[i]();
107 if (auto* r = p.value_if_ready()) {
108 if (!Traits::IsOk(*r)) {
109 return Traits::template EarlyReturn<Result>(std::move(*r));
110 }
111 done[i] = true;
112 } else {
113 still_working = true;
114 }
115 }
116 if (still_working) return Pending{};
117 return Traits::FinalReturn();
118 };
119 }
120
121 } // namespace grpc_core
122
123 #endif // GRPC_SRC_CORE_LIB_PROMISE_ALL_OK_H
124