[promises] New `Seq`, `TrySeq` implementation (#33991)

Our current implementation of `Seq`, `TrySeq` leverage some complicated
template stuff to work, which makes them hard to maintain. I've been
thinking about ways to simplify that for some time and had something
like this in mind - using a code generator that's at least a little more
understandable to code generate most of the complexity into a file that
is checkable.

Concurrently - I have a cool optimization in mind - but it requires that
we can move promises after polling, which is a contract change. I'm
going to work through the set of primitives we have in the coming weeks
and change that contract to enable the optimization.

---------

Co-authored-by: ctiller <ctiller@users.noreply.github.com>
pull/33995/head
Craig Tiller 2 years ago committed by GitHub
parent 66f60aa763
commit 54651a7168
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      BUILD
  2. 6
      CMakeLists.txt
  3. 2
      Package.swift
  4. 34
      build_autogenerated.yaml
  5. 4
      gRPC-C++.podspec
  6. 4
      gRPC-Core.podspec
  7. 2
      grpc.gemspec
  8. 2
      package.xml
  9. 24
      src/core/BUILD
  10. 1
      src/core/ext/filters/channel_idle/channel_idle_filter.cc
  11. 1
      src/core/lib/channel/connected_channel.cc
  12. 2
      src/core/lib/channel/promise_based_filter.cc
  13. 373
      src/core/lib/promise/detail/basic_seq.h
  14. 2076
      src/core/lib/promise/detail/seq_state.h
  15. 21
      src/core/lib/promise/seq.h
  16. 36
      src/core/lib/promise/try_seq.h
  17. 1
      src/core/lib/resource_quota/memory_quota.cc
  18. 2
      src/core/lib/security/transport/client_auth_filter.cc
  19. 2
      src/core/lib/security/transport/server_auth_filter.cc
  20. 1
      src/core/lib/surface/call.cc
  21. 2
      src/core/lib/surface/server.cc
  22. 2
      test/core/filters/filter_test.cc
  23. 3
      test/core/promise/BUILD
  24. 1
      test/core/promise/for_each_test.cc
  25. 3
      test/core/promise/loop_test.cc
  26. 1
      test/core/promise/map_pipe_test.cc
  27. 2
      test/core/promise/promise_fuzzer.cc
  28. 2
      test/core/promise/seq_test.cc
  29. 2
      test/core/promise/try_seq_metadata_test.cc
  30. 1
      test/core/promise/try_seq_test.cc
  31. 271
      tools/codegen/core/gen_seq.py
  32. 2
      tools/doxygen/Doxyfile.c++.internal
  33. 2
      tools/doxygen/Doxyfile.core.internal

@ -1534,7 +1534,6 @@ grpc_cc_library(
"//src/core:arena",
"//src/core:arena_promise",
"//src/core:atomic_utils",
"//src/core:basic_seq",
"//src/core:bitset",
"//src/core:cancel_callback",
"//src/core:channel_args",
@ -1799,7 +1798,6 @@ grpc_cc_library(
"//src/core:activity",
"//src/core:arena",
"//src/core:arena_promise",
"//src/core:basic_seq",
"//src/core:channel_args",
"//src/core:channel_fwd",
"//src/core:closure",

6
CMakeLists.txt generated

@ -8777,7 +8777,6 @@ target_link_libraries(chunked_vector_test
absl::hash
absl::type_traits
absl::statusor
absl::utility
gpr
upb
)
@ -12110,7 +12109,6 @@ target_link_libraries(flow_control_test
absl::hash
absl::type_traits
absl::statusor
absl::utility
gpr
upb
)
@ -15292,7 +15290,6 @@ target_link_libraries(interceptor_list_test
absl::hash
absl::type_traits
absl::statusor
absl::utility
gpr
upb
)
@ -16227,7 +16224,6 @@ target_link_libraries(loop_test
${_gRPC_ALLTARGETS_LIBRARIES}
absl::type_traits
absl::statusor
absl::utility
gpr
)
@ -22151,7 +22147,6 @@ target_link_libraries(seq_test
${_gRPC_ZLIB_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
absl::type_traits
absl::utility
gpr
)
@ -26523,7 +26518,6 @@ target_link_libraries(try_seq_test
${_gRPC_ALLTARGETS_LIBRARIES}
absl::type_traits
absl::statusor
absl::utility
gpr
)

2
Package.swift generated

@ -1447,8 +1447,8 @@ let package = Package(
"src/core/lib/promise/detail/basic_seq.h",
"src/core/lib/promise/detail/promise_factory.h",
"src/core/lib/promise/detail/promise_like.h",
"src/core/lib/promise/detail/seq_state.h",
"src/core/lib/promise/detail/status.h",
"src/core/lib/promise/detail/switch.h",
"src/core/lib/promise/exec_ctx_wakeup_scheduler.h",
"src/core/lib/promise/for_each.h",
"src/core/lib/promise/if.h",

@ -857,8 +857,8 @@ libs:
- src/core/lib/promise/detail/basic_seq.h
- src/core/lib/promise/detail/promise_factory.h
- src/core/lib/promise/detail/promise_like.h
- src/core/lib/promise/detail/seq_state.h
- src/core/lib/promise/detail/status.h
- src/core/lib/promise/detail/switch.h
- src/core/lib/promise/exec_ctx_wakeup_scheduler.h
- src/core/lib/promise/for_each.h
- src/core/lib/promise/if.h
@ -2253,8 +2253,8 @@ libs:
- src/core/lib/promise/detail/basic_seq.h
- src/core/lib/promise/detail/promise_factory.h
- src/core/lib/promise/detail/promise_like.h
- src/core/lib/promise/detail/seq_state.h
- src/core/lib/promise/detail/status.h
- src/core/lib/promise/detail/switch.h
- src/core/lib/promise/exec_ctx_wakeup_scheduler.h
- src/core/lib/promise/for_each.h
- src/core/lib/promise/if.h
@ -3754,8 +3754,8 @@ libs:
- src/core/lib/promise/detail/basic_seq.h
- src/core/lib/promise/detail/promise_factory.h
- src/core/lib/promise/detail/promise_like.h
- src/core/lib/promise/detail/seq_state.h
- src/core/lib/promise/detail/status.h
- src/core/lib/promise/detail/switch.h
- src/core/lib/promise/exec_ctx_wakeup_scheduler.h
- src/core/lib/promise/for_each.h
- src/core/lib/promise/if.h
@ -4307,8 +4307,8 @@ targets:
- src/core/lib/promise/detail/basic_seq.h
- src/core/lib/promise/detail/promise_factory.h
- src/core/lib/promise/detail/promise_like.h
- src/core/lib/promise/detail/seq_state.h
- src/core/lib/promise/detail/status.h
- src/core/lib/promise/detail/switch.h
- src/core/lib/promise/join.h
- src/core/lib/promise/poll.h
- src/core/lib/promise/promise.h
@ -6115,8 +6115,8 @@ targets:
- src/core/lib/promise/detail/basic_seq.h
- src/core/lib/promise/detail/promise_factory.h
- src/core/lib/promise/detail/promise_like.h
- src/core/lib/promise/detail/seq_state.h
- src/core/lib/promise/detail/status.h
- src/core/lib/promise/detail/switch.h
- src/core/lib/promise/exec_ctx_wakeup_scheduler.h
- src/core/lib/promise/loop.h
- src/core/lib/promise/map.h
@ -6167,7 +6167,6 @@ targets:
- absl/hash:hash
- absl/meta:type_traits
- absl/status:statusor
- absl/utility:utility
- gpr
- upb
uses_polling: false
@ -7761,8 +7760,8 @@ targets:
- src/core/lib/promise/detail/basic_seq.h
- src/core/lib/promise/detail/promise_factory.h
- src/core/lib/promise/detail/promise_like.h
- src/core/lib/promise/detail/seq_state.h
- src/core/lib/promise/detail/status.h
- src/core/lib/promise/detail/switch.h
- src/core/lib/promise/exec_ctx_wakeup_scheduler.h
- src/core/lib/promise/loop.h
- src/core/lib/promise/map.h
@ -7818,7 +7817,6 @@ targets:
- absl/hash:hash
- absl/meta:type_traits
- absl/status:statusor
- absl/utility:utility
- gpr
- upb
uses_polling: false
@ -7854,8 +7852,8 @@ targets:
- src/core/lib/promise/detail/basic_seq.h
- src/core/lib/promise/detail/promise_factory.h
- src/core/lib/promise/detail/promise_like.h
- src/core/lib/promise/detail/seq_state.h
- src/core/lib/promise/detail/status.h
- src/core/lib/promise/detail/switch.h
- src/core/lib/promise/exec_ctx_wakeup_scheduler.h
- src/core/lib/promise/for_each.h
- src/core/lib/promise/if.h
@ -8226,8 +8224,8 @@ targets:
- src/core/lib/promise/detail/basic_seq.h
- src/core/lib/promise/detail/promise_factory.h
- src/core/lib/promise/detail/promise_like.h
- src/core/lib/promise/detail/seq_state.h
- src/core/lib/promise/detail/status.h
- src/core/lib/promise/detail/switch.h
- src/core/lib/promise/exec_ctx_wakeup_scheduler.h
- src/core/lib/promise/for_each.h
- src/core/lib/promise/if.h
@ -9702,8 +9700,8 @@ targets:
- src/core/lib/promise/detail/basic_seq.h
- src/core/lib/promise/detail/promise_factory.h
- src/core/lib/promise/detail/promise_like.h
- src/core/lib/promise/detail/seq_state.h
- src/core/lib/promise/detail/status.h
- src/core/lib/promise/detail/switch.h
- src/core/lib/promise/exec_ctx_wakeup_scheduler.h
- src/core/lib/promise/interceptor_list.h
- src/core/lib/promise/loop.h
@ -9758,7 +9756,6 @@ targets:
- absl/hash:hash
- absl/meta:type_traits
- absl/status:statusor
- absl/utility:utility
- gpr
- upb
uses_polling: false
@ -10075,8 +10072,8 @@ targets:
- src/core/lib/promise/detail/basic_seq.h
- src/core/lib/promise/detail/promise_factory.h
- src/core/lib/promise/detail/promise_like.h
- src/core/lib/promise/detail/seq_state.h
- src/core/lib/promise/detail/status.h
- src/core/lib/promise/detail/switch.h
- src/core/lib/promise/join.h
- src/core/lib/promise/latch.h
- src/core/lib/promise/poll.h
@ -10179,7 +10176,7 @@ targets:
- src/core/lib/promise/detail/basic_seq.h
- src/core/lib/promise/detail/promise_factory.h
- src/core/lib/promise/detail/promise_like.h
- src/core/lib/promise/detail/switch.h
- src/core/lib/promise/detail/seq_state.h
- src/core/lib/promise/loop.h
- src/core/lib/promise/poll.h
- src/core/lib/promise/seq.h
@ -10188,7 +10185,6 @@ targets:
deps:
- absl/meta:type_traits
- absl/status:statusor
- absl/utility:utility
- gpr
uses_polling: false
- name: map_pipe_test
@ -10223,8 +10219,8 @@ targets:
- src/core/lib/promise/detail/basic_seq.h
- src/core/lib/promise/detail/promise_factory.h
- src/core/lib/promise/detail/promise_like.h
- src/core/lib/promise/detail/seq_state.h
- src/core/lib/promise/detail/status.h
- src/core/lib/promise/detail/switch.h
- src/core/lib/promise/exec_ctx_wakeup_scheduler.h
- src/core/lib/promise/for_each.h
- src/core/lib/promise/if.h
@ -13441,14 +13437,13 @@ targets:
- src/core/lib/promise/detail/basic_seq.h
- src/core/lib/promise/detail/promise_factory.h
- src/core/lib/promise/detail/promise_like.h
- src/core/lib/promise/detail/switch.h
- src/core/lib/promise/detail/seq_state.h
- src/core/lib/promise/poll.h
- src/core/lib/promise/seq.h
src:
- test/core/promise/seq_test.cc
deps:
- absl/meta:type_traits
- absl/utility:utility
- gpr
uses_polling: false
- name: sequential_connectivity_test
@ -15414,8 +15409,8 @@ targets:
- src/core/lib/promise/detail/basic_seq.h
- src/core/lib/promise/detail/promise_factory.h
- src/core/lib/promise/detail/promise_like.h
- src/core/lib/promise/detail/seq_state.h
- src/core/lib/promise/detail/status.h
- src/core/lib/promise/detail/switch.h
- src/core/lib/promise/poll.h
- src/core/lib/promise/try_seq.h
src:
@ -15423,7 +15418,6 @@ targets:
deps:
- absl/meta:type_traits
- absl/status:statusor
- absl/utility:utility
- gpr
uses_polling: false
- name: unique_type_name_test

4
gRPC-C++.podspec generated

@ -951,8 +951,8 @@ Pod::Spec.new do |s|
'src/core/lib/promise/detail/basic_seq.h',
'src/core/lib/promise/detail/promise_factory.h',
'src/core/lib/promise/detail/promise_like.h',
'src/core/lib/promise/detail/seq_state.h',
'src/core/lib/promise/detail/status.h',
'src/core/lib/promise/detail/switch.h',
'src/core/lib/promise/exec_ctx_wakeup_scheduler.h',
'src/core/lib/promise/for_each.h',
'src/core/lib/promise/if.h',
@ -2001,8 +2001,8 @@ Pod::Spec.new do |s|
'src/core/lib/promise/detail/basic_seq.h',
'src/core/lib/promise/detail/promise_factory.h',
'src/core/lib/promise/detail/promise_like.h',
'src/core/lib/promise/detail/seq_state.h',
'src/core/lib/promise/detail/status.h',
'src/core/lib/promise/detail/switch.h',
'src/core/lib/promise/exec_ctx_wakeup_scheduler.h',
'src/core/lib/promise/for_each.h',
'src/core/lib/promise/if.h',

4
gRPC-Core.podspec generated

@ -1548,8 +1548,8 @@ Pod::Spec.new do |s|
'src/core/lib/promise/detail/basic_seq.h',
'src/core/lib/promise/detail/promise_factory.h',
'src/core/lib/promise/detail/promise_like.h',
'src/core/lib/promise/detail/seq_state.h',
'src/core/lib/promise/detail/status.h',
'src/core/lib/promise/detail/switch.h',
'src/core/lib/promise/exec_ctx_wakeup_scheduler.h',
'src/core/lib/promise/for_each.h',
'src/core/lib/promise/if.h',
@ -2737,8 +2737,8 @@ Pod::Spec.new do |s|
'src/core/lib/promise/detail/basic_seq.h',
'src/core/lib/promise/detail/promise_factory.h',
'src/core/lib/promise/detail/promise_like.h',
'src/core/lib/promise/detail/seq_state.h',
'src/core/lib/promise/detail/status.h',
'src/core/lib/promise/detail/switch.h',
'src/core/lib/promise/exec_ctx_wakeup_scheduler.h',
'src/core/lib/promise/for_each.h',
'src/core/lib/promise/if.h',

2
grpc.gemspec generated

@ -1453,8 +1453,8 @@ Gem::Specification.new do |s|
s.files += %w( src/core/lib/promise/detail/basic_seq.h )
s.files += %w( src/core/lib/promise/detail/promise_factory.h )
s.files += %w( src/core/lib/promise/detail/promise_like.h )
s.files += %w( src/core/lib/promise/detail/seq_state.h )
s.files += %w( src/core/lib/promise/detail/status.h )
s.files += %w( src/core/lib/promise/detail/switch.h )
s.files += %w( src/core/lib/promise/exec_ctx_wakeup_scheduler.h )
s.files += %w( src/core/lib/promise/for_each.h )
s.files += %w( src/core/lib/promise/if.h )

2
package.xml generated

@ -1435,8 +1435,8 @@
<file baseinstalldir="/" name="src/core/lib/promise/detail/basic_seq.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/detail/promise_factory.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/detail/promise_like.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/detail/seq_state.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/detail/status.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/detail/switch.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/exec_ctx_wakeup_scheduler.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/for_each.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/if.h" role="src" />

@ -665,21 +665,30 @@ grpc_cc_library(
grpc_cc_library(
name = "basic_seq",
external_deps = [
"absl/meta:type_traits",
"absl/utility",
],
language = "c++",
public_hdrs = [
"lib/promise/detail/basic_seq.h",
],
deps = [
"construct_destruct",
"poll",
"//:gpr_platform",
],
)
grpc_cc_library(
name = "seq_state",
external_deps = ["absl/base:core_headers"],
language = "c++",
public_hdrs = [
"lib/promise/detail/seq_state.h",
],
deps = [
"construct_destruct",
"poll",
"promise_factory",
"promise_like",
"switch",
"//:gpr_platform",
"//:gpr",
],
)
@ -693,6 +702,7 @@ grpc_cc_library(
"basic_seq",
"poll",
"promise_like",
"seq_state",
"//:gpr_platform",
],
)
@ -713,6 +723,7 @@ grpc_cc_library(
"poll",
"promise_like",
"promise_status",
"seq_state",
"//:gpr_platform",
],
)
@ -1080,7 +1091,6 @@ grpc_cc_library(
],
deps = [
"activity",
"basic_seq",
"event_engine_memory_allocator",
"exec_ctx_wakeup_scheduler",
"experiments",

@ -19,6 +19,7 @@
#include "src/core/ext/filters/channel_idle/channel_idle_filter.h"
#include <stdint.h>
#include <stdlib.h>
#include <functional>

@ -57,7 +57,6 @@
#include "src/core/lib/promise/activity.h"
#include "src/core/lib/promise/arena_promise.h"
#include "src/core/lib/promise/context.h"
#include "src/core/lib/promise/detail/basic_seq.h"
#include "src/core/lib/promise/detail/status.h"
#include "src/core/lib/promise/for_each.h"
#include "src/core/lib/promise/if.h"

@ -38,7 +38,7 @@
#include "src/core/lib/gprpp/manual_constructor.h"
#include "src/core/lib/gprpp/status_helper.h"
#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/promise/detail/basic_seq.h"
#include "src/core/lib/promise/seq.h"
#include "src/core/lib/slice/slice.h"
extern grpc_core::TraceFlag grpc_trace_channel;

@ -17,384 +17,13 @@
#include <grpc/support/port_platform.h>
#include <array>
#include <cassert>
#include <new>
#include <tuple>
#include <utility>
#include "absl/meta/type_traits.h"
#include "absl/utility/utility.h"
#include "src/core/lib/gprpp/construct_destruct.h"
#include "src/core/lib/promise/detail/promise_factory.h"
#include "src/core/lib/promise/detail/promise_like.h"
#include "src/core/lib/promise/detail/switch.h"
#include "src/core/lib/promise/poll.h"
namespace grpc_core {
namespace promise_detail {
// Helper for SeqState to evaluate some common types to all partial
// specializations.
template <template <typename> class Traits, typename FPromise, typename FNext>
struct SeqStateTypes {
// Our current promise.
using Promise = FPromise;
// The result of our current promise.
using PromiseResult = typename Promise::Result;
// Traits around the result of our promise.
using PromiseResultTraits = Traits<PromiseResult>;
// Wrap the factory callable in our factory wrapper to deal with common edge
// cases. We use the 'unwrapped type' from the traits, so for instance, TrySeq
// can pass back a T from a StatusOr<T>.
using Next = promise_detail::OncePromiseFactory<
typename PromiseResultTraits::UnwrappedType, FNext>;
};
// One state in a sequence.
// A state contains the current promise, and the promise factory to turn the
// result of the current promise into the next state's promise. We play a shell
// game such that the prior state and our current promise are kept in a union,
// and the next promise factory is kept alongside in the state struct.
// Recursively this guarantees that the next functions get initialized once, and
// destroyed once, and don't need to be moved around in between, which avoids a
// potential O(n**2) loop of next factory moves had we used a variant of states
// here. The very first state does not have a prior state, and so that state has
// a partial specialization below. The final state does not have a next state;
// that state is inlined in BasicSeq since that was simpler to type.
template <template <typename> class Traits, char I, typename... Fs>
struct SeqState {
// The state evaluated before this state.
using PriorState = SeqState<Traits, I - 1, Fs...>;
// Initialization from callables.
explicit SeqState(std::tuple<Fs*...> fs)
: next_factory(std::move(*std::get<I + 1>(fs))) {
new (&prior) PriorState(fs);
}
// Move constructor - assumes we're in the initial state (move prior) as it's
// illegal to move a promise after polling it.
SeqState(SeqState&& other) noexcept
: next_factory(std::move(other.next_factory)) {
new (&prior) PriorState(std::move(other.prior));
}
// Copy constructor - assumes we're in the initial state (move prior) as it's
// illegal to move a promise after polling it.
SeqState(const SeqState& other) : next_factory(other.next_factory) {
new (&prior) PriorState(std::move(other.prior));
}
// Empty destructor - we instead destruct the innards in BasicSeq manually
// depending on state.
~SeqState() {}
// Evaluate the current promise, next promise factory types for this state.
// The current promise is the next promise from the prior state.
// The next factory callable is from the callables passed in:
// Fs[0] is the initial promise, Fs[1] is the state 0 next factory, Fs[2] is
// the state 1 next factory, etc...
using Types = SeqStateTypes<
Traits, typename PriorState::Types::Next::Promise,
typename std::tuple_element<I + 1, std::tuple<Fs...>>::type>;
// Storage for either the current promise or the prior state.
union {
// If we're in the prior state.
GPR_NO_UNIQUE_ADDRESS PriorState prior;
// The callables representing our promise.
GPR_NO_UNIQUE_ADDRESS typename Types::Promise current_promise;
};
// Storage for the next promise factory.
GPR_NO_UNIQUE_ADDRESS typename Types::Next next_factory;
};
// Partial specialization of SeqState above for the first state - it has no
// prior state, so we take the first callable from the template arg list and use
// it as a promise.
template <template <typename> class Traits, typename... Fs>
struct SeqState<Traits, 0, Fs...> {
// Initialization from callables.
explicit SeqState(std::tuple<Fs*...> args)
: current_promise(std::move(*std::get<0>(args))),
next_factory(std::move(*std::get<1>(args))) {}
// Move constructor - it's assumed we're in this state (see above).
SeqState(SeqState&& other) noexcept
: current_promise(std::move(other.current_promise)),
next_factory(std::move(other.next_factory)) {}
// Copy constructor - it's assumed we're in this state (see above).
SeqState(const SeqState& other)
: current_promise(other.current_promise),
next_factory(other.next_factory) {}
// Empty destructor - we instead destruct the innards in BasicSeq manually
// depending on state.
~SeqState() {}
// Evaluate the current promise, next promise factory types for this state.
// Our callable is the first element of Fs, wrapped in PromiseLike to handle
// some common edge cases. The next factory is the second element.
using Types = SeqStateTypes<
Traits,
PromiseLike<typename std::tuple_element<0, std::tuple<Fs...>>::type>,
typename std::tuple_element<1, std::tuple<Fs...>>::type>;
GPR_NO_UNIQUE_ADDRESS typename Types::Promise current_promise;
GPR_NO_UNIQUE_ADDRESS typename Types::Next next_factory;
};
// Helper to get a specific state index.
// Calls the prior state, unless it's this state, in which case it returns
// that.
template <char I, template <typename> class Traits, char J, typename... Fs>
struct GetSeqStateInner {
static SeqState<Traits, I, Fs...>* f(SeqState<Traits, J, Fs...>* p) {
return GetSeqStateInner<I, Traits, J - 1, Fs...>::f(&p->prior);
}
};
template <char I, template <typename> class Traits, typename... Fs>
struct GetSeqStateInner<I, Traits, I, Fs...> {
static SeqState<Traits, I, Fs...>* f(SeqState<Traits, I, Fs...>* p) {
return p;
}
};
template <char I, template <typename> class Traits, char J, typename... Fs>
absl::enable_if_t<I <= J, SeqState<Traits, I, Fs...>*> GetSeqState(
SeqState<Traits, J, Fs...>* p) {
return GetSeqStateInner<I, Traits, J, Fs...>::f(p);
}
template <template <typename> class Traits, char I, typename... Fs, typename T>
auto CallNext(SeqState<Traits, I, Fs...>* state, T&& arg)
-> decltype(SeqState<Traits, I, Fs...>::Types::PromiseResultTraits::
CallFactory(&state->next_factory, std::forward<T>(arg))) {
return SeqState<Traits, I, Fs...>::Types::PromiseResultTraits::CallFactory(
&state->next_factory, std::forward<T>(arg));
}
// A sequence under some traits for some set of callables Fs.
// Fs[0] should be a promise-like object that yields a value.
// Fs[1..] should be promise-factory-like objects that take the value from the
// previous step and yield a promise. Note that most of the machinery in
// PromiseFactory exists to make it possible for those promise-factory-like
// objects to be anything that's convenient. Traits defines how we move from one
// step to the next. Traits sets up the wrapping and escape handling for the
// sequence. Promises return wrapped values that the trait can inspect and
// unwrap before passing them to the next element of the sequence. The trait can
// also interpret a wrapped value as an escape value, which terminates
// evaluation of the sequence immediately yielding a result. Traits for type T
// have the members:
// * type UnwrappedType - the type after removing wrapping from T (i.e. for
// TrySeq, T=StatusOr<U> yields UnwrappedType=U).
// * type WrappedType - the type after adding wrapping if it doesn't already
// exist (i.e. for TrySeq if T is not Status/StatusOr/void, then
// WrappedType=StatusOr<T>; if T is Status then WrappedType=Status (it's
// already wrapped!))
// * template <typename Next> void CallFactory(Next* next_factory, T&& value) -
// call promise factory next_factory with the result of unwrapping value, and
// return the resulting promise.
// * template <typename Result, typename RunNext> Poll<Result>
// CheckResultAndRunNext(T prior, RunNext run_next) - examine the value of
// prior, and decide to escape or continue. If escaping, return the final
// sequence value of type Poll<Result>. If continuing, return the value of
// run_next(std::move(prior)).
template <template <typename> class Traits, typename... Fs>
class BasicSeq {
private:
// Number of states in the sequence - we'll refer to this some!
static constexpr char N = sizeof...(Fs);
// Current state.
static_assert(N < 128, "Long sequence... please revisit BasicSeq");
char state_ = 0;
// The penultimate state contains all the preceding states too.
using PenultimateState = SeqState<Traits, N - 2, Fs...>;
// The final state is simply the final promise, which is the next promise from
// the penultimate state.
using FinalPromise = typename PenultimateState::Types::Next::Promise;
union {
GPR_NO_UNIQUE_ADDRESS PenultimateState penultimate_state_;
GPR_NO_UNIQUE_ADDRESS FinalPromise final_promise_;
};
using FinalPromiseResult = typename FinalPromise::Result;
using Result = typename Traits<FinalPromiseResult>::WrappedType;
// Get a state by index.
template <char I>
absl::enable_if_t < I<N - 2, SeqState<Traits, I, Fs...>*> state() {
return GetSeqState<I>(&penultimate_state_);
}
template <char I>
absl::enable_if_t<I == N - 2, PenultimateState*> state() {
return &penultimate_state_;
}
// Get the next state's promise.
template <char I>
auto next_promise() -> absl::enable_if_t<
I != N - 2,
decltype(&GetSeqState<I + 1>(static_cast<PenultimateState*>(nullptr))
->current_promise)> {
return &GetSeqState<I + 1>(&penultimate_state_)->current_promise;
}
template <char I>
absl::enable_if_t<I == N - 2, FinalPromise*> next_promise() {
return &final_promise_;
}
// Callable to advance the state to the next one after I given the result from
// state I.
template <char I>
struct RunNext {
BasicSeq* s;
template <typename T>
Poll<Result> operator()(T&& value) {
auto* prior = s->state<I>();
using StateType = absl::remove_reference_t<decltype(*prior)>;
// Destroy the promise that just completed.
Destruct(&prior->current_promise);
// Construct the next promise by calling the next promise factory.
// We need to ask the traits to do this to deal with value
// wrapping/unwrapping.
auto n = StateType::Types::PromiseResultTraits::CallFactory(
&prior->next_factory, std::forward<T>(value));
// Now we also no longer need the factory, so we can destroy that.
Destruct(&prior->next_factory);
// Constructing the promise for the new state will use the memory
// previously occupied by the promise & next factory of the old state.
Construct(s->next_promise<I>(), std::move(n));
// Store the state counter.
s->state_ = I + 1;
// Recursively poll the new current state.
return s->RunState<I + 1>();
}
};
// Poll the current state, advance it if necessary.
template <char I>
absl::enable_if_t<I != N - 1, Poll<Result>> RunState() {
// Get a pointer to the state object.
auto* s = state<I>();
// Poll the current promise in this state.
auto r = s->current_promise();
// If we are still pending, say so by returning.
if (r.pending()) return Pending();
// Current promise is ready, as the traits to do the next thing.
// That may be returning - eg if TrySeq sees an error.
// Or it may be by calling the callable we hand down - RunNext - which
// will advance the state and call the next promise.
return Traits<
typename absl::remove_reference_t<decltype(*s)>::Types::PromiseResult>::
template CheckResultAndRunNext<Result>(std::move(r.value()),
RunNext<I>{this});
}
// Specialization of RunState to run the final state.
template <char I>
absl::enable_if_t<I == N - 1, Poll<Result>> RunState() {
// Poll the final promise.
auto r = final_promise_();
// If we are still pending, say so by returning.
if (r.pending()) return Pending();
// We are complete, return the (wrapped) result.
return Result(std::move(r.value()));
}
// For state numbered I, destruct the current promise and the next promise
// factory, and recursively destruct the next promise factories for future
// states (since they also still exist).
template <char I>
absl::enable_if_t<I != N - 1, void>
DestructCurrentPromiseAndSubsequentFactories() {
Destruct(&GetSeqState<I>(&penultimate_state_)->current_promise);
DestructSubsequentFactories<I>();
}
template <char I>
absl::enable_if_t<I == N - 1, void>
DestructCurrentPromiseAndSubsequentFactories() {
Destruct(&final_promise_);
}
// For state I, destruct the next promise factory, and recursively the next
// promise factories after.
template <char I>
absl::enable_if_t<I != N - 1, void> DestructSubsequentFactories() {
Destruct(&GetSeqState<I>(&penultimate_state_)->next_factory);
DestructSubsequentFactories<I + 1>();
}
template <char I>
absl::enable_if_t<I == N - 1, void> DestructSubsequentFactories() {}
// Placate older compilers by wrapping RunState in a struct so that their
// parameter unpacking can work.
template <char I>
struct RunStateStruct {
BasicSeq* s;
Poll<Result> operator()() { return s->RunState<I>(); }
};
// Similarly placate those compilers for
// DestructCurrentPromiseAndSubsequentFactories
template <char I>
struct DestructCurrentPromiseAndSubsequentFactoriesStruct {
BasicSeq* s;
void operator()() {
return s->DestructCurrentPromiseAndSubsequentFactories<I>();
}
};
// Run the current state (and possibly later states if that one finishes).
// Single argument is a type that encodes the integer sequence 0, 1, 2, ...,
// N-1 as a type, but which uses no memory. This is used to expand out
// RunState instances using a template unpack to pass to Switch, which encodes
// a switch statement over the various cases. This ultimately gives us a
// Duff's device like mechanic for evaluating sequences.
template <char... I>
Poll<Result> Run(absl::integer_sequence<char, I...>) {
return Switch<Poll<Result>>(state_, RunStateStruct<I>{this}...);
}
// Run the appropriate destructors for a given state.
// Single argument is a type that encodes the integer sequence 0, 1, 2, ...,
// N-1 as a type, but which uses no memory. This is used to expand out
// DestructCurrentPromiseAndSubsequentFactories instances to pass to Switch,
// which can choose the correct instance at runtime to destroy everything.
template <char... I>
void RunDestruct(absl::integer_sequence<char, I...>) {
Switch<void>(
state_, DestructCurrentPromiseAndSubsequentFactoriesStruct<I>{this}...);
}
public:
// Construct a sequence given the callables that will control it.
explicit BasicSeq(Fs... fs) : penultimate_state_(std::make_tuple(&fs...)) {}
// No assignment... we don't need it (but if we ever find a good case, then
// it's ok to implement).
BasicSeq& operator=(const BasicSeq&) = delete;
// Copy construction - only for state 0.
// It's illegal to copy a Promise after polling it - if we are in state>0 we
// *must* have been polled.
BasicSeq(const BasicSeq& other) {
assert(other.state_ == 0);
new (&penultimate_state_) PenultimateState(other.penultimate_state_);
}
// Move construction - only for state 0.
// It's illegal to copy a Promise after polling it - if we are in state>0 we
// *must* have been polled.
BasicSeq(BasicSeq&& other) noexcept {
assert(other.state_ == 0);
new (&penultimate_state_)
PenultimateState(std::move(other.penultimate_state_));
}
// Destruct based on current state.
~BasicSeq() { RunDestruct(absl::make_integer_sequence<char, N>()); }
// Poll the sequence once.
Poll<Result> operator()() {
return Run(absl::make_integer_sequence<char, N>());
}
};
// As above, but models a sequence of unknown size
// Models a sequence of unknown size
// At each element, the accumulator A and the current value V is passed to some
// function of type IterTraits::Factory as f(V, IterTraits::Argument); f is
// expected to return a promise that resolves to IterTraits::Wrapped.

File diff suppressed because it is too large Load Diff

@ -17,11 +17,14 @@
#include <grpc/support/port_platform.h>
#include <stdlib.h>
#include <type_traits>
#include <utility>
#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/poll.h"
namespace grpc_core {
@ -36,6 +39,11 @@ struct SeqTraits {
static auto CallFactory(Next* next, T&& value) {
return next->Make(std::forward<T>(value));
}
static bool IsOk(const T&) { return true; }
template <typename R>
static R ReturnValue(T&&) {
abort();
}
template <typename F, typename Elem>
static auto CallSeqFactory(F& f, Elem&& elem, T&& value) {
return f(std::forward<Elem>(elem), std::forward<T>(value));
@ -47,8 +55,17 @@ struct SeqTraits {
}
};
template <typename... Fs>
using Seq = BasicSeq<SeqTraits, Fs...>;
template <typename P, typename... Fs>
class Seq {
public:
explicit Seq(P&& promise, Fs&&... factories)
: state_(std::forward<P>(promise), std::forward<Fs>(factories)...) {}
auto operator()() { return state_.PollOnce(); }
private:
SeqState<SeqTraits, P, Fs...> state_;
};
template <typename I, typename F, typename Arg>
struct SeqIterTraits {

@ -17,6 +17,8 @@
#include <grpc/support/port_platform.h>
#include <stdlib.h>
#include <type_traits>
#include <utility>
@ -26,6 +28,7 @@
#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"
@ -41,6 +44,11 @@ struct TrySeqTraitsWithSfinae {
static auto CallFactory(Next* next, T&& value) {
return next->Make(std::forward<T>(value));
}
static bool IsOk(const T&) { return true; }
template <typename R>
static R ReturnValue(T&&) {
abort();
}
template <typename F, typename Elem>
static auto CallSeqFactory(F& f, Elem&& elem, T&& value)
-> decltype(f(std::forward<Elem>(elem), std::forward<T>(value))) {
@ -60,6 +68,11 @@ struct TrySeqTraitsWithSfinae<absl::StatusOr<T>> {
static auto CallFactory(Next* next, absl::StatusOr<T>&& status) {
return next->Make(std::move(*status));
}
static bool IsOk(const absl::StatusOr<T>& status) { return status.ok(); }
template <typename R>
static R ReturnValue(absl::StatusOr<T>&& status) {
return StatusCast<R>(status.status());
}
template <typename F, typename Elem>
static auto CallSeqFactory(F& f, Elem&& elem, absl::StatusOr<T> value)
-> decltype(f(std::forward<Elem>(elem), std::move(*value))) {
@ -86,6 +99,11 @@ struct TrySeqTraitsWithSfinae<
static auto CallFactory(Next* next, T&&) {
return next->Make();
}
static bool IsOk(const T& status) { return IsStatusOk(status); }
template <typename R>
static R ReturnValue(T&& status) {
return R(std::move(status));
}
template <typename Result, typename RunNext>
static Poll<Result> CheckResultAndRunNext(T prior, RunNext run_next) {
if (!IsStatusOk(prior)) return Result(std::move(prior));
@ -100,6 +118,11 @@ struct TrySeqTraitsWithSfinae<absl::Status> {
static auto CallFactory(Next* next, absl::Status&&) {
return next->Make();
}
static bool IsOk(const absl::Status& status) { return status.ok(); }
template <typename R>
static R ReturnValue(absl::Status&& status) {
return StatusCast<R>(std::move(status));
}
template <typename Result, typename RunNext>
static Poll<Result> CheckResultAndRunNext(absl::Status prior,
RunNext run_next) {
@ -111,8 +134,17 @@ struct TrySeqTraitsWithSfinae<absl::Status> {
template <typename T>
using TrySeqTraits = TrySeqTraitsWithSfinae<T>;
template <typename... Fs>
using TrySeq = BasicSeq<TrySeqTraits, Fs...>;
template <typename P, typename... Fs>
class TrySeq {
public:
explicit TrySeq(P&& promise, Fs&&... factories)
: state_(std::forward<P>(promise), std::forward<Fs>(factories)...) {}
auto operator()() { return state_.PollOnce(); }
private:
SeqState<TrySeqTraits, P, Fs...> state_;
};
template <typename I, typename F, typename Arg>
struct TrySeqIterTraits {

@ -28,7 +28,6 @@
#include "src/core/lib/debug/trace.h"
#include "src/core/lib/gpr/useful.h"
#include "src/core/lib/gprpp/mpscq.h"
#include "src/core/lib/promise/detail/basic_seq.h"
#include "src/core/lib/promise/exec_ctx_wakeup_scheduler.h"
#include "src/core/lib/promise/loop.h"
#include "src/core/lib/promise/map.h"

@ -18,6 +18,7 @@
#include <grpc/support/port_platform.h>
#include <stdint.h>
#include <string.h>
#include <functional>
@ -43,7 +44,6 @@
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/promise/arena_promise.h"
#include "src/core/lib/promise/context.h"
#include "src/core/lib/promise/detail/basic_seq.h"
#include "src/core/lib/promise/promise.h"
#include "src/core/lib/promise/seq.h"
#include "src/core/lib/promise/try_seq.h"

@ -18,6 +18,8 @@
#include <grpc/support/port_platform.h>
#include <stdint.h>
#include <algorithm>
#include <atomic>
#include <cstddef>

@ -85,7 +85,6 @@
#include "src/core/lib/promise/activity.h"
#include "src/core/lib/promise/arena_promise.h"
#include "src/core/lib/promise/context.h"
#include "src/core/lib/promise/detail/basic_seq.h"
#include "src/core/lib/promise/latch.h"
#include "src/core/lib/promise/map.h"
#include "src/core/lib/promise/party.h"

@ -61,11 +61,11 @@
#include "src/core/lib/iomgr/pollset_set.h"
#include "src/core/lib/promise/activity.h"
#include "src/core/lib/promise/context.h"
#include "src/core/lib/promise/detail/basic_seq.h"
#include "src/core/lib/promise/map.h"
#include "src/core/lib/promise/pipe.h"
#include "src/core/lib/promise/poll.h"
#include "src/core/lib/promise/promise.h"
#include "src/core/lib/promise/seq.h"
#include "src/core/lib/promise/try_seq.h"
#include "src/core/lib/slice/slice_buffer.h"
#include "src/core/lib/slice/slice_internal.h"

@ -31,9 +31,9 @@
#include "src/core/lib/promise/activity.h"
#include "src/core/lib/promise/arena_promise.h"
#include "src/core/lib/promise/context.h"
#include "src/core/lib/promise/detail/basic_seq.h"
#include "src/core/lib/promise/pipe.h"
#include "src/core/lib/promise/poll.h"
#include "src/core/lib/promise/seq.h"
#include "src/core/lib/resource_quota/arena.h"
#include "src/core/lib/slice/slice.h"
#include "test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.pb.h"

@ -206,7 +206,6 @@ grpc_cc_test(
uses_event_engine = False,
uses_polling = False,
deps = [
"//src/core:basic_seq",
"//src/core:loop",
"//src/core:seq",
],
@ -337,7 +336,6 @@ grpc_cc_test(
"//src/core:activity",
"//src/core:arena",
"//src/core:basic_join",
"//src/core:basic_seq",
"//src/core:event_engine_memory_allocator",
"//src/core:for_each",
"//src/core:join",
@ -364,7 +362,6 @@ grpc_cc_test(
"//src/core:activity",
"//src/core:arena",
"//src/core:basic_join",
"//src/core:basic_seq",
"//src/core:event_engine_memory_allocator",
"//src/core:for_each",
"//src/core:join",

@ -27,7 +27,6 @@
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/promise/activity.h"
#include "src/core/lib/promise/detail/basic_join.h"
#include "src/core/lib/promise/detail/basic_seq.h"
#include "src/core/lib/promise/join.h"
#include "src/core/lib/promise/map.h"
#include "src/core/lib/promise/pipe.h"

@ -14,11 +14,12 @@
#include "src/core/lib/promise/loop.h"
#include <stdint.h>
#include <utility>
#include "gtest/gtest.h"
#include "src/core/lib/promise/detail/basic_seq.h"
#include "src/core/lib/promise/seq.h"
namespace grpc_core {

@ -28,7 +28,6 @@
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/promise/activity.h"
#include "src/core/lib/promise/detail/basic_join.h"
#include "src/core/lib/promise/detail/basic_seq.h"
#include "src/core/lib/promise/for_each.h"
#include "src/core/lib/promise/join.h"
#include "src/core/lib/promise/map.h"

@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdint.h>
#include <functional>
#include <map>
#include <memory>

@ -14,6 +14,8 @@
#include "src/core/lib/promise/seq.h"
#include <stdint.h>
#include <memory>
#include <string>
#include <vector>

@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdint.h>
#include <memory>
#include "gtest/gtest.h"

@ -14,6 +14,7 @@
#include "src/core/lib/promise/try_seq.h"
#include <stdint.h>
#include <stdlib.h>
#include <algorithm>

@ -0,0 +1,271 @@
#!/usr/bin/env python3
# Copyright 2023 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.
import sys
from mako.template import Template
seq_state = Template(
"""
<%def name="decl(promise_name, i, n)">
using Promise${i} = ${promise_name};
% if i < n-1:
using NextFactory${i} = OncePromiseFactory<typename Promise${i}::Result, F${i}>;
${decl(f"typename NextFactory{i}::Promise", i+1, n)}
% endif
</%def>
<%def name="state(i, n)">
% if i == 0:
Promise0 current_promise;
NextFactory0 next_factory;
% elif i == n-1:
union {
struct { ${state(i-1, n)} } prior;
Promise${i} current_promise;
};
% else:
union {
struct { ${state(i-1, n)} } prior;
P${i} current_promise;
};
NextFactory${i} next_factory;
% endif
</%def>
template <template<typename> class Traits, typename P, ${",".join(f"typename F{i}" for i in range(0,n-1))}>
struct SeqState<Traits, P, ${",".join(f"F{i}" for i in range(0,n-1))}> {
<% name="PromiseLike<P>" %>
% for i in range(0,n-1):
using Promise${i} = ${name};
using PromiseResult${i} = typename Promise${i}::Result;
using PromiseResultTraits${i} = Traits<PromiseResult${i}>;
using NextFactory${i} = OncePromiseFactory<typename PromiseResultTraits${i}::UnwrappedType, F${i}>;
<% name=f"typename NextFactory{i}::Promise" %>\\
% endfor
using Promise${n-1} = ${name};
using PromiseResult${n-1} = typename Promise${n-1}::Result;
using PromiseResultTraits${n-1} = Traits<PromiseResult${n-1}>;
using Result = typename PromiseResultTraits${n-1}::WrappedType;
% if n == 1:
Promise0 current_promise;
% else:
% for i in range(0,n-1):
struct Running${i} {
% if i != 0:
union {
GPR_NO_UNIQUE_ADDRESS Running${i-1} prior;
% endif
GPR_NO_UNIQUE_ADDRESS Promise${i} current_promise;
% if i != 0:
};
% endif
GPR_NO_UNIQUE_ADDRESS NextFactory${i} next_factory;
};
% endfor
union {
GPR_NO_UNIQUE_ADDRESS Running${n-2} prior;
GPR_NO_UNIQUE_ADDRESS Promise${n-1} current_promise;
};
% endif
enum class State : uint8_t { ${",".join(f"kState{i}" for i in range(0,n))} };
GPR_NO_UNIQUE_ADDRESS State state = State::kState0;
SeqState(P&& p, ${",".join(f"F{i}&& f{i}" for i in range(0,n-1))}) noexcept {
Construct(&${"prior."*(n-1)}current_promise, std::forward<P>(p));
% for i in range(0,n-1):
Construct(&${"prior."*(n-1-i)}next_factory, std::forward<F${i}>(f${i}));
% endfor
}
~SeqState() {
switch (state) {
% for i in range(0,n-1):
case State::kState${i}:
Destruct(&${"prior."*(n-1-i)}current_promise);
goto tail${i};
% endfor
case State::kState${n-1}:
Destruct(&current_promise);
return;
}
% for i in range(0,n-1):
tail${i}:
Destruct(&${"prior."*(n-1-i)}next_factory);
% endfor
}
SeqState(const SeqState& other) noexcept : state(other.state) {
GPR_ASSERT(state == State::kState0);
Construct(&${"prior."*(n-1-i)}current_promise,
other.${"prior."*(n-1-i)}current_promise);
% for i in range(0,n-1):
Construct(&${"prior."*(n-1-i)}next_factory,
other.${"prior."*(n-1-i)}next_factory);
% endfor
}
SeqState& operator=(const SeqState& other) = delete;
SeqState(SeqState&& other) noexcept : state(other.state) {
switch (state) {
% for i in range(0,n-1):
case State::kState${i}:
Construct(&${"prior."*(n-1-i)}current_promise,
std::move(other.${"prior."*(n-1-i)}current_promise));
goto tail${i};
% endfor
case State::kState${n-1}:
Construct(&current_promise, std::move(other.current_promise));
return;
}
% for i in range(0,n-1):
tail${i}:
Construct(&${"prior."*(n-1-i)}next_factory,
std::move(other.${"prior."*(n-1-i)}next_factory));
% endfor
}
SeqState& operator=(SeqState&& other) = delete;
Poll<Result> PollOnce() {
switch (state) {
% for i in range(0,n-1):
case State::kState${i}: {
auto result = ${"prior."*(n-1-i)}current_promise();
PromiseResult${i}* p = result.value_if_ready();
if (p == nullptr) return Pending{};
if (!PromiseResultTraits${i}::IsOk(*p)) {
return PromiseResultTraits${i}::template ReturnValue<Result>(std::move(*p));
}
Destruct(&${"prior."*(n-1-i)}current_promise);
auto next_promise = PromiseResultTraits${i}::CallFactory(&${"prior."*(n-1-i)}next_factory, std::move(*p));
Destruct(&${"prior."*(n-1-i)}next_factory);
Construct(&${"prior."*(n-2-i)}current_promise, std::move(next_promise));
state = State::kState${i+1};
}
ABSL_FALLTHROUGH_INTENDED;
% endfor
default:
case State::kState${n-1}: {
auto result = current_promise();
auto* p = result.value_if_ready();
if (p == nullptr) return Pending{};
return Result(std::move(*p));
}
}
}
};"""
)
front_matter = """
#ifndef GRPC_SRC_CORE_LIB_PROMISE_DETAIL_SEQ_STATE_H
#define GRPC_SRC_CORE_LIB_PROMISE_DETAIL_SEQ_STATE_H
// This file is generated by tools/codegen/core/gen_seq.py
#include <grpc/support/port_platform.h>
#include <stdint.h>
#include <utility>
#include "absl/base/attributes.h"
#include <grpc/support/log.h>
#include "src/core/lib/gprpp/construct_destruct.h"
#include "src/core/lib/promise/detail/promise_factory.h"
#include "src/core/lib/promise/detail/promise_like.h"
#include "src/core/lib/promise/poll.h"
// A sequence under some traits for some set of callables P, Fs.
// P should be a promise-like object that yields a value.
// Fs... should be promise-factory-like objects that take the value from the
// previous step and yield a promise. Note that most of the machinery in
// PromiseFactory exists to make it possible for those promise-factory-like
// objects to be anything that's convenient.
// Traits defines how we move from one step to the next. Traits sets up the
// wrapping and escape handling for the sequence.
// Promises return wrapped values that the trait can inspect and unwrap before
// passing them to the next element of the sequence. The trait can
// also interpret a wrapped value as an escape value, which terminates
// evaluation of the sequence immediately yielding a result. Traits for type T
// have the members:
// * type UnwrappedType - the type after removing wrapping from T (i.e. for
// TrySeq, T=StatusOr<U> yields UnwrappedType=U).
// * type WrappedType - the type after adding wrapping if it doesn't already
// exist (i.e. for TrySeq if T is not Status/StatusOr/void, then
// WrappedType=StatusOr<T>; if T is Status then WrappedType=Status (it's
// already wrapped!))
// * template <typename Next> void CallFactory(Next* next_factory, T&& value) -
// call promise factory next_factory with the result of unwrapping value, and
// return the resulting promise.
// * template <typename Result, typename RunNext> Poll<Result>
// CheckResultAndRunNext(T prior, RunNext run_next) - examine the value of
// prior, and decide to escape or continue. If escaping, return the final
// sequence value of type Poll<Result>. If continuing, return the value of
// run_next(std::move(prior)).
//
// A state contains the current promise, and the promise factory to turn the
// result of the current promise into the next state's promise. We play a shell
// game such that the prior state and our current promise are kept in a union,
// and the next promise factory is kept alongside in the state struct.
// Recursively this guarantees that the next functions get initialized once, and
// destroyed once, and don't need to be moved around in between, which avoids a
// potential O(n**2) loop of next factory moves had we used a variant of states
// here. The very first state does not have a prior state, and so that state has
// a partial specialization below. The final state does not have a next state;
// that state is inlined in BasicSeq since that was simpler to type.
namespace grpc_core {
namespace promise_detail {
template <template<typename> class Traits, typename P, typename... Fs>
struct SeqState;
"""
end_matter = """
} // namespace promise_detail
} // namespace grpc_core
#endif // GRPC_SRC_CORE_LIB_PROMISE_DETAIL_SEQ_STATE_H
"""
# utility: print a big comment block into a set of files
def put_banner(files, banner):
for f in files:
for line in banner:
print("// %s" % line, file=f)
print("", file=f)
with open(sys.argv[0]) as my_source:
copyright = []
for line in my_source:
if line[0] != "#":
break
for line in my_source:
if line[0] == "#":
copyright.append(line)
break
for line in my_source:
if line[0] != "#":
break
copyright.append(line)
copyright = [line[2:].rstrip() for line in copyright]
with open("src/core/lib/promise/detail/seq_state.h", "w") as f:
put_banner([f], copyright)
print(front_matter, file=f)
for n in range(2, 10):
print(seq_state.render(n=n), file=f)
print(end_matter, file=f)

@ -2450,8 +2450,8 @@ src/core/lib/promise/context.h \
src/core/lib/promise/detail/basic_seq.h \
src/core/lib/promise/detail/promise_factory.h \
src/core/lib/promise/detail/promise_like.h \
src/core/lib/promise/detail/seq_state.h \
src/core/lib/promise/detail/status.h \
src/core/lib/promise/detail/switch.h \
src/core/lib/promise/exec_ctx_wakeup_scheduler.h \
src/core/lib/promise/for_each.h \
src/core/lib/promise/if.h \

@ -2231,8 +2231,8 @@ src/core/lib/promise/context.h \
src/core/lib/promise/detail/basic_seq.h \
src/core/lib/promise/detail/promise_factory.h \
src/core/lib/promise/detail/promise_like.h \
src/core/lib/promise/detail/seq_state.h \
src/core/lib/promise/detail/status.h \
src/core/lib/promise/detail/switch.h \
src/core/lib/promise/exec_ctx_wakeup_scheduler.h \
src/core/lib/promise/for_each.h \
src/core/lib/promise/if.h \

Loading…
Cancel
Save