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