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_SEQ_H 16 #define GRPC_SRC_CORE_LIB_PROMISE_TRY_SEQ_H 17 18 #include <grpc/support/port_platform.h> 19 20 #include <stdlib.h> 21 22 #include <type_traits> 23 #include <utility> 24 25 #include "absl/meta/type_traits.h" 26 #include "absl/status/status.h" 27 #include "absl/status/statusor.h" 28 29 #include "src/core/lib/promise/detail/basic_seq.h" 30 #include "src/core/lib/promise/detail/promise_like.h" 31 #include "src/core/lib/promise/detail/seq_state.h" 32 #include "src/core/lib/promise/detail/status.h" 33 #include "src/core/lib/promise/poll.h" 34 #include "src/core/lib/promise/status_flag.h" 35 36 namespace grpc_core { 37 38 namespace promise_detail { 39 40 template <typename T, typename Ignored = void> 41 struct TrySeqTraitsWithSfinae { 42 using UnwrappedType = T; 43 using WrappedType = absl::StatusOr<T>; 44 template <typename Next> CallFactoryTrySeqTraitsWithSfinae45 static auto CallFactory(Next* next, T&& value) { 46 return next->Make(std::forward<T>(value)); 47 } IsOkTrySeqTraitsWithSfinae48 static bool IsOk(const T&) { return true; } ErrorStringTrySeqTraitsWithSfinae49 static const char* ErrorString(const T&) { abort(); } 50 template <typename R> ReturnValueTrySeqTraitsWithSfinae51 static R ReturnValue(T&&) { 52 abort(); 53 } 54 template <typename F, typename Elem> 55 static auto CallSeqFactory(F& f, Elem&& elem, T&& value) 56 -> decltype(f(std::forward<Elem>(elem), std::forward<T>(value))) { 57 return f(std::forward<Elem>(elem), std::forward<T>(value)); 58 } 59 template <typename Result, typename RunNext> CheckResultAndRunNextTrySeqTraitsWithSfinae60 static Poll<Result> CheckResultAndRunNext(T prior, RunNext run_next) { 61 return run_next(std::move(prior)); 62 } 63 }; 64 65 template <typename T> 66 struct TrySeqTraitsWithSfinae<absl::StatusOr<T>> { 67 using UnwrappedType = T; 68 using WrappedType = absl::StatusOr<T>; 69 template <typename Next> 70 static auto CallFactory(Next* next, absl::StatusOr<T>&& status) { 71 return next->Make(std::move(*status)); 72 } 73 static bool IsOk(const absl::StatusOr<T>& status) { return status.ok(); } 74 static std::string ErrorString(const absl::StatusOr<T>& status) { 75 return status.status().ToString(); 76 } 77 template <typename R> 78 static R ReturnValue(absl::StatusOr<T>&& status) { 79 return FailureStatusCast<R>(status.status()); 80 } 81 template <typename F, typename Elem> 82 static auto CallSeqFactory(F& f, Elem&& elem, absl::StatusOr<T> value) 83 -> decltype(f(std::forward<Elem>(elem), std::move(*value))) { 84 return f(std::forward<Elem>(elem), std::move(*value)); 85 } 86 template <typename Result, typename RunNext> 87 static Poll<Result> CheckResultAndRunNext(absl::StatusOr<T> prior, 88 RunNext run_next) { 89 if (!prior.ok()) return FailureStatusCast<Result>(prior.status()); 90 return run_next(std::move(prior)); 91 } 92 }; 93 94 template <typename T> 95 struct AllowGenericTrySeqTraits { 96 static constexpr bool value = true; 97 }; 98 99 template <> 100 struct AllowGenericTrySeqTraits<absl::Status> { 101 static constexpr bool value = false; 102 }; 103 104 template <typename T> 105 struct AllowGenericTrySeqTraits<absl::StatusOr<T>> { 106 static constexpr bool value = false; 107 }; 108 109 template <typename T, typename AnyType = void> 110 struct TakeValueExists { 111 static constexpr bool value = false; 112 }; 113 template <typename T> 114 struct TakeValueExists<T, 115 absl::void_t<decltype(TakeValue(std::declval<T>()))>> { 116 static constexpr bool value = true; 117 }; 118 // If there exists a function 'IsStatusOk(const T&) -> bool' then we assume that 119 // T is a status type for the purposes of promise sequences, and a non-OK T 120 // should terminate the sequence and return. 121 template <typename T> 122 struct TrySeqTraitsWithSfinae< 123 T, absl::enable_if_t< 124 std::is_same<decltype(IsStatusOk(std::declval<T>())), bool>::value && 125 !TakeValueExists<T>::value && AllowGenericTrySeqTraits<T>::value, 126 void>> { 127 using UnwrappedType = void; 128 using WrappedType = T; 129 template <typename Next> 130 static auto CallFactory(Next* next, T&&) { 131 return next->Make(); 132 } 133 static bool IsOk(const T& status) { return IsStatusOk(status); } 134 static std::string ErrorString(const T& status) { 135 return IsStatusOk(status) ? "OK" : "FAILED"; 136 } 137 template <typename R> 138 static R ReturnValue(T&& status) { 139 return FailureStatusCast<R>(std::move(status)); 140 } 141 template <typename Result, typename RunNext> 142 static Poll<Result> CheckResultAndRunNext(T prior, RunNext run_next) { 143 if (!IsStatusOk(prior)) return Result(std::move(prior)); 144 return run_next(std::move(prior)); 145 } 146 }; 147 template <typename T> 148 struct TrySeqTraitsWithSfinae< 149 T, absl::enable_if_t< 150 std::is_same<decltype(IsStatusOk(std::declval<T>())), bool>::value && 151 TakeValueExists<T>::value && AllowGenericTrySeqTraits<T>::value, 152 void>> { 153 using UnwrappedType = decltype(TakeValue(std::declval<T>())); 154 using WrappedType = T; 155 template <typename Next> 156 static auto CallFactory(Next* next, T&& status) { 157 return next->Make(TakeValue(std::forward<T>(status))); 158 } 159 static bool IsOk(const T& status) { return IsStatusOk(status); } 160 static std::string ErrorString(const T& status) { 161 return IsStatusOk(status) ? "OK" : "FAILED"; 162 } 163 template <typename R> 164 static R ReturnValue(T&& status) { 165 GPR_DEBUG_ASSERT(!IsStatusOk(status)); 166 return FailureStatusCast<R>(status.status()); 167 } 168 template <typename Result, typename RunNext> 169 static Poll<Result> CheckResultAndRunNext(T prior, RunNext run_next) { 170 if (!IsStatusOk(prior)) return Result(std::move(prior)); 171 return run_next(std::move(prior)); 172 } 173 }; 174 template <> 175 struct TrySeqTraitsWithSfinae<absl::Status> { 176 using UnwrappedType = void; 177 using WrappedType = absl::Status; 178 template <typename Next> 179 static auto CallFactory(Next* next, absl::Status&&) { 180 return next->Make(); 181 } 182 static bool IsOk(const absl::Status& status) { return status.ok(); } 183 static std::string ErrorString(const absl::Status& status) { 184 return status.ToString(); 185 } 186 template <typename R> 187 static R ReturnValue(absl::Status&& status) { 188 return FailureStatusCast<R>(std::move(status)); 189 } 190 template <typename Result, typename RunNext> 191 static Poll<Result> CheckResultAndRunNext(absl::Status prior, 192 RunNext run_next) { 193 if (!prior.ok()) return StatusCast<Result>(std::move(prior)); 194 return run_next(std::move(prior)); 195 } 196 }; 197 198 template <typename T> 199 using TrySeqTraits = TrySeqTraitsWithSfinae<T>; 200 201 template <typename P, typename... Fs> 202 class TrySeq { 203 public: 204 explicit TrySeq(P&& promise, Fs&&... factories, DebugLocation whence) 205 : state_(std::forward<P>(promise), std::forward<Fs>(factories)..., 206 whence) {} 207 208 auto operator()() { return state_.PollOnce(); } 209 210 private: 211 SeqState<TrySeqTraits, P, Fs...> state_; 212 }; 213 214 template <typename I, typename F, typename Arg> 215 struct TrySeqIterTraits { 216 using Iter = I; 217 using Factory = F; 218 using Argument = Arg; 219 using IterValue = decltype(*std::declval<Iter>()); 220 using StateCreated = decltype(std::declval<F>()(std::declval<IterValue>(), 221 std::declval<Arg>())); 222 using State = PromiseLike<StateCreated>; 223 using Wrapped = typename State::Result; 224 225 using Traits = TrySeqTraits<Wrapped>; 226 }; 227 228 template <typename Iter, typename Factory, typename Argument> 229 struct TrySeqIterResultTraits { 230 using IterTraits = TrySeqIterTraits<Iter, Factory, Argument>; 231 using Result = BasicSeqIter<IterTraits>; 232 }; 233 234 } // namespace promise_detail 235 236 // Try a sequence of operations. 237 // * Run the first functor as a promise. 238 // * Feed its success result into the second functor to create a promise, 239 // then run that. 240 // * ... 241 // * Feed the second-final success result into the final functor to create a 242 // promise, then run that, with the overall success result being that 243 // promises success result. 244 // If any step fails, fail everything. 245 // Functors can return StatusOr<> to signal that a value is fed forward, or 246 // Status to indicate only success/failure. In the case of returning Status, 247 // the construction functors take no arguments. 248 template <typename F> 249 F TrySeq(F functor) { 250 return functor; 251 } 252 253 template <typename F0, typename F1> 254 promise_detail::TrySeq<F0, F1> TrySeq(F0 f0, F1 f1, DebugLocation whence = {}) { 255 return promise_detail::TrySeq<F0, F1>(std::move(f0), std::move(f1), whence); 256 } 257 258 template <typename F0, typename F1, typename F2> 259 promise_detail::TrySeq<F0, F1, F2> TrySeq(F0 f0, F1 f1, F2 f2, 260 DebugLocation whence = {}) { 261 return promise_detail::TrySeq<F0, F1, F2>(std::move(f0), std::move(f1), 262 std::move(f2), whence); 263 } 264 265 template <typename F0, typename F1, typename F2, typename F3> 266 promise_detail::TrySeq<F0, F1, F2, F3> TrySeq(F0 f0, F1 f1, F2 f2, F3 f3, 267 DebugLocation whence = {}) { 268 return promise_detail::TrySeq<F0, F1, F2, F3>( 269 std::move(f0), std::move(f1), std::move(f2), std::move(f3), whence); 270 } 271 272 template <typename F0, typename F1, typename F2, typename F3, typename F4> 273 promise_detail::TrySeq<F0, F1, F2, F3, F4> TrySeq(F0 f0, F1 f1, F2 f2, F3 f3, 274 F4 f4, 275 DebugLocation whence = {}) { 276 return promise_detail::TrySeq<F0, F1, F2, F3, F4>( 277 std::move(f0), std::move(f1), std::move(f2), std::move(f3), std::move(f4), 278 whence); 279 } 280 281 template <typename F0, typename F1, typename F2, typename F3, typename F4, 282 typename F5> 283 promise_detail::TrySeq<F0, F1, F2, F3, F4, F5> TrySeq( 284 F0 f0, F1 f1, F2 f2, F3 f3, F4 f4, F5 f5, DebugLocation whence = {}) { 285 return promise_detail::TrySeq<F0, F1, F2, F3, F4, F5>( 286 std::move(f0), std::move(f1), std::move(f2), std::move(f3), std::move(f4), 287 std::move(f5), whence); 288 } 289 290 template <typename F0, typename F1, typename F2, typename F3, typename F4, 291 typename F5, typename F6> 292 promise_detail::TrySeq<F0, F1, F2, F3, F4, F5, F6> TrySeq( 293 F0 f0, F1 f1, F2 f2, F3 f3, F4 f4, F5 f5, F6 f6, 294 DebugLocation whence = {}) { 295 return promise_detail::TrySeq<F0, F1, F2, F3, F4, F5, F6>( 296 std::move(f0), std::move(f1), std::move(f2), std::move(f3), std::move(f4), 297 std::move(f5), std::move(f6), whence); 298 } 299 300 template <typename F0, typename F1, typename F2, typename F3, typename F4, 301 typename F5, typename F6, typename F7> 302 promise_detail::TrySeq<F0, F1, F2, F3, F4, F5, F6, F7> TrySeq( 303 F0 f0, F1 f1, F2 f2, F3 f3, F4 f4, F5 f5, F6 f6, F7 f7, 304 DebugLocation whence = {}) { 305 return promise_detail::TrySeq<F0, F1, F2, F3, F4, F5, F6, F7>( 306 std::move(f0), std::move(f1), std::move(f2), std::move(f3), std::move(f4), 307 std::move(f5), std::move(f6), std::move(f7), whence); 308 } 309 310 // Try a sequence of operations of unknown length. 311 // Asynchronously: 312 // for (element in (begin, end)) { 313 // auto r = wait_for factory(element, argument); 314 // if (!r.ok()) return r; 315 // argument = *r; 316 // } 317 // return argument; 318 template <typename Iter, typename Factory, typename Argument> 319 typename promise_detail::TrySeqIterResultTraits<Iter, Factory, Argument>::Result 320 TrySeqIter(Iter begin, Iter end, Argument argument, Factory factory) { 321 using Result = 322 typename promise_detail::TrySeqIterResultTraits<Iter, Factory, 323 Argument>::Result; 324 return Result(begin, end, std::move(factory), std::move(argument)); 325 } 326 327 } // namespace grpc_core 328 329 #endif // GRPC_SRC_CORE_LIB_PROMISE_TRY_SEQ_H 330