// Copyright 2021 gRPC authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef GRPC_SRC_CORE_LIB_PROMISE_TRY_SEQ_H #define GRPC_SRC_CORE_LIB_PROMISE_TRY_SEQ_H #include #include #include #include #include "absl/log/check.h" #include "absl/meta/type_traits.h" #include "absl/status/status.h" #include "absl/status/statusor.h" #include "src/core/lib/promise/detail/basic_seq.h" #include "src/core/lib/promise/detail/promise_like.h" #include "src/core/lib/promise/detail/seq_state.h" #include "src/core/lib/promise/detail/status.h" #include "src/core/lib/promise/poll.h" #include "src/core/lib/promise/status_flag.h" namespace grpc_core { namespace promise_detail { template struct TrySeqTraitsWithSfinae { using UnwrappedType = T; using WrappedType = absl::StatusOr; template GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static auto CallFactory(Next* next, T&& value) { return next->Make(std::forward(value)); } GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static bool IsOk(const T&) { return true; } static const char* ErrorString(const T&) { abort(); } template static R ReturnValue(T&&) { abort(); } template GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static Poll CheckResultAndRunNext(T prior, RunNext run_next) { return run_next(std::move(prior)); } }; template struct TrySeqTraitsWithSfinae> { using UnwrappedType = T; using WrappedType = absl::StatusOr; template GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static auto CallFactory( Next* next, absl::StatusOr&& status) { return next->Make(std::move(*status)); } GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static bool IsOk( const absl::StatusOr& status) { return status.ok(); } static std::string ErrorString(const absl::StatusOr& status) { return status.status().ToString(); } template GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static R ReturnValue( absl::StatusOr&& status) { return FailureStatusCast(status.status()); } template GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static Poll CheckResultAndRunNext(absl::StatusOr prior, RunNext run_next) { if (!prior.ok()) return FailureStatusCast(prior.status()); return run_next(std::move(prior)); } }; template struct AllowGenericTrySeqTraits { static constexpr bool value = true; }; template <> struct AllowGenericTrySeqTraits { static constexpr bool value = false; }; template struct AllowGenericTrySeqTraits> { static constexpr bool value = false; }; template struct TakeValueExists { static constexpr bool value = false; }; template struct TakeValueExists()))>> { static constexpr bool value = true; }; // If there exists a function 'IsStatusOk(const T&) -> bool' then we assume that // T is a status type for the purposes of promise sequences, and a non-OK T // should terminate the sequence and return. template struct TrySeqTraitsWithSfinae< T, absl::enable_if_t< std::is_same())), bool>::value && !TakeValueExists::value && AllowGenericTrySeqTraits::value, void>> { using UnwrappedType = void; using WrappedType = T; template GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static auto CallFactory(Next* next, T&&) { return next->Make(); } GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static bool IsOk(const T& status) { return IsStatusOk(status); } GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static std::string ErrorString( const T& status) { return IsStatusOk(status) ? "OK" : "FAILED"; } template GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static R ReturnValue(T&& status) { return FailureStatusCast(std::move(status)); } template GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static Poll CheckResultAndRunNext(T prior, RunNext run_next) { if (!IsStatusOk(prior)) return Result(std::move(prior)); return run_next(std::move(prior)); } }; template struct TrySeqTraitsWithSfinae< T, absl::enable_if_t< std::is_same())), bool>::value && TakeValueExists::value && AllowGenericTrySeqTraits::value, void>> { using UnwrappedType = decltype(TakeValue(std::declval())); using WrappedType = T; template GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static auto CallFactory(Next* next, T&& status) { return next->Make(TakeValue(std::forward(status))); } GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static bool IsOk(const T& status) { return IsStatusOk(status); } GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static std::string ErrorString( const T& status) { return IsStatusOk(status) ? "OK" : "FAILED"; } template GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static R ReturnValue(T&& status) { DCHECK(!IsStatusOk(status)); return FailureStatusCast(status.status()); } template GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static Poll CheckResultAndRunNext(T prior, RunNext run_next) { if (!IsStatusOk(prior)) return Result(std::move(prior)); return run_next(std::move(prior)); } }; template <> struct TrySeqTraitsWithSfinae { using UnwrappedType = void; using WrappedType = absl::Status; template GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static auto CallFactory(Next* next, absl::Status&&) { return next->Make(); } GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static bool IsOk( const absl::Status& status) { return status.ok(); } static std::string ErrorString(const absl::Status& status) { return status.ToString(); } template GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static R ReturnValue( absl::Status&& status) { return FailureStatusCast(std::move(status)); } template GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static Poll CheckResultAndRunNext(absl::Status prior, RunNext run_next) { if (!prior.ok()) return StatusCast(std::move(prior)); return run_next(std::move(prior)); } }; template using TrySeqTraits = TrySeqTraitsWithSfinae; template class TrySeq { public: GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION explicit TrySeq(P&& promise, Fs&&... factories, DebugLocation whence) : state_(std::forward

(promise), std::forward(factories)..., whence) {} GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION auto operator()() { return state_.PollOnce(); } private: SeqState state_; }; template using TrySeqIter = BasicSeqIter; template struct TrySeqContainerResultTraits { using BaseResult = TrySeqIter; class Result { public: Result(Container container, Factory factory, Argument argument) : container_(std::move(container)), base_result_(container_.begin(), container_.end(), std::move(factory), std::move(argument)) {} Result(const Result&) = delete; Result& operator=(const Result&) = delete; Result(Result&&) = default; Result& operator=(Result&&) = default; auto operator()() { return base_result_(); } private: Container container_; BaseResult base_result_; }; }; } // namespace promise_detail // Try a sequence of operations. // * Run the first functor as a promise. // * Feed its success result into the second functor to create a promise, // then run that. // * ... // * Feed the second-final success result into the final functor to create a // promise, then run that, with the overall success result being that // promises success result. // If any step fails, fail everything. // Functors can return StatusOr<> to signal that a value is fed forward, or // Status to indicate only success/failure. In the case of returning Status, // the construction functors take no arguments. template GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline F TrySeq(F functor) { return functor; } template GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline promise_detail::TrySeq TrySeq(F0 f0, F1 f1, DebugLocation whence = {}) { return promise_detail::TrySeq(std::move(f0), std::move(f1), whence); } template GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline promise_detail::TrySeq TrySeq(F0 f0, F1 f1, F2 f2, DebugLocation whence = {}) { return promise_detail::TrySeq(std::move(f0), std::move(f1), std::move(f2), whence); } template GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline promise_detail::TrySeq TrySeq(F0 f0, F1 f1, F2 f2, F3 f3, DebugLocation whence = {}) { return promise_detail::TrySeq( std::move(f0), std::move(f1), std::move(f2), std::move(f3), whence); } template GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline promise_detail::TrySeq TrySeq(F0 f0, F1 f1, F2 f2, F3 f3, F4 f4, DebugLocation whence = {}) { return promise_detail::TrySeq( std::move(f0), std::move(f1), std::move(f2), std::move(f3), std::move(f4), whence); } template GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION promise_detail::TrySeq TrySeq(F0 f0, F1 f1, F2 f2, F3 f3, F4 f4, F5 f5, DebugLocation whence = {}) { return promise_detail::TrySeq( std::move(f0), std::move(f1), std::move(f2), std::move(f3), std::move(f4), std::move(f5), whence); } template GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION promise_detail::TrySeq TrySeq(F0 f0, F1 f1, F2 f2, F3 f3, F4 f4, F5 f5, F6 f6, DebugLocation whence = {}) { return promise_detail::TrySeq( std::move(f0), std::move(f1), std::move(f2), std::move(f3), std::move(f4), std::move(f5), std::move(f6), whence); } template GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION promise_detail::TrySeq TrySeq(F0 f0, F1 f1, F2 f2, F3 f3, F4 f4, F5 f5, F6 f6, F7 f7, DebugLocation whence = {}) { return promise_detail::TrySeq( std::move(f0), std::move(f1), std::move(f2), std::move(f3), std::move(f4), std::move(f5), std::move(f6), std::move(f7), whence); } // Try a sequence of operations of unknown length. // Asynchronously: // for (element in (begin, end)) { // auto r = wait_for factory(element, argument); // if (!r.ok()) return r; // argument = *r; // } // return argument; template GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION auto TrySeqIter(Iter begin, Iter end, Argument argument, Factory factory) { return promise_detail::TrySeqIter( begin, end, std::move(factory), std::move(argument)); } template auto TrySeqContainer(Container container, Argument argument, Factory factory) { using Result = typename promise_detail::TrySeqContainerResultTraits::Result; return Result(std::move(container), std::move(factory), std::move(argument)); } } // namespace grpc_core #endif // GRPC_SRC_CORE_LIB_PROMISE_TRY_SEQ_H