[promises] Promise based grpc_call (#29598)

* Automated change: Fix sanity tests

* fix

* fixes

* fixup allocator story - we should require a context

* fixes

* doodling

* context scribbles

* [arena] Add ManagedNew(), gtest-ify test

Add a ManagedNew() method to Arena that calls the relevant destructor at Arena destruction time.

There are some cases coming up in the promise based call work where this becomes super convenient, and I expect it's likely that there are other places that's true too.

* Automated change: Fix sanity tests

* progress

* lalalal

* progress

* x

* Automated change: Fix sanity tests

* fixes

* fix

* fix

* fix

* fix

* Automated change: Fix sanity tests

* fix

* Automated change: Fix sanity tests

* fixes

* fixes

* fixes

* Automated change: Fix sanity tests

* progress

* fix client streaming

* handle invalid flags

* Automated change: Fix sanity tests

* no logging

* progress

* progress

* channelz

* tentative fix

* fix

* lalala

* Automated change: Fix sanity tests

* more readable trace

* logging improvements, leading to bug fix in connected channel

* fix

* improve debuggability

* fix

* progress to better refcounting

* progress

* Automated change: Fix sanity tests

* Automated change: Fix sanity tests

* fix

* fix

* fix

* threading

* Automated change: Fix sanity tests

* fix

* fix

* improve debuggability

* fix

* fix

* Automated change: Fix sanity tests

* fix

* make promises runtime configurable

* Automated change: Fix sanity tests

* fix

* fix build

* fix broken test

* clean up api

* deal with stats better

* peer string!

* introduce fragments

* Automated change: Fix sanity tests

* use fragments

* stuff

* [promises] Add AtomicWaker type

* Automated change: Fix sanity tests

* fix

* fix write path

* fix

* polling-entity-hell

* review feedback

* fix

* fix

* fix

* fix

* make an experiment

* [experiments] Make output more diffable/readable

* Automated change: Fix sanity tests

* buildifier sized indentations

* fix

* fix

* Automated change: Fix sanity tests

* fix?

* fix promise

* prototype

* progress

* implement new api

* Revert "fix promise"

This reverts commit ded85e7d19.

* Revert "Revert "fix promise""

This reverts commit c2acef1958.

* progress

* done

* Automated change: Fix sanity tests

* fix

* fix

* fix

* Automated change: Fix sanity tests

* updates

* review feedback

* first pass feedback

* Automated change: Fix sanity tests

* review feedback

* naming

* better-logs

* fix test

* Automated change: Fix sanity tests

* comments

* fix

* progress

* validation

* iwyu

* fix

* ugh this needs to be any

* fix flakiness in asan

* call tracing

* cleanup unused args

* fix windows

* fix build

* ugh

* fix tsan race

* threading-fix

* bloat1

* bloat2

* bloat3

* fix

* unused-args

* sanity

* iwyu

* fix

* fix

* this is ok

* iwyu, exchange

* fix

* Automated change: Fix sanity tests

* fix ee lifetime issue

* fix

* review feedback

* Automated change: Fix sanity tests

* comment

* x

* fix tsan race

* iwyu

* Automated change: Fix sanity tests

Co-authored-by: ctiller <ctiller@users.noreply.github.com>
pull/31118/head
Craig Tiller 2 years ago committed by GitHub
parent e1e1f6181f
commit beb5bdca62
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      BUILD
  2. 57
      CMakeLists.txt
  3. 4
      Makefile
  4. 6
      bazel/experiments.bzl
  5. 31
      build_autogenerated.yaml
  6. 2
      config.m4
  7. 2
      config.w32
  8. 6
      gRPC-C++.podspec
  9. 9
      gRPC-Core.podspec
  10. 7
      grpc.gemspec
  11. 7
      grpc.gyp
  12. 7
      package.xml
  13. 1
      src/core/ext/filters/channel_idle/channel_idle_filter.h
  14. 1
      src/core/ext/filters/fault_injection/fault_injection_filter.h
  15. 1
      src/core/ext/filters/http/client/http_client_filter.cc
  16. 1
      src/core/ext/filters/http/client/http_client_filter.h
  17. 1
      src/core/ext/filters/http/client_authority_filter.cc
  18. 1
      src/core/ext/filters/http/client_authority_filter.h
  19. 1
      src/core/ext/filters/http/server/http_server_filter.cc
  20. 1
      src/core/ext/filters/http/server/http_server_filter.h
  21. 1
      src/core/ext/filters/load_reporting/server_load_reporting_filter.cc
  22. 1
      src/core/ext/filters/load_reporting/server_load_reporting_filter.h
  23. 1
      src/core/ext/filters/server_config_selector/server_config_selector_filter.cc
  24. 1
      src/core/ext/transport/chttp2/transport/chttp2_transport.cc
  25. 3
      src/core/ext/transport/chttp2/transport/hpack_parser.cc
  26. 16
      src/core/lib/channel/channel_stack.cc
  27. 8
      src/core/lib/channel/channel_stack.h
  28. 10
      src/core/lib/channel/channel_stack_builder.h
  29. 27
      src/core/lib/channel/channel_stack_builder_impl.cc
  30. 2
      src/core/lib/channel/channel_stack_builder_impl.h
  31. 716
      src/core/lib/channel/connected_channel.cc
  32. 4
      src/core/lib/channel/connected_channel.h
  33. 14
      src/core/lib/channel/promise_based_filter.cc
  34. 17
      src/core/lib/channel/promise_based_filter.h
  35. 5
      src/core/lib/event_engine/posix_engine/tcp_socket_utils.h
  36. 4
      src/core/lib/experiments/experiments.cc
  37. 5
      src/core/lib/experiments/experiments.h
  38. 8
      src/core/lib/experiments/experiments.yaml
  39. 48
      src/core/lib/iomgr/closure.h
  40. 4
      src/core/lib/iomgr/socket_utils_posix.h
  41. 16
      src/core/lib/promise/activity.cc
  42. 30
      src/core/lib/promise/activity.h
  43. 14
      src/core/lib/promise/call_push_pull.h
  44. 15
      src/core/lib/promise/pipe.h
  45. 13
      src/core/lib/promise/poll.h
  46. 1
      src/core/lib/security/authorization/grpc_server_authz_filter.cc
  47. 1
      src/core/lib/security/authorization/grpc_server_authz_filter.h
  48. 2
      src/core/lib/security/credentials/call_creds_util.h
  49. 2
      src/core/lib/security/credentials/composite/composite_credentials.cc
  50. 2
      src/core/lib/security/credentials/composite/composite_credentials.h
  51. 1
      src/core/lib/security/credentials/fake/fake_credentials.cc
  52. 2
      src/core/lib/security/credentials/fake/fake_credentials.h
  53. 1
      src/core/lib/security/credentials/iam/iam_credentials.cc
  54. 2
      src/core/lib/security/credentials/iam/iam_credentials.h
  55. 1
      src/core/lib/security/credentials/jwt/jwt_credentials.cc
  56. 2
      src/core/lib/security/credentials/jwt/jwt_credentials.h
  57. 2
      src/core/lib/security/credentials/oauth2/oauth2_credentials.cc
  58. 2
      src/core/lib/security/credentials/oauth2/oauth2_credentials.h
  59. 1
      src/core/lib/security/credentials/plugin/plugin_credentials.cc
  60. 2
      src/core/lib/security/credentials/plugin/plugin_credentials.h
  61. 1
      src/core/lib/security/transport/client_auth_filter.cc
  62. 1497
      src/core/lib/surface/call.cc
  63. 44
      src/core/lib/surface/call.h
  64. 114
      src/core/lib/surface/call_trace.cc
  65. 30
      src/core/lib/surface/call_trace.h
  66. 7
      src/core/lib/surface/channel.cc
  67. 14
      src/core/lib/surface/channel.h
  68. 7
      src/core/lib/surface/lame_client.cc
  69. 1
      src/core/lib/surface/lame_client.h
  70. 45
      src/core/lib/transport/call_fragments.cc
  71. 232
      src/core/lib/transport/call_fragments.h
  72. 18
      src/core/lib/transport/metadata_batch.h
  73. 32
      src/core/lib/transport/transport.cc
  74. 93
      src/core/lib/transport/transport.h
  75. 3
      src/core/lib/transport/transport_impl.h
  76. 2
      src/python/grpcio/grpc_core_dependencies.py
  77. 1
      test/core/end2end/end2end_tests.h
  78. 8
      test/core/end2end/fixtures/h2_http_proxy.cc
  79. 176
      test/core/end2end/fixtures/h2_sockpair_with_minstack.cc
  80. 20
      test/core/end2end/generate_tests.bzl
  81. 4
      test/core/end2end/tests/cancel_after_accept.cc
  82. 4
      test/core/end2end/tests/cancel_after_client_done.cc
  83. 5
      test/core/end2end/tests/cancel_after_invoke.cc
  84. 4
      test/core/end2end/tests/cancel_after_round_trip.cc
  85. 6
      test/core/end2end/tests/cancel_before_invoke.cc
  86. 4
      test/core/end2end/tests/cancel_with_status.cc
  87. 21
      test/core/end2end/tests/request_with_flags.cc
  88. 1
      test/core/end2end/tests/simple_request.cc
  89. 2
      test/core/end2end/tests/streaming_error_response.cc
  90. 13
      test/core/filters/client_auth_filter_test.cc
  91. 12
      test/core/filters/client_authority_filter_test.cc
  92. 42
      test/core/filters/filter_fuzzer.cc
  93. 1
      test/core/promise/activity_test.cc
  94. 8
      test/core/promise/arena_promise_test.cc
  95. 4
      test/core/promise/call_push_pull_test.cc
  96. 1
      test/core/surface/BUILD
  97. 8
      test/core/surface/lame_client_test.cc
  98. 15
      test/core/transport/BUILD
  99. 96
      test/core/transport/call_fragments_test.cc
  100. 5
      tools/doxygen/Doxyfile.c++.internal
  101. Some files were not shown because too many files have changed in this diff Show More

@ -1659,6 +1659,7 @@ grpc_cc_library(
external_deps = [
"absl/base:core_headers",
"absl/status",
"absl/strings:str_format",
"absl/types:optional",
"absl/types:variant",
"absl/utility",
@ -2820,7 +2821,6 @@ grpc_cc_library(
"absl/strings",
"absl/strings:str_format",
"absl/types:optional",
"absl/utility",
],
deps = [
"event_engine_base_hdrs",
@ -3234,6 +3234,7 @@ grpc_cc_library(
"src/core/lib/surface/call.cc",
"src/core/lib/surface/call_details.cc",
"src/core/lib/surface/call_log_batch.cc",
"src/core/lib/surface/call_trace.cc",
"src/core/lib/surface/channel.cc",
"src/core/lib/surface/channel_ping.cc",
"src/core/lib/surface/completion_queue.cc",
@ -3244,6 +3245,7 @@ grpc_cc_library(
"src/core/lib/surface/server.cc",
"src/core/lib/surface/validate_metadata.cc",
"src/core/lib/surface/version.cc",
"src/core/lib/transport/call_fragments.cc",
"src/core/lib/transport/connectivity_state.cc",
"src/core/lib/transport/error_utils.cc",
"src/core/lib/transport/metadata_batch.cc",
@ -3324,6 +3326,7 @@ grpc_cc_library(
"src/core/lib/surface/builtins.h",
"src/core/lib/surface/call.h",
"src/core/lib/surface/call_test_only.h",
"src/core/lib/surface/call_trace.h",
"src/core/lib/surface/channel.h",
"src/core/lib/surface/completion_queue.h",
"src/core/lib/surface/completion_queue_factory.h",
@ -3334,6 +3337,7 @@ grpc_cc_library(
"src/core/lib/surface/validate_metadata.h",
"src/core/lib/transport/connectivity_state.h",
"src/core/lib/transport/metadata_batch.h",
"src/core/lib/transport/call_fragments.h",
"src/core/lib/transport/parsed_metadata.h",
"src/core/lib/transport/status_conversion.h",
"src/core/lib/transport/timeout_encoding.h",
@ -3359,6 +3363,7 @@ grpc_cc_library(
],
external_deps = [
"absl/base:core_headers",
"absl/cleanup",
"absl/container:flat_hash_map",
"absl/container:inlined_vector",
"absl/functional:any_invocable",
@ -3417,11 +3422,13 @@ grpc_cc_library(
"iomgr_timer",
"json",
"latch",
"match",
"memory_quota",
"no_destruct",
"notification",
"orphanable",
"packed_table",
"pipe",
"poll",
"pollset_set",
"promise",

57
CMakeLists.txt generated

@ -865,6 +865,7 @@ if(gRPC_BUILD_TESTS)
add_dependencies(buildtests_cxx byte_buffer_test)
add_dependencies(buildtests_cxx c_slice_buffer_test)
add_dependencies(buildtests_cxx call_finalization_test)
add_dependencies(buildtests_cxx call_fragments_test)
add_dependencies(buildtests_cxx call_push_pull_test)
add_dependencies(buildtests_cxx cancel_ares_query_test)
add_dependencies(buildtests_cxx cel_authorization_engine_test)
@ -2309,6 +2310,7 @@ add_library(grpc
src/core/lib/surface/call.cc
src/core/lib/surface/call_details.cc
src/core/lib/surface/call_log_batch.cc
src/core/lib/surface/call_trace.cc
src/core/lib/surface/channel.cc
src/core/lib/surface/channel_init.cc
src/core/lib/surface/channel_ping.cc
@ -2324,6 +2326,7 @@ add_library(grpc
src/core/lib/surface/validate_metadata.cc
src/core/lib/surface/version.cc
src/core/lib/transport/bdp_estimator.cc
src/core/lib/transport/call_fragments.cc
src/core/lib/transport/connectivity_state.cc
src/core/lib/transport/error_utils.cc
src/core/lib/transport/handshaker.cc
@ -2407,6 +2410,7 @@ target_link_libraries(grpc
${_gRPC_RE2_LIBRARIES}
${_gRPC_UPB_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
absl::cleanup
absl::flat_hash_map
absl::flat_hash_set
absl::inlined_vector
@ -2877,6 +2881,7 @@ add_library(grpc_unsecure
src/core/lib/surface/call.cc
src/core/lib/surface/call_details.cc
src/core/lib/surface/call_log_batch.cc
src/core/lib/surface/call_trace.cc
src/core/lib/surface/channel.cc
src/core/lib/surface/channel_init.cc
src/core/lib/surface/channel_ping.cc
@ -2892,6 +2897,7 @@ add_library(grpc_unsecure
src/core/lib/surface/validate_metadata.cc
src/core/lib/surface/version.cc
src/core/lib/transport/bdp_estimator.cc
src/core/lib/transport/call_fragments.cc
src/core/lib/transport/connectivity_state.cc
src/core/lib/transport/error_utils.cc
src/core/lib/transport/handshaker.cc
@ -2951,6 +2957,7 @@ target_link_libraries(grpc_unsecure
${_gRPC_RE2_LIBRARIES}
${_gRPC_UPB_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
absl::cleanup
absl::flat_hash_map
absl::flat_hash_set
absl::inlined_vector
@ -3207,7 +3214,6 @@ target_link_libraries(grpc++
${_gRPC_BASELIB_LIBRARIES}
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
absl::cleanup
grpc
)
@ -6528,7 +6534,6 @@ target_include_directories(binder_transport_test
target_link_libraries(binder_transport_test
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
absl::cleanup
grpc_test_util
)
@ -6721,6 +6726,41 @@ target_link_libraries(call_finalization_test
)
endif()
if(gRPC_BUILD_TESTS)
add_executable(call_fragments_test
test/core/transport/call_fragments_test.cc
third_party/googletest/googletest/src/gtest-all.cc
third_party/googletest/googlemock/src/gmock-all.cc
)
target_include_directories(call_fragments_test
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/include
${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
${_gRPC_RE2_INCLUDE_DIR}
${_gRPC_SSL_INCLUDE_DIR}
${_gRPC_UPB_GENERATED_DIR}
${_gRPC_UPB_GRPC_GENERATED_DIR}
${_gRPC_UPB_INCLUDE_DIR}
${_gRPC_XXHASH_INCLUDE_DIR}
${_gRPC_ZLIB_INCLUDE_DIR}
third_party/googletest/googletest/include
third_party/googletest/googletest
third_party/googletest/googlemock/include
third_party/googletest/googlemock
${_gRPC_PROTO_GENS_DIR}
)
target_link_libraries(call_fragments_test
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
grpc_test_util
)
endif()
if(gRPC_BUILD_TESTS)
@ -9139,7 +9179,6 @@ target_include_directories(endpoint_binder_pool_test
target_link_libraries(endpoint_binder_pool_test
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
absl::cleanup
grpc_test_util
)
@ -9774,7 +9813,6 @@ target_include_directories(fake_binder_test
target_link_libraries(fake_binder_test
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
absl::cleanup
grpc_test_util
)
@ -17775,7 +17813,6 @@ if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
target_link_libraries(tcp_posix_socket_utils_test
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
absl::cleanup
grpc_test_util
)
@ -18027,7 +18064,6 @@ target_include_directories(test_core_gprpp_load_file_test
target_link_libraries(test_core_gprpp_load_file_test
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
absl::cleanup
grpc_test_util
)
@ -19198,7 +19234,6 @@ target_include_directories(transport_stream_receiver_test
target_link_libraries(transport_stream_receiver_test
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
absl::cleanup
grpc_test_util
)
@ -19745,7 +19780,6 @@ target_include_directories(wire_reader_test
target_link_libraries(wire_reader_test
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
absl::cleanup
grpc_test_util
)
@ -19848,7 +19882,6 @@ target_include_directories(wire_writer_test
target_link_libraries(wire_writer_test
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
absl::cleanup
grpc_test_util
)
@ -22378,7 +22411,7 @@ generate_pkgconfig(
"gRPC"
"high performance general RPC framework"
"${gRPC_CORE_VERSION}"
"gpr openssl absl_any_invocable absl_base absl_bind_front absl_cord absl_core_headers absl_flat_hash_map absl_flat_hash_set absl_function_ref absl_hash absl_inlined_vector absl_memory absl_optional absl_random_random absl_span absl_status absl_statusor absl_str_format absl_strings absl_synchronization absl_time absl_type_traits absl_utility absl_variant"
"gpr openssl absl_any_invocable absl_base absl_bind_front absl_cleanup absl_cord absl_core_headers absl_flat_hash_map absl_flat_hash_set absl_function_ref absl_hash absl_inlined_vector absl_memory absl_optional absl_random_random absl_span absl_status absl_statusor absl_str_format absl_strings absl_synchronization absl_time absl_type_traits absl_utility absl_variant"
"-lgrpc -laddress_sorting -lre2 -lupb -lcares -lz"
""
"grpc.pc")
@ -22388,7 +22421,7 @@ generate_pkgconfig(
"gRPC unsecure"
"high performance general RPC framework without SSL"
"${gRPC_CORE_VERSION}"
"gpr absl_any_invocable absl_base absl_bind_front absl_cord absl_core_headers absl_flat_hash_map absl_flat_hash_set absl_function_ref absl_hash absl_inlined_vector absl_memory absl_optional absl_random_random absl_span absl_status absl_statusor absl_str_format absl_strings absl_synchronization absl_time absl_type_traits absl_utility absl_variant"
"gpr absl_any_invocable absl_base absl_bind_front absl_cleanup absl_cord absl_core_headers absl_flat_hash_map absl_flat_hash_set absl_function_ref absl_hash absl_inlined_vector absl_memory absl_optional absl_random_random absl_span absl_status absl_statusor absl_str_format absl_strings absl_synchronization absl_time absl_type_traits absl_utility absl_variant"
"-lgrpc_unsecure"
""
"grpc_unsecure.pc")
@ -22408,7 +22441,7 @@ generate_pkgconfig(
"gRPC++ unsecure"
"C++ wrapper for gRPC without SSL"
"${gRPC_CPP_VERSION}"
"grpc_unsecure absl_any_invocable absl_base absl_bind_front absl_cord absl_core_headers absl_flat_hash_map absl_flat_hash_set absl_function_ref absl_hash absl_inlined_vector absl_memory absl_optional absl_random_random absl_span absl_status absl_statusor absl_str_format absl_strings absl_synchronization absl_time absl_type_traits absl_utility absl_variant"
"grpc_unsecure absl_any_invocable absl_base absl_bind_front absl_cleanup absl_cord absl_core_headers absl_flat_hash_map absl_flat_hash_set absl_function_ref absl_hash absl_inlined_vector absl_memory absl_optional absl_random_random absl_span absl_status absl_statusor absl_str_format absl_strings absl_synchronization absl_time absl_type_traits absl_utility absl_variant"
"-lgrpc++_unsecure"
""
"grpc++_unsecure.pc")

4
Makefile generated

@ -1586,6 +1586,7 @@ LIBGRPC_SRC = \
src/core/lib/surface/call.cc \
src/core/lib/surface/call_details.cc \
src/core/lib/surface/call_log_batch.cc \
src/core/lib/surface/call_trace.cc \
src/core/lib/surface/channel.cc \
src/core/lib/surface/channel_init.cc \
src/core/lib/surface/channel_ping.cc \
@ -1601,6 +1602,7 @@ LIBGRPC_SRC = \
src/core/lib/surface/validate_metadata.cc \
src/core/lib/surface/version.cc \
src/core/lib/transport/bdp_estimator.cc \
src/core/lib/transport/call_fragments.cc \
src/core/lib/transport/connectivity_state.cc \
src/core/lib/transport/error_utils.cc \
src/core/lib/transport/handshaker.cc \
@ -2018,6 +2020,7 @@ LIBGRPC_UNSECURE_SRC = \
src/core/lib/surface/call.cc \
src/core/lib/surface/call_details.cc \
src/core/lib/surface/call_log_batch.cc \
src/core/lib/surface/call_trace.cc \
src/core/lib/surface/channel.cc \
src/core/lib/surface/channel_init.cc \
src/core/lib/surface/channel_ping.cc \
@ -2033,6 +2036,7 @@ LIBGRPC_UNSECURE_SRC = \
src/core/lib/surface/validate_metadata.cc \
src/core/lib/surface/version.cc \
src/core/lib/transport/bdp_estimator.cc \
src/core/lib/transport/call_fragments.cc \
src/core/lib/transport/connectivity_state.cc \
src/core/lib/transport/error_utils.cc \
src/core/lib/transport/handshaker.cc \

@ -26,6 +26,9 @@ EXPERIMENTS = {
],
},
"off": {
"core_end2end_test": [
"promise_based_client_call",
],
"endpoint_test": [
"tcp_frame_size_tuning",
"tcp_rcv_lowat",
@ -44,6 +47,9 @@ EXPERIMENTS = {
"hpack_test": [
"periodic_resource_quota_reclamation",
],
"lame_client_test": [
"promise_based_client_call",
],
"promise_test": [
"periodic_resource_quota_reclamation",
],

@ -866,6 +866,7 @@ libs:
- src/core/lib/promise/latch.h
- src/core/lib/promise/loop.h
- src/core/lib/promise/map.h
- src/core/lib/promise/pipe.h
- src/core/lib/promise/poll.h
- src/core/lib/promise/promise.h
- src/core/lib/promise/race.h
@ -955,6 +956,7 @@ libs:
- src/core/lib/surface/builtins.h
- src/core/lib/surface/call.h
- src/core/lib/surface/call_test_only.h
- src/core/lib/surface/call_trace.h
- src/core/lib/surface/channel.h
- src/core/lib/surface/channel_init.h
- src/core/lib/surface/channel_stack_type.h
@ -967,6 +969,7 @@ libs:
- src/core/lib/surface/server.h
- src/core/lib/surface/validate_metadata.h
- src/core/lib/transport/bdp_estimator.h
- src/core/lib/transport/call_fragments.h
- src/core/lib/transport/connectivity_state.h
- src/core/lib/transport/error_utils.h
- src/core/lib/transport/handshaker.h
@ -1643,6 +1646,7 @@ libs:
- src/core/lib/surface/call.cc
- src/core/lib/surface/call_details.cc
- src/core/lib/surface/call_log_batch.cc
- src/core/lib/surface/call_trace.cc
- src/core/lib/surface/channel.cc
- src/core/lib/surface/channel_init.cc
- src/core/lib/surface/channel_ping.cc
@ -1658,6 +1662,7 @@ libs:
- src/core/lib/surface/validate_metadata.cc
- src/core/lib/surface/version.cc
- src/core/lib/transport/bdp_estimator.cc
- src/core/lib/transport/call_fragments.cc
- src/core/lib/transport/connectivity_state.cc
- src/core/lib/transport/error_utils.cc
- src/core/lib/transport/handshaker.cc
@ -1703,6 +1708,7 @@ libs:
- src/core/tsi/transport_security.cc
- src/core/tsi/transport_security_grpc.cc
deps:
- absl/cleanup:cleanup
- absl/container:flat_hash_map
- absl/container:flat_hash_set
- absl/container:inlined_vector
@ -2064,6 +2070,7 @@ libs:
- src/core/lib/promise/latch.h
- src/core/lib/promise/loop.h
- src/core/lib/promise/map.h
- src/core/lib/promise/pipe.h
- src/core/lib/promise/poll.h
- src/core/lib/promise/promise.h
- src/core/lib/promise/race.h
@ -2122,6 +2129,7 @@ libs:
- src/core/lib/surface/builtins.h
- src/core/lib/surface/call.h
- src/core/lib/surface/call_test_only.h
- src/core/lib/surface/call_trace.h
- src/core/lib/surface/channel.h
- src/core/lib/surface/channel_init.h
- src/core/lib/surface/channel_stack_type.h
@ -2134,6 +2142,7 @@ libs:
- src/core/lib/surface/server.h
- src/core/lib/surface/validate_metadata.h
- src/core/lib/transport/bdp_estimator.h
- src/core/lib/transport/call_fragments.h
- src/core/lib/transport/connectivity_state.h
- src/core/lib/transport/error_utils.h
- src/core/lib/transport/handshaker.h
@ -2444,6 +2453,7 @@ libs:
- src/core/lib/surface/call.cc
- src/core/lib/surface/call_details.cc
- src/core/lib/surface/call_log_batch.cc
- src/core/lib/surface/call_trace.cc
- src/core/lib/surface/channel.cc
- src/core/lib/surface/channel_init.cc
- src/core/lib/surface/channel_ping.cc
@ -2459,6 +2469,7 @@ libs:
- src/core/lib/surface/validate_metadata.cc
- src/core/lib/surface/version.cc
- src/core/lib/transport/bdp_estimator.cc
- src/core/lib/transport/call_fragments.cc
- src/core/lib/transport/connectivity_state.cc
- src/core/lib/transport/error_utils.cc
- src/core/lib/transport/handshaker.cc
@ -2480,6 +2491,7 @@ libs:
- src/core/tsi/transport_security.cc
- src/core/tsi/transport_security_grpc.cc
deps:
- absl/cleanup:cleanup
- absl/container:flat_hash_map
- absl/container:flat_hash_set
- absl/container:inlined_vector
@ -2839,7 +2851,6 @@ libs:
- src/cpp/util/string_ref.cc
- src/cpp/util/time_cc.cc
deps:
- absl/cleanup:cleanup
- grpc
baselib: true
- name: grpc++_alts
@ -4383,7 +4394,6 @@ targets:
- test/core/transport/binder/binder_transport_test.cc
- test/core/transport/binder/mock_objects.cc
deps:
- absl/cleanup:cleanup
- grpc_test_util
uses_polling: false
- name: bitset_test
@ -4463,6 +4473,16 @@ targets:
- test/core/channel/call_finalization_test.cc
deps:
- grpc_test_util
- name: call_fragments_test
gtest: true
build: test
language: c++
headers:
- test/core/promise/test_context.h
src:
- test/core/transport/call_fragments_test.cc
deps:
- grpc_test_util
- name: call_push_pull_test
gtest: true
build: test
@ -5549,7 +5569,6 @@ targets:
- test/core/transport/binder/endpoint_binder_pool_test.cc
- test/core/transport/binder/mock_objects.cc
deps:
- absl/cleanup:cleanup
- grpc_test_util
uses_polling: false
- name: endpoint_config_test
@ -5965,7 +5984,6 @@ targets:
- test/core/transport/binder/end2end/fake_binder.cc
- test/core/transport/binder/end2end/fake_binder_test.cc
deps:
- absl/cleanup:cleanup
- grpc_test_util
uses_polling: false
- name: fake_resolver_test
@ -9721,7 +9739,6 @@ targets:
- src/core/lib/iomgr/socket_mutator.cc
- test/core/event_engine/posix/tcp_posix_socket_utils_test.cc
deps:
- absl/cleanup:cleanup
- grpc_test_util
platforms:
- linux
@ -9857,7 +9874,6 @@ targets:
- src/core/lib/gprpp/load_file.cc
- test/core/gprpp/load_file_test.cc
deps:
- absl/cleanup:cleanup
- grpc_test_util
uses_polling: false
- name: test_core_gprpp_time_test
@ -10408,7 +10424,6 @@ targets:
- src/cpp/util/time_cc.cc
- test/core/transport/binder/transport_stream_receiver_test.cc
deps:
- absl/cleanup:cleanup
- grpc_test_util
uses_polling: false
- name: try_join_test
@ -10687,7 +10702,6 @@ targets:
- test/core/transport/binder/mock_objects.cc
- test/core/transport/binder/wire_reader_test.cc
deps:
- absl/cleanup:cleanup
- grpc_test_util
uses_polling: false
- name: wire_writer_test
@ -10796,7 +10810,6 @@ targets:
- test/core/transport/binder/mock_objects.cc
- test/core/transport/binder/wire_writer_test.cc
deps:
- absl/cleanup:cleanup
- grpc_test_util
uses_polling: false
- name: work_queue_test

2
config.m4 generated

@ -710,6 +710,7 @@ if test "$PHP_GRPC" != "no"; then
src/core/lib/surface/call.cc \
src/core/lib/surface/call_details.cc \
src/core/lib/surface/call_log_batch.cc \
src/core/lib/surface/call_trace.cc \
src/core/lib/surface/channel.cc \
src/core/lib/surface/channel_init.cc \
src/core/lib/surface/channel_ping.cc \
@ -725,6 +726,7 @@ if test "$PHP_GRPC" != "no"; then
src/core/lib/surface/validate_metadata.cc \
src/core/lib/surface/version.cc \
src/core/lib/transport/bdp_estimator.cc \
src/core/lib/transport/call_fragments.cc \
src/core/lib/transport/connectivity_state.cc \
src/core/lib/transport/error_utils.cc \
src/core/lib/transport/handshaker.cc \

2
config.w32 generated

@ -676,6 +676,7 @@ if (PHP_GRPC != "no") {
"src\\core\\lib\\surface\\call.cc " +
"src\\core\\lib\\surface\\call_details.cc " +
"src\\core\\lib\\surface\\call_log_batch.cc " +
"src\\core\\lib\\surface\\call_trace.cc " +
"src\\core\\lib\\surface\\channel.cc " +
"src\\core\\lib\\surface\\channel_init.cc " +
"src\\core\\lib\\surface\\channel_ping.cc " +
@ -691,6 +692,7 @@ if (PHP_GRPC != "no") {
"src\\core\\lib\\surface\\validate_metadata.cc " +
"src\\core\\lib\\surface\\version.cc " +
"src\\core\\lib\\transport\\bdp_estimator.cc " +
"src\\core\\lib\\transport\\call_fragments.cc " +
"src\\core\\lib\\transport\\connectivity_state.cc " +
"src\\core\\lib\\transport\\error_utils.cc " +
"src\\core\\lib\\transport\\handshaker.cc " +

6
gRPC-C++.podspec generated

@ -840,6 +840,7 @@ Pod::Spec.new do |s|
'src/core/lib/promise/latch.h',
'src/core/lib/promise/loop.h',
'src/core/lib/promise/map.h',
'src/core/lib/promise/pipe.h',
'src/core/lib/promise/poll.h',
'src/core/lib/promise/promise.h',
'src/core/lib/promise/race.h',
@ -929,6 +930,7 @@ Pod::Spec.new do |s|
'src/core/lib/surface/builtins.h',
'src/core/lib/surface/call.h',
'src/core/lib/surface/call_test_only.h',
'src/core/lib/surface/call_trace.h',
'src/core/lib/surface/channel.h',
'src/core/lib/surface/channel_init.h',
'src/core/lib/surface/channel_stack_type.h',
@ -941,6 +943,7 @@ Pod::Spec.new do |s|
'src/core/lib/surface/server.h',
'src/core/lib/surface/validate_metadata.h',
'src/core/lib/transport/bdp_estimator.h',
'src/core/lib/transport/call_fragments.h',
'src/core/lib/transport/connectivity_state.h',
'src/core/lib/transport/error_utils.h',
'src/core/lib/transport/handshaker.h',
@ -1703,6 +1706,7 @@ Pod::Spec.new do |s|
'src/core/lib/promise/latch.h',
'src/core/lib/promise/loop.h',
'src/core/lib/promise/map.h',
'src/core/lib/promise/pipe.h',
'src/core/lib/promise/poll.h',
'src/core/lib/promise/promise.h',
'src/core/lib/promise/race.h',
@ -1792,6 +1796,7 @@ Pod::Spec.new do |s|
'src/core/lib/surface/builtins.h',
'src/core/lib/surface/call.h',
'src/core/lib/surface/call_test_only.h',
'src/core/lib/surface/call_trace.h',
'src/core/lib/surface/channel.h',
'src/core/lib/surface/channel_init.h',
'src/core/lib/surface/channel_stack_type.h',
@ -1804,6 +1809,7 @@ Pod::Spec.new do |s|
'src/core/lib/surface/server.h',
'src/core/lib/surface/validate_metadata.h',
'src/core/lib/transport/bdp_estimator.h',
'src/core/lib/transport/call_fragments.h',
'src/core/lib/transport/connectivity_state.h',
'src/core/lib/transport/error_utils.h',
'src/core/lib/transport/handshaker.h',

9
gRPC-Core.podspec generated

@ -175,6 +175,7 @@ Pod::Spec.new do |s|
ss.dependency 'BoringSSL-GRPC', '0.0.24'
ss.dependency 'abseil/base/base', abseil_version
ss.dependency 'abseil/base/core_headers', abseil_version
ss.dependency 'abseil/cleanup/cleanup', abseil_version
ss.dependency 'abseil/container/flat_hash_map', abseil_version
ss.dependency 'abseil/container/flat_hash_set', abseil_version
ss.dependency 'abseil/container/inlined_vector', abseil_version
@ -1359,6 +1360,7 @@ Pod::Spec.new do |s|
'src/core/lib/promise/latch.h',
'src/core/lib/promise/loop.h',
'src/core/lib/promise/map.h',
'src/core/lib/promise/pipe.h',
'src/core/lib/promise/poll.h',
'src/core/lib/promise/promise.h',
'src/core/lib/promise/race.h',
@ -1535,6 +1537,8 @@ Pod::Spec.new do |s|
'src/core/lib/surface/call_details.cc',
'src/core/lib/surface/call_log_batch.cc',
'src/core/lib/surface/call_test_only.h',
'src/core/lib/surface/call_trace.cc',
'src/core/lib/surface/call_trace.h',
'src/core/lib/surface/channel.cc',
'src/core/lib/surface/channel.h',
'src/core/lib/surface/channel_init.cc',
@ -1562,6 +1566,8 @@ Pod::Spec.new do |s|
'src/core/lib/surface/version.cc',
'src/core/lib/transport/bdp_estimator.cc',
'src/core/lib/transport/bdp_estimator.h',
'src/core/lib/transport/call_fragments.cc',
'src/core/lib/transport/call_fragments.h',
'src/core/lib/transport/connectivity_state.cc',
'src/core/lib/transport/connectivity_state.h',
'src/core/lib/transport/error_utils.cc',
@ -2330,6 +2336,7 @@ Pod::Spec.new do |s|
'src/core/lib/promise/latch.h',
'src/core/lib/promise/loop.h',
'src/core/lib/promise/map.h',
'src/core/lib/promise/pipe.h',
'src/core/lib/promise/poll.h',
'src/core/lib/promise/promise.h',
'src/core/lib/promise/race.h',
@ -2419,6 +2426,7 @@ Pod::Spec.new do |s|
'src/core/lib/surface/builtins.h',
'src/core/lib/surface/call.h',
'src/core/lib/surface/call_test_only.h',
'src/core/lib/surface/call_trace.h',
'src/core/lib/surface/channel.h',
'src/core/lib/surface/channel_init.h',
'src/core/lib/surface/channel_stack_type.h',
@ -2431,6 +2439,7 @@ Pod::Spec.new do |s|
'src/core/lib/surface/server.h',
'src/core/lib/surface/validate_metadata.h',
'src/core/lib/transport/bdp_estimator.h',
'src/core/lib/transport/call_fragments.h',
'src/core/lib/transport/connectivity_state.h',
'src/core/lib/transport/error_utils.h',
'src/core/lib/transport/handshaker.h',

7
grpc.gemspec generated

@ -1271,6 +1271,7 @@ Gem::Specification.new do |s|
s.files += %w( src/core/lib/promise/latch.h )
s.files += %w( src/core/lib/promise/loop.h )
s.files += %w( src/core/lib/promise/map.h )
s.files += %w( src/core/lib/promise/pipe.h )
s.files += %w( src/core/lib/promise/poll.h )
s.files += %w( src/core/lib/promise/promise.h )
s.files += %w( src/core/lib/promise/race.h )
@ -1447,6 +1448,8 @@ Gem::Specification.new do |s|
s.files += %w( src/core/lib/surface/call_details.cc )
s.files += %w( src/core/lib/surface/call_log_batch.cc )
s.files += %w( src/core/lib/surface/call_test_only.h )
s.files += %w( src/core/lib/surface/call_trace.cc )
s.files += %w( src/core/lib/surface/call_trace.h )
s.files += %w( src/core/lib/surface/channel.cc )
s.files += %w( src/core/lib/surface/channel.h )
s.files += %w( src/core/lib/surface/channel_init.cc )
@ -1474,6 +1477,8 @@ Gem::Specification.new do |s|
s.files += %w( src/core/lib/surface/version.cc )
s.files += %w( src/core/lib/transport/bdp_estimator.cc )
s.files += %w( src/core/lib/transport/bdp_estimator.h )
s.files += %w( src/core/lib/transport/call_fragments.cc )
s.files += %w( src/core/lib/transport/call_fragments.h )
s.files += %w( src/core/lib/transport/connectivity_state.cc )
s.files += %w( src/core/lib/transport/connectivity_state.h )
s.files += %w( src/core/lib/transport/error_utils.cc )
@ -1620,6 +1625,8 @@ Gem::Specification.new do |s|
s.files += %w( third_party/abseil-cpp/absl/base/policy_checks.h )
s.files += %w( third_party/abseil-cpp/absl/base/port.h )
s.files += %w( third_party/abseil-cpp/absl/base/thread_annotations.h )
s.files += %w( third_party/abseil-cpp/absl/cleanup/cleanup.h )
s.files += %w( third_party/abseil-cpp/absl/cleanup/internal/cleanup.h )
s.files += %w( third_party/abseil-cpp/absl/container/fixed_array.h )
s.files += %w( third_party/abseil-cpp/absl/container/flat_hash_map.h )
s.files += %w( third_party/abseil-cpp/absl/container/flat_hash_set.h )

7
grpc.gyp generated

@ -355,6 +355,7 @@
'target_name': 'grpc',
'type': 'static_library',
'dependencies': [
'absl/cleanup:cleanup',
'absl/container:flat_hash_map',
'absl/container:flat_hash_set',
'absl/container:inlined_vector',
@ -1000,6 +1001,7 @@
'src/core/lib/surface/call.cc',
'src/core/lib/surface/call_details.cc',
'src/core/lib/surface/call_log_batch.cc',
'src/core/lib/surface/call_trace.cc',
'src/core/lib/surface/channel.cc',
'src/core/lib/surface/channel_init.cc',
'src/core/lib/surface/channel_ping.cc',
@ -1015,6 +1017,7 @@
'src/core/lib/surface/validate_metadata.cc',
'src/core/lib/surface/version.cc',
'src/core/lib/transport/bdp_estimator.cc',
'src/core/lib/transport/call_fragments.cc',
'src/core/lib/transport/connectivity_state.cc',
'src/core/lib/transport/error_utils.cc',
'src/core/lib/transport/handshaker.cc',
@ -1108,6 +1111,7 @@
'target_name': 'grpc_unsecure',
'type': 'static_library',
'dependencies': [
'absl/cleanup:cleanup',
'absl/container:flat_hash_map',
'absl/container:flat_hash_set',
'absl/container:inlined_vector',
@ -1410,6 +1414,7 @@
'src/core/lib/surface/call.cc',
'src/core/lib/surface/call_details.cc',
'src/core/lib/surface/call_log_batch.cc',
'src/core/lib/surface/call_trace.cc',
'src/core/lib/surface/channel.cc',
'src/core/lib/surface/channel_init.cc',
'src/core/lib/surface/channel_ping.cc',
@ -1425,6 +1430,7 @@
'src/core/lib/surface/validate_metadata.cc',
'src/core/lib/surface/version.cc',
'src/core/lib/transport/bdp_estimator.cc',
'src/core/lib/transport/call_fragments.cc',
'src/core/lib/transport/connectivity_state.cc',
'src/core/lib/transport/error_utils.cc',
'src/core/lib/transport/handshaker.cc',
@ -1480,7 +1486,6 @@
'target_name': 'grpc++',
'type': 'static_library',
'dependencies': [
'absl/cleanup:cleanup',
'grpc',
],
'sources': [

7
package.xml generated

@ -1253,6 +1253,7 @@
<file baseinstalldir="/" name="src/core/lib/promise/latch.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/loop.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/map.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/pipe.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/poll.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/promise.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/race.h" role="src" />
@ -1429,6 +1430,8 @@
<file baseinstalldir="/" name="src/core/lib/surface/call_details.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/surface/call_log_batch.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/surface/call_test_only.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/surface/call_trace.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/surface/call_trace.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/surface/channel.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/surface/channel.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/surface/channel_init.cc" role="src" />
@ -1456,6 +1459,8 @@
<file baseinstalldir="/" name="src/core/lib/surface/version.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/transport/bdp_estimator.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/transport/bdp_estimator.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/transport/call_fragments.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/transport/call_fragments.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/transport/connectivity_state.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/transport/connectivity_state.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/transport/error_utils.cc" role="src" />
@ -1624,6 +1629,8 @@
<file baseinstalldir="/" name="third_party/abseil-cpp/absl/base/policy_checks.h" role="src" />
<file baseinstalldir="/" name="third_party/abseil-cpp/absl/base/port.h" role="src" />
<file baseinstalldir="/" name="third_party/abseil-cpp/absl/base/thread_annotations.h" role="src" />
<file baseinstalldir="/" name="third_party/abseil-cpp/absl/cleanup/cleanup.h" role="src" />
<file baseinstalldir="/" name="third_party/abseil-cpp/absl/cleanup/internal/cleanup.h" role="src" />
<file baseinstalldir="/" name="third_party/abseil-cpp/absl/container/fixed_array.h" role="src" />
<file baseinstalldir="/" name="third_party/abseil-cpp/absl/container/flat_hash_map.h" role="src" />
<file baseinstalldir="/" name="third_party/abseil-cpp/absl/container/flat_hash_set.h" role="src" />

@ -35,6 +35,7 @@
#include "src/core/lib/gprpp/time.h"
#include "src/core/lib/promise/activity.h"
#include "src/core/lib/promise/arena_promise.h"
#include "src/core/lib/transport/call_fragments.h"
#include "src/core/lib/transport/connectivity_state.h"
#include "src/core/lib/transport/transport.h"

@ -32,6 +32,7 @@
#include "src/core/lib/channel/promise_based_filter.h"
#include "src/core/lib/gprpp/sync.h"
#include "src/core/lib/promise/arena_promise.h"
#include "src/core/lib/transport/call_fragments.h"
#include "src/core/lib/transport/transport.h"
// Channel arg key for enabling parsing fault injection via method config.

@ -45,6 +45,7 @@
#include "src/core/lib/promise/seq.h"
#include "src/core/lib/resource_quota/arena.h"
#include "src/core/lib/slice/percent_encoding.h"
#include "src/core/lib/transport/call_fragments.h"
#include "src/core/lib/transport/status_conversion.h"
#include "src/core/lib/transport/transport_fwd.h"
#include "src/core/lib/transport/transport_impl.h"

@ -27,6 +27,7 @@
#include "src/core/lib/channel/promise_based_filter.h"
#include "src/core/lib/promise/arena_promise.h"
#include "src/core/lib/slice/slice.h"
#include "src/core/lib/transport/call_fragments.h"
#include "src/core/lib/transport/metadata_batch.h"
#include "src/core/lib/transport/transport.h"

@ -35,6 +35,7 @@
#include "src/core/lib/config/core_configuration.h"
#include "src/core/lib/surface/channel_init.h"
#include "src/core/lib/surface/channel_stack_type.h"
#include "src/core/lib/transport/call_fragments.h"
#include "src/core/lib/transport/metadata_batch.h"
namespace grpc_core {

@ -30,6 +30,7 @@
#include "src/core/lib/channel/promise_based_filter.h"
#include "src/core/lib/promise/arena_promise.h"
#include "src/core/lib/slice/slice.h"
#include "src/core/lib/transport/call_fragments.h"
#include "src/core/lib/transport/transport.h"
namespace grpc_core {

@ -40,6 +40,7 @@
#include "src/core/lib/resource_quota/arena.h"
#include "src/core/lib/slice/percent_encoding.h"
#include "src/core/lib/slice/slice.h"
#include "src/core/lib/transport/call_fragments.h"
#include "src/core/lib/transport/metadata_batch.h"
namespace grpc_core {

@ -27,6 +27,7 @@
#include "src/core/lib/channel/channel_fwd.h"
#include "src/core/lib/channel/promise_based_filter.h"
#include "src/core/lib/promise/arena_promise.h"
#include "src/core/lib/transport/call_fragments.h"
#include "src/core/lib/transport/transport.h"
namespace grpc_core {

@ -63,6 +63,7 @@
#include "src/core/lib/slice/slice.h"
#include "src/core/lib/surface/channel_init.h"
#include "src/core/lib/surface/channel_stack_type.h"
#include "src/core/lib/transport/call_fragments.h"
#include "src/core/lib/transport/metadata_batch.h"
#include "src/core/lib/uri/uri_parser.h"
#include "src/cpp/server/load_reporter/constants.h"

@ -30,6 +30,7 @@
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/channel/promise_based_filter.h"
#include "src/core/lib/promise/arena_promise.h"
#include "src/core/lib/transport/call_fragments.h"
#include "src/core/lib/transport/transport.h"
namespace grpc_core {

@ -39,6 +39,7 @@
#include "src/core/lib/promise/promise.h"
#include "src/core/lib/resource_quota/arena.h"
#include "src/core/lib/service_config/service_config_call_data.h"
#include "src/core/lib/transport/call_fragments.h"
#include "src/core/lib/transport/transport.h"
namespace grpc_core {

@ -1175,6 +1175,7 @@ static bool contains_non_ok_status(grpc_metadata_batch* batch) {
static void log_metadata(const grpc_metadata_batch* md_batch, uint32_t id,
bool is_client, bool is_initial) {
gpr_log(GPR_INFO, "--metadata--");
const std::string prefix = absl::StrCat(
"HTTP:", id, is_initial ? ":HDR" : ":TRL", is_client ? ":CLI:" : ":SVR:");
md_batch->Log([&prefix](absl::string_view key, absl::string_view value) {

@ -1258,6 +1258,9 @@ void HPackParser::BeginFrame(grpc_metadata_batch* metadata_buffer,
uint32_t metadata_size_limit, Boundary boundary,
Priority priority, LogInfo log_info) {
metadata_buffer_ = metadata_buffer;
if (metadata_buffer != nullptr) {
metadata_buffer->Set(GrpcStatusFromWire(), true);
}
boundary_ = boundary;
priority_ = priority;
dynamic_table_updates_allowed_ = 2;

@ -299,12 +299,12 @@ grpc_core::NextPromiseFactory ServerNext(grpc_channel_element* elem) {
} // namespace
grpc_core::ArenaPromise<grpc_core::ServerMetadataHandle>
grpc_channel_stack::MakeCallPromise(grpc_core::CallArgs call_args) {
if (is_client) {
return ClientNext(grpc_channel_stack_element(this, 0))(
std::move(call_args));
} else {
return ServerNext(grpc_channel_stack_element(this, this->count - 1))(
std::move(call_args));
}
grpc_channel_stack::MakeClientCallPromise(grpc_core::CallArgs call_args) {
return ClientNext(grpc_channel_stack_element(this, 0))(std::move(call_args));
}
grpc_core::ArenaPromise<grpc_core::ServerMetadataHandle>
grpc_channel_stack::MakeServerCallPromise(grpc_core::CallArgs call_args) {
return ServerNext(grpc_channel_stack_element(this, this->count - 1))(
std::move(call_args));
}

@ -70,6 +70,7 @@
#include "src/core/lib/iomgr/polling_entity.h"
#include "src/core/lib/promise/arena_promise.h"
#include "src/core/lib/resource_quota/arena.h"
#include "src/core/lib/transport/call_fragments.h"
#include "src/core/lib/transport/transport.h"
struct grpc_channel_element_args {
@ -202,7 +203,6 @@ struct grpc_call_element {
guarantees they live within a single malloc() allocation */
struct grpc_channel_stack {
grpc_stream_refcount refcount;
bool is_client;
size_t count;
/* Memory required for a call stack (computed at channel stack
initialization) */
@ -225,8 +225,10 @@ struct grpc_channel_stack {
return grpc_core::RefCountedPtr<grpc_channel_stack>(this);
}
grpc_core::ArenaPromise<grpc_core::ServerMetadataHandle> MakeCallPromise(
grpc_core::CallArgs call_args);
grpc_core::ArenaPromise<grpc_core::ServerMetadataHandle>
MakeClientCallPromise(grpc_core::CallArgs call_args);
grpc_core::ArenaPromise<grpc_core::ServerMetadataHandle>
MakeServerCallPromise(grpc_core::CallArgs call_args);
};
/* A call stack tracks a set of related filters for one call, and guarantees

@ -71,6 +71,11 @@ class ChannelStackBuilder {
// Mutable vector of proposed stack entries.
std::vector<const grpc_channel_filter*>* mutable_stack() { return &stack_; }
// Immutable vector of proposed stack entries.
const std::vector<const grpc_channel_filter*>& stack() const {
return stack_;
}
// The type of channel stack being built.
grpc_channel_stack_type channel_stack_type() const { return type_; }
@ -80,6 +85,11 @@ class ChannelStackBuilder {
// Helper to add a filter to the end of the stack.
void AppendFilter(const grpc_channel_filter* filter);
// Determine whether a promise-based call stack is able to be built.
// Iterates each filter and ensures that there's a promise factory there.
// This will go away once the promise conversion is completed.
virtual bool IsPromising() const = 0;
// Build the channel stack.
// After success, *result holds the new channel stack,
// prefix_bytes are allocated before the channel stack,

@ -22,6 +22,7 @@
#include <string.h>
#include <algorithm>
#include <vector>
#include "absl/status/status.h"
@ -30,21 +31,39 @@
#include <grpc/support/alloc.h>
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/channel/channel_fwd.h"
#include "src/core/lib/channel/channel_stack.h"
#include "src/core/lib/debug/trace.h"
#include "src/core/lib/gpr/useful.h"
#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/surface/call_trace.h"
#include "src/core/lib/transport/error_utils.h"
#include "src/core/lib/transport/transport.h"
namespace grpc_core {
bool ChannelStackBuilderImpl::IsPromising() const {
for (const auto* filter : stack()) {
if (filter->make_call_promise == nullptr) return false;
}
return true;
}
absl::StatusOr<RefCountedPtr<grpc_channel_stack>>
ChannelStackBuilderImpl::Build() {
auto* stack = mutable_stack();
std::vector<const grpc_channel_filter*> stack;
const bool is_promising = IsPromising();
for (const auto* filter : this->stack()) {
if (is_promising && grpc_call_trace.enabled()) {
stack.push_back(PromiseTracingFilterFor(filter));
}
stack.push_back(filter);
}
// calculate the size of the channel stack
size_t channel_stack_size =
grpc_channel_stack_size(stack->data(), stack->size());
grpc_channel_stack_size(stack.data(), stack.size());
// allocate memory
auto* channel_stack =
@ -72,7 +91,7 @@ ChannelStackBuilderImpl::Build() {
grpc_channel_stack_destroy(stk);
gpr_free(stk);
},
channel_stack, stack->data(), stack->size(), final_args, name(),
channel_stack, stack.data(), stack.size(), final_args, name(),
channel_stack);
if (!error.ok()) {
@ -83,7 +102,7 @@ ChannelStackBuilderImpl::Build() {
}
// run post-initialization functions
for (size_t i = 0; i < stack->size(); i++) {
for (size_t i = 0; i < stack.size(); i++) {
auto* elem = grpc_channel_stack_element(channel_stack, i);
elem->filter->post_init_channel_elem(channel_stack, elem);
}

@ -34,6 +34,8 @@ class ChannelStackBuilderImpl final : public ChannelStackBuilder {
public:
using ChannelStackBuilder::ChannelStackBuilder;
bool IsPromising() const override;
// Build the channel stack.
// After success, *result holds the new channel stack,
// prefix_bytes are allocated before the channel stack,

@ -20,21 +20,57 @@
#include "src/core/lib/channel/connected_channel.h"
#include <inttypes.h>
#include <string.h>
#include <algorithm>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/base/thread_annotations.h"
#include "absl/status/status.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_join.h"
#include "absl/types/optional.h"
#include "absl/types/variant.h"
#include <grpc/impl/codegen/grpc_types.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/channel/channel_fwd.h"
#include "src/core/lib/channel/channel_stack.h"
#include "src/core/lib/channel/context.h"
#include "src/core/lib/debug/trace.h"
#include "src/core/lib/gpr/alloc.h"
#include "src/core/lib/gprpp/debug_location.h"
#include "src/core/lib/gprpp/match.h"
#include "src/core/lib/gprpp/orphanable.h"
#include "src/core/lib/gprpp/sync.h"
#include "src/core/lib/iomgr/call_combiner.h"
#include "src/core/lib/iomgr/closure.h"
#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/iomgr/exec_ctx.h"
#include "src/core/lib/iomgr/polling_entity.h"
#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/latch.h"
#include "src/core/lib/promise/pipe.h"
#include "src/core/lib/promise/poll.h"
#include "src/core/lib/resource_quota/arena.h"
#include "src/core/lib/slice/slice_buffer.h"
#include "src/core/lib/surface/call.h"
#include "src/core/lib/surface/call_trace.h"
#include "src/core/lib/surface/channel_stack_type.h"
#include "src/core/lib/transport/call_fragments.h"
#include "src/core/lib/transport/metadata_batch.h"
#include "src/core/lib/transport/transport.h"
#include "src/core/lib/transport/transport_fwd.h"
#include "src/core/lib/transport/transport_impl.h"
#define MAX_BUFFER_LENGTH 8192
@ -205,39 +241,661 @@ static void connected_channel_get_channel_info(
grpc_channel_element* /*elem*/, const grpc_channel_info* /*channel_info*/) {
}
const grpc_channel_filter grpc_connected_filter = {
connected_channel_start_transport_stream_op_batch,
nullptr,
connected_channel_start_transport_op,
sizeof(call_data),
connected_channel_init_call_elem,
set_pollset_or_pollset_set,
connected_channel_destroy_call_elem,
sizeof(channel_data),
connected_channel_init_channel_elem,
[](grpc_channel_stack* channel_stack, grpc_channel_element* elem) {
/* HACK(ctiller): increase call stack size for the channel to make space
for channel data. We need a cleaner (but performant) way to do this,
and I'm not sure what that is yet.
This is only "safe" because call stacks place no additional data after
the last call element, and the last call element MUST be the connected
channel. */
channel_stack->call_stack_size += grpc_transport_stream_size(
static_cast<channel_data*>(elem->channel_data)->transport);
},
connected_channel_destroy_channel_elem,
connected_channel_get_channel_info,
"connected",
namespace grpc_core {
namespace {
class ClientStream : public Orphanable {
public:
ClientStream(grpc_transport* transport, CallArgs call_args)
: transport_(transport),
stream_(nullptr, StreamDeleter(this)),
server_initial_metadata_latch_(call_args.server_initial_metadata),
client_to_server_messages_(call_args.outgoing_messages),
server_to_client_messages_(call_args.incoming_messages),
client_initial_metadata_(std::move(call_args.client_initial_metadata)) {
call_context_->IncrementRefCount("client_stream");
GRPC_STREAM_REF_INIT(
&stream_refcount_, 1,
[](void* p, grpc_error_handle) {
static_cast<ClientStream*>(p)->BeginDestroy();
},
this, "client_stream");
if (grpc_call_trace.enabled()) {
gpr_log(GPR_INFO, "%sInitImpl: intitial_metadata=%s",
Activity::current()->DebugTag().c_str(),
client_initial_metadata_->DebugString().c_str());
}
}
void Orphan() override {
bool finished;
{
MutexLock lock(&mu_);
if (grpc_call_trace.enabled()) {
gpr_log(GPR_INFO, "%sDropStream: %s",
Activity::current()->DebugTag().c_str(),
ActiveOpsString().c_str());
}
finished = finished_;
}
// If we hadn't already observed the stream to be finished, we need to
// cancel it at the transport.
if (!finished) {
IncrementRefCount("shutdown client stream");
auto* cancel_op =
GetContext<Arena>()->New<grpc_transport_stream_op_batch>();
cancel_op->cancel_stream = true;
cancel_op->payload = &batch_payload_;
auto* stream = stream_.get();
cancel_op->on_complete = NewClosure(
[this](grpc_error_handle) { Unref("shutdown client stream"); });
batch_payload_.cancel_stream.cancel_error = GRPC_ERROR_CANCELLED;
grpc_transport_perform_stream_op(transport_, stream, cancel_op);
}
Unref("orphan client stream");
}
void IncrementRefCount(const char* reason) {
#ifndef NDEBUG
grpc_stream_ref(&stream_refcount_, reason);
#else
(void)reason;
grpc_stream_ref(&stream_refcount_);
#endif
}
void Unref(const char* reason) {
#ifndef NDEBUG
grpc_stream_unref(&stream_refcount_, reason);
#else
(void)reason;
grpc_stream_unref(&stream_refcount_);
#endif
}
void BeginDestroy() {
if (stream_ != nullptr) {
stream_.reset();
} else {
StreamDestroyed();
}
}
Poll<ServerMetadataHandle> PollOnce() {
MutexLock lock(&mu_);
GPR_ASSERT(!finished_);
if (grpc_call_trace.enabled()) {
gpr_log(GPR_INFO, "%sPollConnectedChannel: %s",
Activity::current()->DebugTag().c_str(),
ActiveOpsString().c_str());
}
auto push_recv_message = [this]() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_) {
recv_message_state_ = PendingReceiveMessage{};
auto& pending_recv_message =
absl::get<PendingReceiveMessage>(recv_message_state_);
memset(&recv_message_, 0, sizeof(recv_message_));
recv_message_.payload = &batch_payload_;
recv_message_.on_complete = nullptr;
recv_message_.recv_message = true;
batch_payload_.recv_message.recv_message = &pending_recv_message.payload;
batch_payload_.recv_message.flags = &pending_recv_message.flags;
batch_payload_.recv_message.call_failed_before_recv_message = nullptr;
batch_payload_.recv_message.recv_message_ready =
&recv_message_batch_done_;
IncrementRefCount("recv_message");
recv_message_waker_ = Activity::current()->MakeOwningWaker();
push_recv_message_ = true;
SchedulePush();
};
if (!std::exchange(requested_metadata_, true)) {
if (grpc_call_trace.enabled()) {
gpr_log(GPR_INFO, "%sPollConnectedChannel: requesting metadata",
Activity::current()->DebugTag().c_str());
}
stream_.reset(static_cast<grpc_stream*>(
GetContext<Arena>()->Alloc(transport_->vtable->sizeof_stream)));
grpc_transport_init_stream(transport_, stream_.get(), &stream_refcount_,
nullptr, GetContext<Arena>());
grpc_transport_set_pops(transport_, stream_.get(),
GetContext<CallContext>()->polling_entity());
memset(&metadata_, 0, sizeof(metadata_));
metadata_.send_initial_metadata = true;
metadata_.recv_initial_metadata = true;
metadata_.recv_trailing_metadata = true;
metadata_.payload = &batch_payload_;
metadata_.on_complete = &metadata_batch_done_;
batch_payload_.send_initial_metadata.send_initial_metadata =
client_initial_metadata_.get();
batch_payload_.send_initial_metadata.peer_string =
GetContext<CallContext>()->peer_string_atm_ptr();
server_initial_metadata_ =
GetContext<FragmentAllocator>()->MakeServerMetadata();
batch_payload_.recv_initial_metadata.recv_initial_metadata =
server_initial_metadata_.get();
batch_payload_.recv_initial_metadata.recv_initial_metadata_ready =
&recv_initial_metadata_ready_;
batch_payload_.recv_initial_metadata.trailing_metadata_available =
nullptr;
batch_payload_.recv_initial_metadata.peer_string = nullptr;
server_trailing_metadata_ =
GetContext<FragmentAllocator>()->MakeClientMetadata();
batch_payload_.recv_trailing_metadata.recv_trailing_metadata =
server_trailing_metadata_.get();
batch_payload_.recv_trailing_metadata.collect_stats =
&GetContext<CallContext>()->call_stats()->transport_stream_stats;
batch_payload_.recv_trailing_metadata.recv_trailing_metadata_ready =
&recv_trailing_metadata_ready_;
push_metadata_ = true;
IncrementRefCount("metadata_batch_done");
IncrementRefCount("initial_metadata_ready");
IncrementRefCount("trailing_metadata_ready");
initial_metadata_waker_ = Activity::current()->MakeOwningWaker();
trailing_metadata_waker_ = Activity::current()->MakeOwningWaker();
SchedulePush();
}
if (absl::holds_alternative<Closed>(send_message_state_)) {
message_to_send_.reset();
}
if (absl::holds_alternative<Idle>(send_message_state_)) {
message_to_send_.reset();
send_message_state_ = client_to_server_messages_->Next();
}
if (auto* next = absl::get_if<PipeReceiver<MessageHandle>::NextType>(
&send_message_state_)) {
auto r = (*next)();
if (auto* p = absl::get_if<NextResult<MessageHandle>>(&r)) {
memset(&send_message_, 0, sizeof(send_message_));
send_message_.payload = &batch_payload_;
send_message_.on_complete = &send_message_batch_done_;
// No value => half close from above.
if (p->has_value()) {
message_to_send_ = std::move(**p);
send_message_state_ = SendMessageToTransport{};
send_message_.send_message = true;
batch_payload_.send_message.send_message =
message_to_send_->payload();
batch_payload_.send_message.flags = message_to_send_->flags();
} else {
GPR_ASSERT(!absl::holds_alternative<Closed>(send_message_state_));
client_trailing_metadata_ =
GetContext<FragmentAllocator>()->MakeClientMetadata();
send_message_state_ = Closed{};
send_message_.send_trailing_metadata = true;
batch_payload_.send_trailing_metadata.send_trailing_metadata =
client_trailing_metadata_.get();
batch_payload_.send_trailing_metadata.sent = nullptr;
}
IncrementRefCount("send_message");
send_message_waker_ = Activity::current()->MakeOwningWaker();
push_send_message_ = true;
SchedulePush();
}
}
if (auto* pending =
absl::get_if<PendingReceiveMessage>(&recv_message_state_)) {
if (pending->received) {
if (pending->payload.has_value()) {
if (grpc_call_trace.enabled()) {
gpr_log(GPR_INFO,
"%sRecvMessageBatchDone: received payload of %" PRIdPTR
" bytes",
recv_message_waker_.ActivityDebugTag().c_str(),
pending->payload->Length());
}
recv_message_state_ = server_to_client_messages_->Push(
GetContext<FragmentAllocator>()->MakeMessage(
std::move(*pending->payload), pending->flags));
} else {
if (grpc_call_trace.enabled()) {
gpr_log(GPR_INFO, "%sRecvMessageBatchDone: received no payload",
recv_message_waker_.ActivityDebugTag().c_str());
}
recv_message_state_ = Closed{};
std::exchange(server_to_client_messages_, nullptr)->Close();
}
}
}
if (server_initial_metadata_state_ ==
ServerInitialMetadataState::kReceivedButNotSet) {
server_initial_metadata_state_ = ServerInitialMetadataState::kSet;
server_initial_metadata_latch_->Set(server_initial_metadata_.get());
}
if (absl::holds_alternative<Idle>(recv_message_state_)) {
if (grpc_call_trace.enabled()) {
gpr_log(GPR_INFO, "%sPollConnectedChannel: requesting message",
Activity::current()->DebugTag().c_str());
}
push_recv_message();
}
if (server_initial_metadata_state_ == ServerInitialMetadataState::kSet &&
!absl::holds_alternative<PipeSender<MessageHandle>::PushType>(
recv_message_state_) &&
std::exchange(queued_trailing_metadata_, false)) {
if (grpc_call_trace.enabled()) {
gpr_log(GPR_INFO,
"%sPollConnectedChannel: finished request, returning: {%s}; "
"active_ops: %s",
Activity::current()->DebugTag().c_str(),
server_trailing_metadata_->DebugString().c_str(),
ActiveOpsString().c_str());
}
finished_ = true;
return ServerMetadataHandle(std::move(server_trailing_metadata_));
}
if (auto* push = absl::get_if<PipeSender<MessageHandle>::PushType>(
&recv_message_state_)) {
auto r = (*push)();
if (bool* result = absl::get_if<bool>(&r)) {
if (*result) {
if (!finished_) {
if (grpc_call_trace.enabled()) {
gpr_log(GPR_INFO,
"%sPollConnectedChannel: pushed message; requesting next",
Activity::current()->DebugTag().c_str());
}
push_recv_message();
} else {
if (grpc_call_trace.enabled()) {
gpr_log(GPR_INFO,
"%sPollConnectedChannel: pushed message and finished; "
"marking closed",
Activity::current()->DebugTag().c_str());
}
recv_message_state_ = Closed{};
}
} else {
if (grpc_call_trace.enabled()) {
gpr_log(GPR_INFO,
"%sPollConnectedChannel: failed to push message; marking "
"closed",
Activity::current()->DebugTag().c_str());
}
recv_message_state_ = Closed{};
}
}
}
return Pending{};
}
void RecvInitialMetadataReady(grpc_error_handle error) {
GPR_ASSERT(error == GRPC_ERROR_NONE);
{
MutexLock lock(&mu_);
server_initial_metadata_state_ =
ServerInitialMetadataState::kReceivedButNotSet;
initial_metadata_waker_.Wakeup();
}
Unref("initial_metadata_ready");
}
void RecvTrailingMetadataReady(grpc_error_handle error) {
GPR_ASSERT(error == GRPC_ERROR_NONE);
{
MutexLock lock(&mu_);
queued_trailing_metadata_ = true;
trailing_metadata_waker_.Wakeup();
}
Unref("trailing_metadata_ready");
}
void MetadataBatchDone(grpc_error_handle error) {
GPR_ASSERT(error == GRPC_ERROR_NONE);
Unref("metadata_batch_done");
}
void SendMessageBatchDone(grpc_error_handle error) {
{
MutexLock lock(&mu_);
if (error != GRPC_ERROR_NONE) {
// Note that we're in error here, the call will be closed by the
// transport in a moment, and we'll return from the promise with an
// error - so we don't need to do any extra work to close out pipes or
// the like.
send_message_state_ = Closed{};
}
if (!absl::holds_alternative<Closed>(send_message_state_)) {
send_message_state_ = Idle{};
}
send_message_waker_.Wakeup();
}
Unref("send_message");
}
void RecvMessageBatchDone(grpc_error_handle error) {
{
MutexLock lock(&mu_);
if (error != GRPC_ERROR_NONE) {
if (grpc_call_trace.enabled()) {
gpr_log(GPR_INFO, "%sRecvMessageBatchDone: error=%s",
recv_message_waker_.ActivityDebugTag().c_str(),
grpc_error_std_string(error).c_str());
}
} else if (absl::holds_alternative<Closed>(recv_message_state_)) {
if (grpc_call_trace.enabled()) {
gpr_log(GPR_INFO, "%sRecvMessageBatchDone: already closed, ignoring",
recv_message_waker_.ActivityDebugTag().c_str());
}
} else {
auto pending =
absl::get_if<PendingReceiveMessage>(&recv_message_state_);
GPR_ASSERT(pending != nullptr);
GPR_ASSERT(pending->received == false);
pending->received = true;
}
recv_message_waker_.Wakeup();
}
Unref("recv_message");
}
// Called from outside the activity to push work down to the transport.
void Push() {
auto do_push = [this](grpc_transport_stream_op_batch* batch) {
if (stream_ != nullptr) {
grpc_transport_perform_stream_op(transport_, stream_.get(), batch);
} else {
grpc_transport_stream_op_batch_finish_with_failure_from_transport(
batch, GRPC_ERROR_CANCELLED);
}
};
bool push_metadata;
bool push_send_message;
bool push_recv_message;
{
MutexLock lock(&mu_);
push_metadata = std::exchange(push_metadata_, false);
push_send_message = std::exchange(push_send_message_, false);
push_recv_message = std::exchange(push_recv_message_, false);
scheduled_push_ = false;
}
if (push_metadata) do_push(&metadata_);
if (push_send_message) do_push(&send_message_);
if (push_recv_message) do_push(&recv_message_);
Unref("push");
}
void StreamDestroyed() {
call_context_->RunInContext([this] {
auto* cc = call_context_;
this->~ClientStream();
cc->Unref("child_stream");
});
}
private:
struct Idle {};
struct Closed {};
struct SendMessageToTransport {};
enum class ServerInitialMetadataState : uint8_t {
// Initial metadata has not been received from the server.
kNotReceived,
// Initial metadata has been received from the server via the transport, but
// has not yet been set on the latch to publish it up the call stack.
kReceivedButNotSet,
// Initial metadata has been received from the server via the transport and
// has been set on the latch to publish it up the call stack.
kSet,
};
class StreamDeleter {
public:
explicit StreamDeleter(ClientStream* impl) : impl_(impl) {}
void operator()(grpc_stream* stream) const {
if (stream == nullptr) return;
grpc_transport_destroy_stream(impl_->transport_, stream,
&impl_->stream_destroyed_);
}
private:
ClientStream* impl_;
};
using StreamPtr = std::unique_ptr<grpc_stream, StreamDeleter>;
void SchedulePush() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_) {
if (std::exchange(scheduled_push_, true)) return;
IncrementRefCount("push");
ExecCtx::Run(DEBUG_LOCATION, &push_, GRPC_ERROR_NONE);
}
std::string ActiveOpsString() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_) {
std::vector<std::string> ops;
if (finished_) ops.push_back("FINISHED");
// Pushes
std::vector<std::string> pushes;
if (push_metadata_) pushes.push_back("metadata");
if (push_send_message_) pushes.push_back("send_message");
if (push_recv_message_) pushes.push_back("recv_message");
if (!pushes.empty()) {
ops.push_back(
absl::StrCat(scheduled_push_ ? "push:" : "unscheduled-push:",
absl::StrJoin(pushes, ",")));
} else if (scheduled_push_) {
ops.push_back("push:nothing");
}
// Results from transport
std::vector<std::string> queued;
if (server_initial_metadata_state_ ==
ServerInitialMetadataState::kReceivedButNotSet) {
queued.push_back("initial_metadata");
}
if (queued_trailing_metadata_) queued.push_back("trailing_metadata");
if (!queued.empty()) {
ops.push_back(absl::StrCat("queued:", absl::StrJoin(queued, ",")));
}
// Send message
std::string send_message_state = SendMessageString();
if (send_message_state != "WAITING") {
ops.push_back(absl::StrCat("send_message:", send_message_state));
}
// Receive message
std::string recv_message_state = RecvMessageString();
if (recv_message_state != "IDLE") {
ops.push_back(absl::StrCat("recv_message:", recv_message_state));
}
return absl::StrJoin(ops, " ");
}
std::string SendMessageString() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_) {
return Match(
send_message_state_, [](Idle) -> std::string { return "IDLE"; },
[](Closed) -> std::string { return "CLOSED"; },
[](const PipeReceiver<MessageHandle>::NextType&) -> std::string {
return "WAITING";
},
[](SendMessageToTransport) -> std::string { return "SENDING"; });
}
std::string RecvMessageString() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_) {
return Match(
recv_message_state_, [](Idle) -> std::string { return "IDLE"; },
[](Closed) -> std::string { return "CLOSED"; },
[](const PendingReceiveMessage&) -> std::string { return "WAITING"; },
[](const absl::optional<MessageHandle>& message) -> std::string {
return absl::StrCat(
"READY:", message.has_value()
? absl::StrCat((*message)->payload()->Length(), "b")
: "EOS");
},
[](const PipeSender<MessageHandle>::PushType&) -> std::string {
return "PUSHING";
});
}
Mutex mu_;
bool requested_metadata_ = false;
bool push_metadata_ ABSL_GUARDED_BY(mu_) = false;
bool push_send_message_ ABSL_GUARDED_BY(mu_) = false;
bool push_recv_message_ ABSL_GUARDED_BY(mu_) = false;
bool scheduled_push_ ABSL_GUARDED_BY(mu_) = false;
ServerInitialMetadataState server_initial_metadata_state_
ABSL_GUARDED_BY(mu_) = ServerInitialMetadataState::kNotReceived;
bool queued_trailing_metadata_ ABSL_GUARDED_BY(mu_) = false;
bool finished_ ABSL_GUARDED_BY(mu_) = false;
CallContext* const call_context_{GetContext<CallContext>()};
Waker initial_metadata_waker_ ABSL_GUARDED_BY(mu_);
Waker trailing_metadata_waker_ ABSL_GUARDED_BY(mu_);
Waker send_message_waker_ ABSL_GUARDED_BY(mu_);
Waker recv_message_waker_ ABSL_GUARDED_BY(mu_);
grpc_transport* const transport_;
grpc_stream_refcount stream_refcount_;
StreamPtr stream_;
Latch<ServerMetadata*>* server_initial_metadata_latch_;
PipeReceiver<MessageHandle>* client_to_server_messages_;
PipeSender<MessageHandle>* server_to_client_messages_;
MessageHandle message_to_send_ ABSL_GUARDED_BY(mu_);
absl::variant<Idle, Closed, PipeReceiver<MessageHandle>::NextType,
SendMessageToTransport>
send_message_state_ ABSL_GUARDED_BY(mu_);
struct PendingReceiveMessage {
absl::optional<SliceBuffer> payload;
uint32_t flags;
bool received = false;
};
absl::variant<Idle, PendingReceiveMessage, Closed,
PipeSender<MessageHandle>::PushType>
recv_message_state_ ABSL_GUARDED_BY(mu_);
grpc_closure recv_initial_metadata_ready_ =
MakeMemberClosure<ClientStream, &ClientStream::RecvInitialMetadataReady>(
this, DEBUG_LOCATION);
grpc_closure recv_trailing_metadata_ready_ =
MakeMemberClosure<ClientStream, &ClientStream::RecvTrailingMetadataReady>(
this, DEBUG_LOCATION);
grpc_closure push_ = MakeMemberClosure<ClientStream, &ClientStream::Push>(
this, DEBUG_LOCATION);
ClientMetadataHandle client_initial_metadata_;
ClientMetadataHandle client_trailing_metadata_;
ServerMetadataHandle server_initial_metadata_;
ServerMetadataHandle server_trailing_metadata_;
grpc_transport_stream_op_batch metadata_;
grpc_closure metadata_batch_done_ =
MakeMemberClosure<ClientStream, &ClientStream::MetadataBatchDone>(
this, DEBUG_LOCATION);
grpc_transport_stream_op_batch send_message_;
grpc_closure send_message_batch_done_ =
MakeMemberClosure<ClientStream, &ClientStream::SendMessageBatchDone>(
this, DEBUG_LOCATION);
grpc_closure recv_message_batch_done_ =
MakeMemberClosure<ClientStream, &ClientStream::RecvMessageBatchDone>(
this, DEBUG_LOCATION);
grpc_transport_stream_op_batch recv_message_;
grpc_transport_stream_op_batch_payload batch_payload_{
GetContext<grpc_call_context_element>()};
grpc_closure stream_destroyed_ =
MakeMemberClosure<ClientStream, &ClientStream::StreamDestroyed>(
this, DEBUG_LOCATION);
};
class ClientConnectedCallPromise {
public:
ClientConnectedCallPromise(grpc_transport* transport, CallArgs call_args)
: impl_(GetContext<Arena>()->New<ClientStream>(transport,
std::move(call_args))) {}
ClientConnectedCallPromise(const ClientConnectedCallPromise&) = delete;
ClientConnectedCallPromise& operator=(const ClientConnectedCallPromise&) =
delete;
ClientConnectedCallPromise(ClientConnectedCallPromise&& other) noexcept
: impl_(std::exchange(other.impl_, nullptr)) {}
ClientConnectedCallPromise& operator=(
ClientConnectedCallPromise&& other) noexcept {
impl_ = std::move(other.impl_);
return *this;
}
static ArenaPromise<ServerMetadataHandle> Make(grpc_transport* transport,
CallArgs call_args) {
return ClientConnectedCallPromise(transport, std::move(call_args));
}
Poll<ServerMetadataHandle> operator()() { return impl_->PollOnce(); }
private:
OrphanablePtr<ClientStream> impl_;
};
template <ArenaPromise<ServerMetadataHandle> (*make_call_promise)(
grpc_transport*, CallArgs)>
grpc_channel_filter MakeConnectedFilter() {
// Create a vtable that contains both the legacy call methods (for filter
// stack based calls) and the new promise based method for creating promise
// based calls (the latter iff make_call_promise != nullptr).
// In this way the filter can be inserted into either kind of channel stack,
// and only if all the filters in the stack are promise based will the call
// be promise based.
return {
connected_channel_start_transport_stream_op_batch,
make_call_promise == nullptr
? nullptr
: +[](grpc_channel_element* elem, CallArgs call_args,
NextPromiseFactory) {
grpc_transport* transport =
static_cast<channel_data*>(elem->channel_data)->transport;
return make_call_promise(transport, std::move(call_args));
},
connected_channel_start_transport_op,
sizeof(call_data),
connected_channel_init_call_elem,
set_pollset_or_pollset_set,
connected_channel_destroy_call_elem,
sizeof(channel_data),
connected_channel_init_channel_elem,
+[](grpc_channel_stack* channel_stack, grpc_channel_element* elem) {
/* HACK(ctiller): increase call stack size for the channel to make space
for channel data. We need a cleaner (but performant) way to do this,
and I'm not sure what that is yet.
This is only "safe" because call stacks place no additional data
after the last call element, and the last call element MUST be the
connected channel. */
channel_stack->call_stack_size += grpc_transport_stream_size(
static_cast<channel_data*>(elem->channel_data)->transport);
},
connected_channel_destroy_channel_elem,
connected_channel_get_channel_info,
"connected",
};
}
ArenaPromise<ServerMetadataHandle> MakeTransportCallPromise(
grpc_transport* transport, CallArgs call_args) {
return transport->vtable->make_call_promise(transport, std::move(call_args));
}
const grpc_channel_filter kPromiseBasedTransportFilter =
MakeConnectedFilter<MakeTransportCallPromise>();
const grpc_channel_filter kClientEmulatedFilter =
MakeConnectedFilter<ClientConnectedCallPromise::Make>();
const grpc_channel_filter kNoPromiseFilter = MakeConnectedFilter<nullptr>();
} // namespace
} // namespace grpc_core
bool grpc_add_connected_filter(grpc_core::ChannelStackBuilder* builder) {
grpc_transport* t = builder->transport();
GPR_ASSERT(t != nullptr);
builder->AppendFilter(&grpc_connected_filter);
// Choose the right vtable for the connected filter.
// We can't know promise based call or not here (that decision needs the
// collaboration of all of the filters on the channel, and we don't want
// ordering constraints on when we add filters).
// We can know if this results in a promise based call how we'll create our
// promise (if indeed we can), and so that is the choice made here.
if (t->vtable->make_call_promise != nullptr) {
// Option 1, and our ideal: the transport supports promise based calls, and
// so we simply use the transport directly.
builder->AppendFilter(&grpc_core::kPromiseBasedTransportFilter);
} else if (grpc_channel_stack_type_is_client(builder->channel_stack_type())) {
// Option 2: the transport does not support promise based calls, but we're
// on the client and so we have an implementation that we can use to convert
// to batches.
builder->AppendFilter(&grpc_core::kClientEmulatedFilter);
} else {
// Option 3: the transport does not support promise based calls, and we're
// on the server so we can't construct promise based calls just yet.
builder->AppendFilter(&grpc_core::kNoPromiseFilter);
}
return true;
}
grpc_stream* grpc_connected_channel_get_stream(grpc_call_element* elem) {
call_data* calld = static_cast<call_data*>(elem->call_data);
return TRANSPORT_STREAM_FROM_CALL_DATA(calld);
}

@ -24,13 +24,9 @@
#include "src/core/lib/channel/channel_fwd.h"
#include "src/core/lib/channel/channel_stack.h"
#include "src/core/lib/channel/channel_stack_builder.h"
#include "src/core/lib/transport/transport.h"
extern const grpc_channel_filter grpc_connected_filter;
bool grpc_add_connected_filter(grpc_core::ChannelStackBuilder* builder);
/* Debug helper to dig the transport stream out of a call element */
grpc_stream* grpc_connected_channel_get_stream(grpc_call_element* elem);
#endif /* GRPC_CORE_LIB_CHANNEL_CONNECTED_CHANNEL_H */

@ -665,7 +665,7 @@ void ClientCallData::StartPromise(Flusher* flusher) {
promise_ = filter->MakeCallPromise(
CallArgs{WrapMetadata(send_initial_metadata_batch_->payload
->send_initial_metadata.send_initial_metadata),
server_initial_metadata_latch()},
server_initial_metadata_latch(), nullptr, nullptr},
[this](CallArgs call_args) {
return MakeNextPromise(std::move(call_args));
});
@ -1154,12 +1154,12 @@ void ServerCallData::RecvInitialMetadataReady(grpc_error_handle error) {
ScopedContext context(this);
// Construct the promise.
ChannelFilter* filter = static_cast<ChannelFilter*>(elem()->channel_data);
promise_ =
filter->MakeCallPromise(CallArgs{WrapMetadata(recv_initial_metadata_),
server_initial_metadata_latch()},
[this](CallArgs call_args) {
return MakeNextPromise(std::move(call_args));
});
promise_ = filter->MakeCallPromise(
CallArgs{WrapMetadata(recv_initial_metadata_),
server_initial_metadata_latch(), nullptr, nullptr},
[this](CallArgs call_args) {
return MakeNextPromise(std::move(call_args));
});
// Poll once.
WakeInsideCombiner(&flusher);
if (auto* closure =

@ -19,6 +19,9 @@
// promise-style. Most of this will be removed once the promises conversion is
// completed.
// TODO(ctiller): When removing this file, also reduce the number of *'s on the
// server initial metadata latch.
#include <grpc/support/port_platform.h>
#include <stdint.h>
@ -27,6 +30,7 @@
#include <atomic>
#include <memory>
#include <new>
#include <string>
#include <utility>
#include "absl/container/inlined_vector.h"
@ -56,6 +60,7 @@
#include "src/core/lib/promise/latch.h"
#include "src/core/lib/promise/poll.h"
#include "src/core/lib/resource_quota/arena.h"
#include "src/core/lib/transport/call_fragments.h"
#include "src/core/lib/transport/error_utils.h"
#include "src/core/lib/transport/metadata_batch.h"
#include "src/core/lib/transport/transport.h"
@ -146,6 +151,8 @@ class BaseCallData : public Activity, private Wakeable {
Waker MakeNonOwningWaker() final;
Waker MakeOwningWaker() final;
std::string ActivityDebugTag() const override { return DebugTag(); }
void Finalize(const grpc_call_final_info* final_info) {
finalization_.Run(final_info);
}
@ -234,13 +241,13 @@ class BaseCallData : public Activity, private Wakeable {
grpc_transport_stream_op_batch* batch_;
};
static MetadataHandle<grpc_metadata_batch> WrapMetadata(
static FragmentHandle<grpc_metadata_batch> WrapMetadata(
grpc_metadata_batch* p) {
return MetadataHandle<grpc_metadata_batch>(p);
return FragmentHandle<grpc_metadata_batch>(p, false);
}
static grpc_metadata_batch* UnwrapMetadata(
MetadataHandle<grpc_metadata_batch> p) {
FragmentHandle<grpc_metadata_batch> p) {
return p.Unwrap();
}
@ -311,8 +318,8 @@ class ClientCallData : public BaseCallData {
kQueued,
// We've forwarded the op to the next filter.
kForwarded,
// The op has completed from below, but we haven't yet forwarded it up (the
// promise gets to interject and mutate it).
// The op has completed from below, but we haven't yet forwarded it up
// (the promise gets to interject and mutate it).
kComplete,
// We've called the recv_metadata_ready callback from the original
// recv_trailing_metadata op that was presented to us.

@ -25,7 +25,6 @@
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/utility/utility.h"
#include <grpc/event_engine/endpoint_config.h>
#include <grpc/event_engine/event_engine.h>
@ -74,7 +73,7 @@ struct PosixTcpOptions {
PosixTcpOptions() = default;
// Move ctor
PosixTcpOptions(PosixTcpOptions&& other) noexcept {
socket_mutator = absl::exchange(other.socket_mutator, nullptr);
socket_mutator = std::exchange(other.socket_mutator, nullptr);
resource_quota = std::move(other.resource_quota);
CopyIntegerOptions(other);
}
@ -83,7 +82,7 @@ struct PosixTcpOptions {
if (socket_mutator != nullptr) {
grpc_socket_mutator_unref(socket_mutator);
}
socket_mutator = absl::exchange(other.socket_mutator, nullptr);
socket_mutator = std::exchange(other.socket_mutator, nullptr);
resource_quota = std::move(other.resource_quota);
CopyIntegerOptions(other);
return *this;

@ -48,6 +48,9 @@ const char* const description_event_engine_client =
"Use EventEngine clients instead of iomgr's grpc_tcp_client";
const char* const description_monitoring_experiment =
"Placeholder experiment to prove/disprove our monitoring is working";
const char* const description_promise_based_client_call =
"If set, use the new gRPC promise based call code when it's appropriate "
"(ie when all filters in a stack are promise based)";
#ifdef NDEBUG
const bool kDefaultForDebugOnly = false;
#else
@ -73,6 +76,7 @@ const ExperimentMetadata g_experiment_metadata[] = {
kDefaultForDebugOnly},
{"event_engine_client", description_event_engine_client, false},
{"monitoring_experiment", description_monitoring_experiment, true},
{"promise_based_client_call", description_promise_based_client_call, false},
};
} // namespace grpc_core

@ -42,6 +42,9 @@ inline bool IsUnconstrainedMaxQuotaBufferSizeEnabled() {
inline bool IsNewHpackHuffmanDecoderEnabled() { return IsExperimentEnabled(8); }
inline bool IsEventEngineClientEnabled() { return IsExperimentEnabled(9); }
inline bool IsMonitoringExperimentEnabled() { return IsExperimentEnabled(10); }
inline bool IsPromiseBasedClientCallEnabled() {
return IsExperimentEnabled(11);
}
struct ExperimentMetadata {
const char* name;
@ -49,7 +52,7 @@ struct ExperimentMetadata {
bool default_value;
};
constexpr const size_t kNumExperiments = 11;
constexpr const size_t kNumExperiments = 12;
extern const ExperimentMetadata g_experiment_metadata[kNumExperiments];
} // namespace grpc_core

@ -119,3 +119,11 @@
expiry: 2022/10/01
owner: ctiller@google.com
test_tags: []
- name: promise_based_client_call
description:
If set, use the new gRPC promise based call code when it's appropriate
(ie when all filters in a stack are promise based)
default: false
expiry: 2023/01/01
owner: ctiller@google.com
test_tags: ["core_end2end_test", "lame_client_test"]

@ -118,6 +118,54 @@ inline grpc_closure* grpc_closure_init(grpc_closure* closure,
grpc_closure_init(closure, cb, cb_arg)
#endif
namespace grpc_core {
template <typename T, void (T::*cb)(grpc_error_handle)>
grpc_closure MakeMemberClosure(T* p, DebugLocation location = DebugLocation()) {
grpc_closure out;
GRPC_CLOSURE_INIT(
&out, [](void* p, grpc_error_handle e) { (static_cast<T*>(p)->*cb)(e); },
p, nullptr);
#ifndef NDEBUG
out.file_created = location.file();
out.line_created = location.line();
#else
(void)location;
#endif
return out;
}
template <typename T, void (T::*cb)()>
grpc_closure MakeMemberClosure(T* p, DebugLocation location = DebugLocation()) {
grpc_closure out;
GRPC_CLOSURE_INIT(
&out, [](void* p, grpc_error_handle) { (static_cast<T*>(p)->*cb)(); }, p,
nullptr);
#ifndef NDEBUG
out.file_created = location.file();
out.line_created = location.line();
#else
(void)location;
#endif
return out;
}
template <typename F>
grpc_closure* NewClosure(F f) {
struct Closure : public grpc_closure {
explicit Closure(F f) : f(std::move(f)) {}
F f;
static void Run(void* arg, grpc_error_handle error) {
auto self = static_cast<Closure*>(arg);
self->f(error);
delete self;
}
};
Closure* c = new Closure(std::move(f));
GRPC_CLOSURE_INIT(c, Closure::Run, c, nullptr);
return c;
}
} // namespace grpc_core
namespace closure_impl {
struct wrapped_closure {

@ -64,7 +64,7 @@ struct PosixTcpOptions {
PosixTcpOptions() = default;
// Move ctor
PosixTcpOptions(PosixTcpOptions&& other) noexcept {
socket_mutator = absl::exchange(other.socket_mutator, nullptr);
socket_mutator = std::exchange(other.socket_mutator, nullptr);
resource_quota = std::move(other.resource_quota);
CopyIntegerOptions(other);
}
@ -73,7 +73,7 @@ struct PosixTcpOptions {
if (socket_mutator != nullptr) {
grpc_socket_mutator_unref(socket_mutator);
}
socket_mutator = absl::exchange(other.socket_mutator, nullptr);
socket_mutator = std::exchange(other.socket_mutator, nullptr);
resource_quota = std::move(other.resource_quota);
CopyIntegerOptions(other);
return *this;

@ -18,6 +18,8 @@
#include <stddef.h>
#include "absl/strings/str_format.h"
#include "src/core/lib/gprpp/atomic_utils.h"
namespace grpc_core {
@ -32,6 +34,8 @@ namespace promise_detail {
///////////////////////////////////////////////////////////////////////////////
// HELPER TYPES
std::string Unwakeable::ActivityDebugTag() const { return "<unknown>"; }
// Weak handle to an Activity.
// Handle can persist while Activity goes away.
class FreestandingActivity::Handle final : public Wakeable {
@ -74,6 +78,11 @@ class FreestandingActivity::Handle final : public Wakeable {
void Drop() override { Unref(); }
std::string ActivityDebugTag() const override {
MutexLock lock(&mu_);
return activity_ == nullptr ? "<unknown>" : activity_->DebugTag();
}
private:
// Unref the Handle (not the activity).
void Unref() {
@ -85,7 +94,7 @@ class FreestandingActivity::Handle final : public Wakeable {
// Two initial refs: one for the waiter that caused instantiation, one for the
// activity.
std::atomic<size_t> refs_{2};
Mutex mu_ ABSL_ACQUIRED_AFTER(activity_->mu_);
mutable Mutex mu_ ABSL_ACQUIRED_AFTER(activity_->mu_);
FreestandingActivity* activity_ ABSL_GUARDED_BY(mu_);
};
@ -117,4 +126,9 @@ Waker FreestandingActivity::MakeNonOwningWaker() {
}
} // namespace promise_detail
std::string Activity::DebugTag() const {
return absl::StrFormat("ACTIVITY[%p]", this);
}
} // namespace grpc_core

@ -22,6 +22,7 @@
#include <algorithm>
#include <atomic>
#include <memory>
#include <string>
#include <utility>
#include "absl/base/thread_annotations.h"
@ -43,6 +44,8 @@
namespace grpc_core {
class Activity;
// A Wakeable object is used by queues to wake activities.
class Wakeable {
public:
@ -52,19 +55,23 @@ class Wakeable {
// Drop this wakeable without waking up the underlying activity.
virtual void Drop() = 0;
// Return the underlying activity debug tag, or "<unknown>" if not available.
virtual std::string ActivityDebugTag() const = 0;
protected:
inline ~Wakeable() {}
};
namespace activity_detail {
namespace promise_detail {
struct Unwakeable final : public Wakeable {
void Wakeup() override {}
void Drop() override {}
std::string ActivityDebugTag() const override;
};
static Unwakeable* unwakeable() {
return NoDestructSingleton<Unwakeable>::Get();
}
} // namespace activity_detail
} // namespace promise_detail
class AtomicWaker;
@ -73,7 +80,7 @@ class AtomicWaker;
class Waker {
public:
explicit Waker(Wakeable* wakeable) : wakeable_(wakeable) {}
Waker() : Waker(activity_detail::unwakeable()) {}
Waker() : Waker(promise_detail::unwakeable()) {}
~Waker() { wakeable_->Drop(); }
Waker(const Waker&) = delete;
Waker& operator=(const Waker&) = delete;
@ -95,11 +102,15 @@ class Waker {
return wakeable_ == other.wakeable_;
}
std::string ActivityDebugTag() {
return wakeable_ == nullptr ? "<unknown>" : wakeable_->ActivityDebugTag();
}
private:
friend class AtomicWaker;
Wakeable* Take() {
return std::exchange(wakeable_, activity_detail::unwakeable());
return std::exchange(wakeable_, promise_detail::unwakeable());
}
Wakeable* wakeable_;
@ -109,7 +120,7 @@ class Waker {
class AtomicWaker {
public:
explicit AtomicWaker(Wakeable* wakeable) : wakeable_(wakeable) {}
AtomicWaker() : AtomicWaker(activity_detail::unwakeable()) {}
AtomicWaker() : AtomicWaker(promise_detail::unwakeable()) {}
explicit AtomicWaker(Waker waker) : AtomicWaker(waker.Take()) {}
~AtomicWaker() { wakeable_.load(std::memory_order_acquire)->Drop(); }
AtomicWaker(const AtomicWaker&) = delete;
@ -123,7 +134,7 @@ class AtomicWaker {
// Return true if there is a not-unwakeable wakeable present.
bool Armed() const noexcept {
return wakeable_.load(std::memory_order_relaxed) !=
activity_detail::unwakeable();
promise_detail::unwakeable();
}
// Set to some new waker
@ -133,7 +144,7 @@ class AtomicWaker {
private:
Wakeable* Take() {
return wakeable_.exchange(activity_detail::unwakeable(),
return wakeable_.exchange(promise_detail::unwakeable(),
std::memory_order_acq_rel);
}
@ -179,6 +190,9 @@ class Activity : public Orphanable {
// delivered until long after the activity should be destroyed.
virtual Waker MakeNonOwningWaker() = 0;
// Some descriptive text to add to log messages to identify this activity.
virtual std::string DebugTag() const;
protected:
// Check if this activity is the current activity executing on the current
// thread.
@ -333,6 +347,8 @@ class FreestandingActivity : public Activity, private Wakeable {
Mutex* mu() ABSL_LOCK_RETURNED(mu_) { return &mu_; }
std::string ActivityDebugTag() const override { return DebugTag(); }
private:
class Handle;

@ -86,13 +86,9 @@ class CallPushPull {
if (!done_.is_set(kDoneMain)) {
auto p = main_();
if (auto* status = absl::get_if<kPollReadyIdx>(&p)) {
if (IsStatusOk(*status)) {
done_.set(kDoneMain);
Destruct(&main_);
Construct(&result_, std::move(*status));
} else {
return std::move(*status);
}
done_.set(kDoneMain);
Destruct(&main_);
Construct(&result_, std::move(*status));
}
}
if (!done_.is_set(kDonePull)) {
@ -134,6 +130,10 @@ class CallPushPull {
// When polling, the push is polled first, then the main call (descending the
// stack), then the pull (as we ascend once more).
//
// If the push or the pull fail early, then the entire call fails.
// If the main part of the call fails, we wait until both push and pull are also
// done.
//
// This strategy minimizes repolls.
template <typename FMain, typename FPush, typename FPull>
promise_detail::CallPushPull<FMain, FPush, FPull> CallPushPull(FMain f_main,

@ -240,6 +240,8 @@ class Center {
template <typename T>
class PipeSender {
public:
using PushType = pipe_detail::Push<T>;
PipeSender(const PipeSender&) = delete;
PipeSender& operator=(const PipeSender&) = delete;
@ -257,11 +259,15 @@ class PipeSender {
if (center_ != nullptr) center_->UnrefSend();
}
void Close() {
if (auto* center = std::exchange(center_, nullptr)) center->UnrefSend();
}
// Send a single message along the pipe.
// Returns a promise that will resolve to a bool - true if the message was
// sent, false if it could never be sent. Blocks the promise until the
// receiver is either closed or able to receive another message.
pipe_detail::Push<T> Push(T value);
PushType Push(T value);
private:
friend struct Pipe<T>;
@ -273,6 +279,8 @@ class PipeSender {
template <typename T>
class PipeReceiver {
public:
using NextType = pipe_detail::Next<T>;
PipeReceiver(const PipeReceiver&) = delete;
PipeReceiver& operator=(const PipeReceiver&) = delete;
@ -294,7 +302,7 @@ class PipeReceiver {
// message was received, or no value if the other end of the pipe was closed.
// Blocks the promise until the receiver is either closed or a message is
// available.
pipe_detail::Next<T> Next();
NextType Next();
private:
friend struct Pipe<T>;
@ -434,7 +442,8 @@ void NextResult<T>::reset() {
// polling code) would likely be more appropriate.
template <typename T>
struct Pipe {
Pipe() : Pipe(GetContext<Arena>()->New<pipe_detail::Center<T>>()) {}
Pipe() : Pipe(GetContext<Arena>()) {}
explicit Pipe(Arena* arena) : Pipe(arena->New<pipe_detail::Center<T>>()) {}
Pipe(const Pipe&) = delete;
Pipe& operator=(const Pipe&) = delete;
Pipe(Pipe&&) noexcept = default;

@ -19,6 +19,8 @@
#include <stddef.h>
#include <string>
#include "absl/types/variant.h"
namespace grpc_core {
@ -61,6 +63,17 @@ struct PollTraits<Poll<T>> {
static constexpr bool is_poll() { return true; }
};
// Convert a poll to a string
template <typename T, typename F>
std::string PollToString(
const Poll<T>& poll,
F t_to_string = [](const T& t) { return t.ToString(); }) {
if (absl::holds_alternative<Pending>(poll)) {
return "<<pending>>";
}
return t_to_string(absl::get<T>(poll));
}
} // namespace grpc_core
#endif // GRPC_CORE_LIB_PROMISE_POLL_H

@ -32,6 +32,7 @@
#include "src/core/lib/promise/promise.h"
#include "src/core/lib/security/authorization/authorization_engine.h"
#include "src/core/lib/security/authorization/evaluate_args.h"
#include "src/core/lib/transport/call_fragments.h"
#include "src/core/lib/transport/transport.h"
namespace grpc_core {

@ -30,6 +30,7 @@
#include "src/core/lib/security/authorization/authorization_policy_provider.h"
#include "src/core/lib/security/authorization/evaluate_args.h"
#include "src/core/lib/security/context/security_context.h"
#include "src/core/lib/transport/call_fragments.h"
#include "src/core/lib/transport/transport.h"
namespace grpc_core {

@ -24,7 +24,7 @@
#include <grpc/grpc_security.h>
#include "src/core/lib/security/credentials/credentials.h"
#include "src/core/lib/transport/transport.h"
#include "src/core/lib/transport/call_fragments.h"
namespace grpc_core {

@ -33,7 +33,7 @@
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/promise/try_seq.h"
#include "src/core/lib/surface/api_trace.h"
#include "src/core/lib/transport/transport.h"
#include "src/core/lib/transport/call_fragments.h"
//
// grpc_composite_channel_credentials

@ -39,7 +39,7 @@
#include "src/core/lib/promise/arena_promise.h"
#include "src/core/lib/security/credentials/credentials.h"
#include "src/core/lib/security/security_connector/security_connector.h"
#include "src/core/lib/transport/transport.h"
#include "src/core/lib/transport/call_fragments.h"
/* -- Composite channel credentials. -- */

@ -31,6 +31,7 @@
#include "src/core/lib/promise/promise.h"
#include "src/core/lib/security/security_connector/fake/fake_security_connector.h"
#include "src/core/lib/security/security_connector/security_connector.h"
#include "src/core/lib/transport/call_fragments.h"
#include "src/core/lib/transport/metadata_batch.h"
/* -- Fake transport security credentials. -- */

@ -35,7 +35,7 @@
#include "src/core/lib/promise/arena_promise.h"
#include "src/core/lib/security/credentials/credentials.h"
#include "src/core/lib/slice/slice.h"
#include "src/core/lib/transport/transport.h"
#include "src/core/lib/transport/call_fragments.h"
#define GRPC_ARG_FAKE_SECURITY_EXPECTED_TARGETS \
"grpc.fake_security.expected_targets"

@ -34,6 +34,7 @@
#include "src/core/lib/iomgr/exec_ctx.h"
#include "src/core/lib/promise/promise.h"
#include "src/core/lib/surface/api_trace.h"
#include "src/core/lib/transport/call_fragments.h"
#include "src/core/lib/transport/metadata_batch.h"
grpc_core::ArenaPromise<absl::StatusOr<grpc_core::ClientMetadataHandle>>

@ -33,7 +33,7 @@
#include "src/core/lib/promise/arena_promise.h"
#include "src/core/lib/security/credentials/credentials.h"
#include "src/core/lib/slice/slice.h"
#include "src/core/lib/transport/transport.h"
#include "src/core/lib/transport/call_fragments.h"
class grpc_google_iam_credentials : public grpc_call_credentials {
public:

@ -42,6 +42,7 @@
#include "src/core/lib/promise/promise.h"
#include "src/core/lib/security/credentials/call_creds_util.h"
#include "src/core/lib/surface/api_trace.h"
#include "src/core/lib/transport/call_fragments.h"
#include "src/core/lib/transport/metadata_batch.h"
#include "src/core/lib/uri/uri_parser.h"

@ -43,7 +43,7 @@
#include "src/core/lib/security/credentials/credentials.h"
#include "src/core/lib/security/credentials/jwt/json_token.h"
#include "src/core/lib/slice/slice.h"
#include "src/core/lib/transport/transport.h"
#include "src/core/lib/transport/call_fragments.h"
class grpc_service_account_jwt_access_credentials
: public grpc_call_credentials {

@ -57,9 +57,9 @@
#include "src/core/lib/promise/promise.h"
#include "src/core/lib/security/util/json_util.h"
#include "src/core/lib/surface/api_trace.h"
#include "src/core/lib/transport/call_fragments.h"
#include "src/core/lib/transport/error_utils.h"
#include "src/core/lib/transport/metadata_batch.h"
#include "src/core/lib/transport/transport.h"
#include "src/core/lib/uri/uri_parser.h"
using grpc_core::Json;

@ -48,7 +48,7 @@
#include "src/core/lib/promise/arena_promise.h"
#include "src/core/lib/security/credentials/credentials.h"
#include "src/core/lib/slice/slice.h"
#include "src/core/lib/transport/transport.h"
#include "src/core/lib/transport/call_fragments.h"
#include "src/core/lib/uri/uri_parser.h"
// Constants.

@ -37,6 +37,7 @@
#include "src/core/lib/slice/slice_internal.h"
#include "src/core/lib/surface/api_trace.h"
#include "src/core/lib/surface/validate_metadata.h"
#include "src/core/lib/transport/call_fragments.h"
#include "src/core/lib/transport/metadata_batch.h"
grpc_core::TraceFlag grpc_plugin_credentials_trace(false, "plugin_credentials");

@ -46,7 +46,7 @@
#include "src/core/lib/security/credentials/call_creds_util.h"
#include "src/core/lib/security/credentials/credentials.h"
#include "src/core/lib/slice/slice.h"
#include "src/core/lib/transport/transport.h"
#include "src/core/lib/transport/call_fragments.h"
extern grpc_core::TraceFlag grpc_plugin_credentials_trace;

@ -52,6 +52,7 @@
#include "src/core/lib/security/security_connector/security_connector.h"
#include "src/core/lib/security/transport/auth_filters.h"
#include "src/core/lib/slice/slice.h"
#include "src/core/lib/transport/call_fragments.h"
#include "src/core/lib/transport/metadata_batch.h"
#include "src/core/lib/transport/transport.h"

File diff suppressed because it is too large Load Diff

@ -24,14 +24,17 @@
#include <stddef.h>
#include <stdint.h>
#include "absl/functional/any_invocable.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include <grpc/impl/codegen/compression_types.h>
#include <grpc/impl/codegen/grpc_types.h>
#include <grpc/support/atm.h>
#include <grpc/support/log.h>
#include "src/core/lib/channel/channel_fwd.h"
#include "src/core/lib/channel/channel_stack.h"
#include "src/core/lib/channel/context.h"
#include "src/core/lib/debug/trace.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h"
@ -39,6 +42,8 @@
#include "src/core/lib/iomgr/closure.h"
#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/iomgr/iomgr_fwd.h"
#include "src/core/lib/iomgr/polling_entity.h"
#include "src/core/lib/promise/context.h"
#include "src/core/lib/resource_quota/arena.h"
#include "src/core/lib/slice/slice.h"
#include "src/core/lib/surface/api_trace.h"
@ -67,6 +72,45 @@ typedef struct grpc_call_create_args {
grpc_core::Timestamp send_deadline;
} grpc_call_create_args;
namespace grpc_core {
class PromiseBasedCall;
// TODO(ctiller): move more call things into this type
class CallContext {
public:
explicit CallContext(PromiseBasedCall* call) : call_(call) {}
// Run some action in the call activity context. This is needed to adapt some
// legacy systems to promises, and will likely disappear once that conversion
// is complete.
void RunInContext(absl::AnyInvocable<void()> fn);
// TODO(ctiller): remove this once transport APIs are promise based
void IncrementRefCount(const char* reason = "call_context");
// TODO(ctiller): remove this once transport APIs are promise based
void Unref(const char* reason = "call_context");
grpc_call_stats* call_stats() { return &call_stats_; }
gpr_atm* peer_string_atm_ptr();
grpc_polling_entity* polling_entity() { return &pollent_; }
private:
friend class PromiseBasedCall;
// Call final info.
grpc_call_stats call_stats_;
// Pollset stuff, can't wait to remove.
// TODO(ctiller): bring forth EventEngine.
grpc_polling_entity pollent_;
// TODO(ctiller): remove this once transport APIs are promise based and we
// don't need refcounting here.
PromiseBasedCall* const call_;
};
template <>
struct ContextType<CallContext> {};
} // namespace grpc_core
/* Create a new call based on \a args.
Regardless of success or failure, always returns a valid new call into *call
*/

@ -0,0 +1,114 @@
// Copyright 2022 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.
#include <grpc/support/port_platform.h>
#include "src/core/lib/surface/call_trace.h"
#include <functional>
#include <memory>
#include <string>
#include <utility>
#include "absl/base/thread_annotations.h"
#include "absl/container/flat_hash_map.h"
#include "absl/meta/type_traits.h"
#include "absl/status/status.h"
#include "absl/types/variant.h"
#include <grpc/support/log.h>
#include "src/core/lib/channel/channel_stack.h"
#include "src/core/lib/gprpp/no_destruct.h"
#include "src/core/lib/gprpp/sync.h"
#include "src/core/lib/iomgr/closure.h"
#include "src/core/lib/promise/activity.h"
#include "src/core/lib/promise/arena_promise.h"
#include "src/core/lib/transport/call_fragments.h"
#include "src/core/lib/transport/metadata_batch.h"
#include "src/core/lib/transport/transport.h"
namespace grpc_core {
const grpc_channel_filter* PromiseTracingFilterFor(
const grpc_channel_filter* filter) {
struct DerivedFilter : public grpc_channel_filter {
explicit DerivedFilter(const grpc_channel_filter* filter)
: grpc_channel_filter{
/* start_transport_stream_op_batch: */ grpc_call_next_op,
/* make_call_promise: */
[](grpc_channel_element* elem, CallArgs call_args,
NextPromiseFactory next_promise_factory)
-> ArenaPromise<ServerMetadataHandle> {
auto* source_filter =
static_cast<const DerivedFilter*>(elem->filter)->filter;
gpr_log(
GPR_DEBUG,
"%sCreateCallPromise[%s]: client_initial_metadata=%s",
Activity::current()->DebugTag().c_str(),
source_filter->name,
call_args.client_initial_metadata->DebugString().c_str());
return [source_filter, child = next_promise_factory(
std::move(call_args))]() mutable {
gpr_log(GPR_DEBUG, "%sPollCallPromise[%s]: begin",
Activity::current()->DebugTag().c_str(),
source_filter->name);
auto r = child();
if (auto* p = absl::get_if<ServerMetadataHandle>(&r)) {
gpr_log(GPR_DEBUG, "%sPollCallPromise[%s]: done: %s",
Activity::current()->DebugTag().c_str(),
source_filter->name, (*p)->DebugString().c_str());
} else {
gpr_log(GPR_DEBUG, "%sPollCallPromise[%s]: <<pending>",
Activity::current()->DebugTag().c_str(),
source_filter->name);
}
return r;
};
},
grpc_channel_next_op, /* sizeof_call_data: */ 0,
/* init_call_elem: */
[](grpc_call_element*, const grpc_call_element_args*) {
return absl::OkStatus();
},
grpc_call_stack_ignore_set_pollset_or_pollset_set,
/* destroy_call_elem: */
[](grpc_call_element*, const grpc_call_final_info*,
grpc_closure*) {},
/* sizeof_channel_data: */ 0, /* init_channel_elem: */
[](grpc_channel_element*, grpc_channel_element_args*) {
return absl::OkStatus();
},
/* post_init_channel_elem: */
[](grpc_channel_stack*, grpc_channel_element*) {},
/* destroy_channel_elem: */ [](grpc_channel_element*) {},
grpc_channel_next_get_info, filter->name},
filter(filter) {}
const grpc_channel_filter* const filter;
};
struct Globals {
Mutex mu;
absl::flat_hash_map<const grpc_channel_filter*,
std::unique_ptr<DerivedFilter>>
map ABSL_GUARDED_BY(mu);
};
auto* globals = NoDestructSingleton<Globals>::Get();
MutexLock lock(&globals->mu);
auto it = globals->map.find(filter);
if (it != globals->map.end()) return it->second.get();
return globals->map.emplace(filter, std::make_unique<DerivedFilter>(filter))
.first->second.get();
}
} // namespace grpc_core

@ -0,0 +1,30 @@
// Copyright 2022 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_CORE_LIB_SURFACE_CALL_TRACE_H
#define GRPC_CORE_LIB_SURFACE_CALL_TRACE_H
#include <grpc/support/port_platform.h>
#include "src/core/lib/channel/channel_fwd.h"
#include "src/core/lib/debug/trace.h"
extern grpc_core::TraceFlag grpc_call_trace;
namespace grpc_core {
const grpc_channel_filter* PromiseTracingFilterFor(
const grpc_channel_filter* filter);
}
#endif // GRPC_CORE_LIB_SURFACE_CALL_TRACE_H

@ -62,11 +62,12 @@
namespace grpc_core {
Channel::Channel(bool is_client, std::string target,
Channel::Channel(bool is_client, bool is_promising, std::string target,
const ChannelArgs& channel_args,
grpc_compression_options compression_options,
RefCountedPtr<grpc_channel_stack> channel_stack)
: is_client_(is_client),
is_promising_(is_promising),
compression_options_(compression_options),
call_size_estimate_(channel_stack->call_stack_size +
grpc_call_get_initial_size_estimate()),
@ -150,8 +151,8 @@ absl::StatusOr<RefCountedPtr<Channel>> Channel::CreateWithBuilder(
return RefCountedPtr<Channel>(new Channel(
grpc_channel_stack_type_is_client(builder->channel_stack_type()),
std::string(builder->target()), channel_args, compression_options,
std::move(*r)));
builder->IsPromising(), std::string(builder->target()), channel_args,
compression_options, std::move(*r)));
}
namespace {

@ -26,6 +26,7 @@
#include <atomic>
#include <map>
#include <memory>
#include <string>
#include <utility>
@ -34,6 +35,7 @@
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include <grpc/event_engine/event_engine.h>
#include <grpc/event_engine/memory_allocator.h>
#include <grpc/impl/codegen/compression_types.h>
#include <grpc/impl/codegen/grpc_types.h>
@ -44,6 +46,7 @@
#include "src/core/lib/channel/channel_stack.h" // IWYU pragma: keep
#include "src/core/lib/channel/channel_stack_builder.h"
#include "src/core/lib/channel/channelz.h"
#include "src/core/lib/event_engine/default_event_engine.h"
#include "src/core/lib/gprpp/cpp_impl_of.h"
#include "src/core/lib/gprpp/debug_location.h"
#include "src/core/lib/gprpp/ref_counted.h"
@ -141,6 +144,7 @@ class Channel : public RefCounted<Channel>,
absl::string_view target() const { return target_; }
MemoryAllocator* allocator() { return &allocator_; }
bool is_client() const { return is_client_; }
bool is_promising() const { return is_promising_; }
RegisteredCall* RegisterCall(const char* method, const char* host);
int TestOnlyRegisteredCalls() {
@ -153,12 +157,18 @@ class Channel : public RefCounted<Channel>,
return registration_table_.method_registration_attempts;
}
grpc_event_engine::experimental::EventEngine* event_engine() const {
return event_engine_.get();
}
private:
Channel(bool is_client, std::string target, const ChannelArgs& channel_args,
Channel(bool is_client, bool is_promising, std::string target,
const ChannelArgs& channel_args,
grpc_compression_options compression_options,
RefCountedPtr<grpc_channel_stack> channel_stack);
const bool is_client_;
const bool is_promising_;
const grpc_compression_options compression_options_;
std::atomic<size_t> call_size_estimate_;
CallRegistrationTable registration_table_;
@ -166,6 +176,8 @@ class Channel : public RefCounted<Channel>,
MemoryAllocator allocator_;
std::string target_;
const RefCountedPtr<grpc_channel_stack> channel_stack_;
const std::shared_ptr<grpc_event_engine::experimental::EventEngine>
event_engine_ = grpc_event_engine::experimental::GetDefaultEventEngine();
};
} // namespace grpc_core

@ -41,10 +41,12 @@
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/gprpp/sync.h"
#include "src/core/lib/iomgr/exec_ctx.h"
#include "src/core/lib/promise/pipe.h"
#include "src/core/lib/promise/promise.h"
#include "src/core/lib/surface/api_trace.h"
#include "src/core/lib/surface/channel.h"
#include "src/core/lib/surface/channel_stack_type.h"
#include "src/core/lib/transport/call_fragments.h"
#include "src/core/lib/transport/connectivity_state.h"
#include "src/core/lib/transport/transport.h"
@ -72,7 +74,10 @@ LameClientFilter::State::State()
: state_tracker("lame_client", GRPC_CHANNEL_SHUTDOWN) {}
ArenaPromise<ServerMetadataHandle> LameClientFilter::MakeCallPromise(
CallArgs, NextPromiseFactory) {
CallArgs args, NextPromiseFactory) {
// TODO(ctiller): remove if check once promise_based_filter is removed (Close
// is still needed)
if (args.incoming_messages != nullptr) args.incoming_messages->Close();
return Immediate(ServerMetadataHandle(error_));
}

@ -35,6 +35,7 @@
#include "src/core/lib/gprpp/sync.h"
#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/promise/arena_promise.h"
#include "src/core/lib/transport/call_fragments.h"
#include "src/core/lib/transport/connectivity_state.h"
#include "src/core/lib/transport/transport.h"

@ -0,0 +1,45 @@
// Copyright 2022 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.
#include <grpc/support/port_platform.h>
#include "src/core/lib/transport/call_fragments.h"
namespace grpc_core {
FragmentAllocator::Node* FragmentAllocator::AllocateNode() {
if (free_list_ != nullptr) {
Node* node = free_list_;
free_list_ = free_list_->next_free;
return node;
}
return static_cast<Node*>(GetContext<Arena>()->Alloc(sizeof(Node)));
}
void FragmentAllocator::FreeNode(Node* node) {
node->next_free = free_list_;
free_list_ = node;
}
void FragmentAllocator::Delete(grpc_metadata_batch* p) {
p->~grpc_metadata_batch();
FreeNode(reinterpret_cast<Node*>(p));
}
void FragmentAllocator::Delete(Message* m) {
m->~Message();
FreeNode(reinterpret_cast<Node*>(m));
}
} // namespace grpc_core

@ -0,0 +1,232 @@
// Copyright 2022 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_CORE_LIB_TRANSPORT_CALL_FRAGMENTS_H
#define GRPC_CORE_LIB_TRANSPORT_CALL_FRAGMENTS_H
#include <grpc/support/port_platform.h>
#include <stdint.h>
#include <new>
#include <utility>
#include "absl/status/status.h"
#include "absl/types/optional.h"
#include <grpc/status.h>
#include "src/core/lib/promise/context.h"
#include "src/core/lib/resource_quota/arena.h"
#include "src/core/lib/slice/slice.h"
#include "src/core/lib/slice/slice_buffer.h"
#include "src/core/lib/transport/metadata_batch.h"
namespace grpc_core {
// TODO(ctiller): eliminate once MetadataHandle is constructable directly.
namespace promise_filter_detail {
class BaseCallData;
} // namespace promise_filter_detail
class FragmentAllocator;
// Small owned "handle" type to ensure one accessor at a time to metadata.
// The focus here is to get promises to use the syntax we'd like - we'll
// probably substitute some other smart pointer later.
template <typename T>
class FragmentHandle {
public:
FragmentHandle() = default;
FragmentHandle(const FragmentHandle&) = delete;
FragmentHandle& operator=(const FragmentHandle&) = delete;
FragmentHandle(FragmentHandle&& other) noexcept
: handle_(other.handle_),
allocated_by_allocator_(other.allocated_by_allocator_) {
other.handle_ = nullptr;
other.allocated_by_allocator_ = false;
}
FragmentHandle& operator=(FragmentHandle&& other) noexcept {
DestroyHandle();
handle_ = other.handle_;
allocated_by_allocator_ = other.allocated_by_allocator_;
other.handle_ = nullptr;
other.allocated_by_allocator_ = false;
return *this;
}
explicit FragmentHandle(const absl::Status& status);
~FragmentHandle() { DestroyHandle(); }
T* operator->() const { return handle_; }
bool has_value() const { return handle_ != nullptr; }
T* get() const { return handle_; }
void reset() { *this = FragmentHandle(); }
static FragmentHandle TestOnlyWrap(T* p) { return FragmentHandle(p, false); }
private:
// We restrict access to construction from a pointer to limit the number of
// cases that need dealing with as this code evolves.
friend class promise_filter_detail::BaseCallData;
friend class FragmentAllocator;
explicit FragmentHandle(T* handle, bool allocated_by_allocator)
: handle_(handle), allocated_by_allocator_(allocated_by_allocator) {}
void DestroyHandle();
T* Unwrap() {
T* result = handle_;
handle_ = nullptr;
return result;
}
T* handle_ = nullptr;
// TODO(ctiller): remove this once promise_based_filter goes away.
// This bit determines whether the pointer is allocated by a metadata
// allocator or some other system. If it's held by a metadata allocator, we'll
// release it back when we're done with it.
bool allocated_by_allocator_ = false;
};
// Server metadata type
// TODO(ctiller): This should be a bespoke instance of MetadataMap<>
using ServerMetadata = grpc_metadata_batch;
using ServerMetadataHandle = FragmentHandle<ServerMetadata>;
// Client initial metadata type
// TODO(ctiller): This should be a bespoke instance of MetadataMap<>
using ClientMetadata = grpc_metadata_batch;
using ClientMetadataHandle = FragmentHandle<ClientMetadata>;
class Message {
public:
Message() = default;
~Message() = default;
Message(SliceBuffer payload, uint32_t flags)
: payload_(std::move(payload)), flags_(flags) {}
Message(const Message&) = delete;
Message& operator=(const Message&) = delete;
uint32_t flags() const { return flags_; }
SliceBuffer* payload() { return &payload_; }
const SliceBuffer* payload() const { return &payload_; }
private:
SliceBuffer payload_;
uint32_t flags_ = 0;
};
using MessageHandle = FragmentHandle<Message>;
// Ok/not-ok check for trailing metadata, so that it can be used as result types
// for TrySeq.
inline bool IsStatusOk(const ServerMetadataHandle& m) {
return m->get(GrpcStatusMetadata()).value_or(GRPC_STATUS_UNKNOWN) ==
GRPC_STATUS_OK;
}
// Within a call arena we need metadata at least four times - (client,server) x
// (initial,trailing), and possibly more for early returning promises.
// Since we often don't need these *simultaneously*, we can save memory by
// allocating/releasing them.
// We'd still like the memory to be part of the arena though, so this type
// creates a small free list of metadata objects and a central (call context)
// based place to create/destroy them.
class FragmentAllocator {
public:
FragmentAllocator() = default;
~FragmentAllocator() = default;
FragmentAllocator(const FragmentAllocator&) = delete;
FragmentAllocator& operator=(const FragmentAllocator&) = delete;
ClientMetadataHandle MakeClientMetadata() {
auto* node = AllocateNode();
// TODO(ctiller): once we finish the promise transition, have metadata map
// know about arena contexts and allocate directly from there.
// (we could do so before, but there's enough places where we don't have a
// promise context up that it's too much whackamole)
new (&node->batch) ClientMetadata(GetContext<Arena>());
return ClientMetadataHandle(&node->batch, true);
}
ServerMetadataHandle MakeServerMetadata() { return MakeClientMetadata(); }
template <typename... Args>
MessageHandle MakeMessage(Args&&... args) {
auto* node = AllocateNode();
new (&node->message) Message(std::forward<Args>(args)...);
return MessageHandle(&node->message, true);
}
private:
union Node {
Node* next_free;
grpc_metadata_batch batch;
Message message;
};
template <typename T>
friend class FragmentHandle;
void Delete(grpc_metadata_batch* p);
void Delete(Message* m);
Node* AllocateNode();
void FreeNode(Node* node);
Node* free_list_ = nullptr;
};
template <>
struct ContextType<FragmentAllocator> {};
template <typename T>
FragmentHandle<T>::FragmentHandle(const absl::Status& status) {
// TODO(ctiller): currently we guarantee that MetadataAllocator is only
// present for promise based calls, and if we're using promise_based_filter
// it's not present. If we're in a promise based call, the correct thing is to
// use the metadata allocator to track the memory we need. If we're not, we
// need to do the hacky thing promise_based_filter does.
// This all goes away when promise_based_filter goes away, and this code will
// just assume there's an allocator present and move forward.
if (auto* allocator = GetContext<FragmentAllocator>()) {
handle_ = nullptr;
allocated_by_allocator_ = false;
*this = allocator->MakeServerMetadata();
} else {
handle_ = GetContext<Arena>()->New<T>(GetContext<Arena>());
allocated_by_allocator_ = false;
}
handle_->Set(GrpcStatusMetadata(),
static_cast<grpc_status_code>(status.code()));
if (status.ok()) return;
handle_->Set(GrpcMessageMetadata(),
Slice::FromCopiedString(status.message()));
}
template <typename T>
void FragmentHandle<T>::DestroyHandle() {
if (allocated_by_allocator_) {
GetContext<FragmentAllocator>()->Delete(handle_);
}
}
} // namespace grpc_core
#endif // GRPC_CORE_LIB_TRANSPORT_CALL_FRAGMENTS_H

@ -387,6 +387,14 @@ struct GrpcStatusContext {
static const std::string& DisplayValue(const std::string& x);
};
// Annotation added by a transport to note that the status came from the wire.
struct GrpcStatusFromWire {
static absl::string_view DebugKey() { return "GrpcStatusFromWire"; }
static constexpr bool kRepeatable = false;
using ValueType = bool;
static absl::string_view DisplayValue(bool x) { return x ? "true" : "false"; }
};
// Annotation added by client surface code to denote wait-for-ready state
struct WaitForReady {
struct ValueType {
@ -645,6 +653,13 @@ struct AdaptDisplayValueToLog<const std::string&> {
static std::string ToString(const std::string& value) { return value; }
};
template <>
struct AdaptDisplayValueToLog<absl::string_view> {
static std::string ToString(absl::string_view value) {
return std::string(value);
}
};
template <>
struct AdaptDisplayValueToLog<Slice> {
static std::string ToString(Slice value) {
@ -1301,7 +1316,8 @@ using grpc_metadata_batch_base = grpc_core::MetadataMap<
grpc_core::LbCostBinMetadata, grpc_core::LbTokenMetadata,
// Non-encodable things
grpc_core::GrpcStreamNetworkState, grpc_core::PeerString,
grpc_core::GrpcStatusContext, grpc_core::WaitForReady>;
grpc_core::GrpcStatusContext, grpc_core::GrpcStatusFromWire,
grpc_core::WaitForReady>;
struct grpc_metadata_batch : public grpc_metadata_batch_base {
using grpc_metadata_batch_base::grpc_metadata_batch_base;

@ -24,6 +24,8 @@
#include <new>
#include "absl/status/status.h"
#include "src/core/lib/gpr/alloc.h"
#include "src/core/lib/iomgr/exec_ctx.h"
#include "src/core/lib/iomgr/executor.h"
@ -183,6 +185,36 @@ void grpc_transport_stream_op_batch_queue_finish_with_failure(
}
}
void grpc_transport_stream_op_batch_finish_with_failure_from_transport(
grpc_transport_stream_op_batch* batch, grpc_error_handle error) {
if (batch->cancel_stream) {
GRPC_ERROR_UNREF(batch->payload->cancel_stream.cancel_error);
}
// Construct a list of closures to execute.
if (batch->recv_initial_metadata) {
grpc_core::ExecCtx::Run(
DEBUG_LOCATION,
batch->payload->recv_initial_metadata.recv_initial_metadata_ready,
GRPC_ERROR_REF(error));
}
if (batch->recv_message) {
grpc_core::ExecCtx::Run(DEBUG_LOCATION,
batch->payload->recv_message.recv_message_ready,
GRPC_ERROR_REF(error));
}
if (batch->recv_trailing_metadata) {
grpc_core::ExecCtx::Run(
DEBUG_LOCATION,
batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready,
GRPC_ERROR_REF(error));
}
if (batch->on_complete != nullptr) {
grpc_core::ExecCtx::Run(DEBUG_LOCATION, batch->on_complete,
GRPC_ERROR_REF(error));
}
GRPC_ERROR_UNREF(error);
}
struct made_transport_op {
grpc_closure outer_on_complete;
grpc_closure* inner_on_complete = nullptr;

@ -28,7 +28,6 @@
#include <functional>
#include <string>
#include "absl/status/status.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
@ -50,11 +49,11 @@
#include "src/core/lib/iomgr/iomgr_fwd.h"
#include "src/core/lib/iomgr/polling_entity.h"
#include "src/core/lib/promise/arena_promise.h"
#include "src/core/lib/promise/context.h"
#include "src/core/lib/promise/latch.h"
#include "src/core/lib/promise/pipe.h"
#include "src/core/lib/resource_quota/arena.h"
#include "src/core/lib/slice/slice.h"
#include "src/core/lib/slice/slice_buffer.h"
#include "src/core/lib/transport/call_fragments.h"
#include "src/core/lib/transport/connectivity_state.h"
#include "src/core/lib/transport/metadata_batch.h"
#include "src/core/lib/transport/transport_fwd.h"
@ -81,83 +80,21 @@ struct grpc_transport_stream_op_batch_payload;
(GRPC_WRITE_INTERNAL_COMPRESS | GRPC_WRITE_INTERNAL_TEST_ONLY_WAS_COMPRESSED)
namespace grpc_core {
// TODO(ctiller): eliminate once MetadataHandle is constructable directly.
namespace promise_filter_detail {
class BaseCallData;
}
// Small unowned "handle" type to ensure one accessor at a time to metadata.
// The focus here is to get promises to use the syntax we'd like - we'll
// probably substitute some other smart pointer later.
template <typename T>
class MetadataHandle {
public:
MetadataHandle() = default;
MetadataHandle(const MetadataHandle&) = delete;
MetadataHandle& operator=(const MetadataHandle&) = delete;
MetadataHandle(MetadataHandle&& other) noexcept : handle_(other.handle_) {
other.handle_ = nullptr;
}
MetadataHandle& operator=(MetadataHandle&& other) noexcept {
handle_ = other.handle_;
other.handle_ = nullptr;
return *this;
}
explicit MetadataHandle(const absl::Status& status) {
handle_ = GetContext<Arena>()->New<T>(GetContext<Arena>());
handle_->Set(GrpcStatusMetadata(),
static_cast<grpc_status_code>(status.code()));
if (status.ok()) return;
handle_->Set(GrpcMessageMetadata(),
Slice::FromCopiedString(status.message()));
}
T* operator->() const { return handle_; }
bool has_value() const { return handle_ != nullptr; }
T* get() const { return handle_; }
static MetadataHandle TestOnlyWrap(T* p) { return MetadataHandle(p); }
private:
friend class promise_filter_detail::BaseCallData;
explicit MetadataHandle(T* handle) : handle_(handle) {}
T* Unwrap() {
T* result = handle_;
handle_ = nullptr;
return result;
}
T* handle_ = nullptr;
};
// Server metadata type
// TODO(ctiller): This should be a bespoke instance of MetadataMap<>
using ServerMetadata = grpc_metadata_batch;
using ServerMetadataHandle = MetadataHandle<ServerMetadata>;
// Ok/not-ok check for trailing metadata, so that it can be used as result types
// for TrySeq.
inline bool IsStatusOk(const ServerMetadataHandle& m) {
return m->get(GrpcStatusMetadata()).value_or(GRPC_STATUS_UNKNOWN) ==
GRPC_STATUS_OK;
}
// Client initial metadata type
// TODO(ctiller): This should be a bespoke instance of MetadataMap<>
using ClientMetadata = grpc_metadata_batch;
using ClientMetadataHandle = MetadataHandle<ClientMetadata>;
// Server initial metadata type
// TODO(ctiller): This should be a bespoke instance of MetadataMap<>
using ServerMetadataHandle = MetadataHandle<grpc_metadata_batch>;
struct CallArgs {
// Initial metadata from the client to the server.
// During promise setup this can be manipulated by filters (and then
// passed on to the next filter).
ClientMetadataHandle client_initial_metadata;
// Initial metadata from the server to the client.
// Set once when it's available.
// During promise setup filters can substitute their own latch for this
// and consequently intercept the sent value and mutate/observe it.
Latch<ServerMetadata*>* server_initial_metadata;
// Messages travelling from the application to the transport.
PipeReceiver<MessageHandle>* outgoing_messages;
// Messages travelling from the transport to the application.
PipeSender<MessageHandle>* incoming_messages;
};
using NextPromiseFactory =
@ -538,6 +475,10 @@ void grpc_transport_stream_op_batch_finish_with_failure(
void grpc_transport_stream_op_batch_queue_finish_with_failure(
grpc_transport_stream_op_batch* batch, grpc_error_handle error,
grpc_core::CallCombinerClosureList* closures);
// Fail a batch from within the transport (i.e. without the activity lock/call
// combiner taken).
void grpc_transport_stream_op_batch_finish_with_failure_from_transport(
grpc_transport_stream_op_batch* batch, grpc_error_handle error);
std::string grpc_transport_stream_op_batch_string(
grpc_transport_stream_op_batch* op);

@ -30,6 +30,7 @@
#include "src/core/lib/iomgr/iomgr_fwd.h"
#include "src/core/lib/promise/arena_promise.h"
#include "src/core/lib/resource_quota/arena.h"
#include "src/core/lib/transport/call_fragments.h"
#include "src/core/lib/transport/transport.h"
#include "src/core/lib/transport/transport_fwd.h"
@ -56,7 +57,7 @@ typedef struct grpc_transport_vtable {
There is an on-going migration to move all filters to providing this, and
then to drop perform_stream_op. */
grpc_core::ArenaPromise<grpc_core::ServerMetadataHandle> (*make_call_promise)(
grpc_transport* self, grpc_core::ClientMetadataHandle initial_metadata);
grpc_transport* self, grpc_core::CallArgs call_args);
/* implementation of grpc_transport_set_pollset */
void (*set_pollset)(grpc_transport* self, grpc_stream* stream,

@ -685,6 +685,7 @@ CORE_SOURCE_FILES = [
'src/core/lib/surface/call.cc',
'src/core/lib/surface/call_details.cc',
'src/core/lib/surface/call_log_batch.cc',
'src/core/lib/surface/call_trace.cc',
'src/core/lib/surface/channel.cc',
'src/core/lib/surface/channel_init.cc',
'src/core/lib/surface/channel_ping.cc',
@ -700,6 +701,7 @@ CORE_SOURCE_FILES = [
'src/core/lib/surface/validate_metadata.cc',
'src/core/lib/surface/version.cc',
'src/core/lib/transport/bdp_estimator.cc',
'src/core/lib/transport/call_fragments.cc',
'src/core/lib/transport/connectivity_state.cc',
'src/core/lib/transport/error_utils.cc',
'src/core/lib/transport/handshaker.cc',

@ -46,6 +46,7 @@ typedef struct grpc_end2end_test_config grpc_end2end_test_config;
#define FEATURE_MASK_DOES_NOT_SUPPORT_NETWORK_STATUS_CHANGE 256
#define FEATURE_MASK_SUPPORTS_WORKAROUNDS 512
#define FEATURE_MASK_DOES_NOT_SUPPORT_CLIENT_HANDSHAKE_COMPLETE_FIRST 1024
#define FEATURE_MASK_DOES_NOT_SUPPORT_DEADLINES 2048
#define FAIL_AUTH_CHECK_SERVER_ARG_NAME "fail_auth_check"

@ -27,7 +27,6 @@
#include <grpc/support/log.h>
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/gprpp/env.h"
#include "src/core/lib/gprpp/host_port.h"
#include "test/core/end2end/end2end_tests.h"
#include "test/core/end2end/fixtures/http_proxy_fixture.h"
@ -75,9 +74,12 @@ void chttp2_init_client_fullstack(grpc_end2end_test_fixture* f,
absl::StrFormat("http://%s@%s", proxy_auth_str,
grpc_end2end_http_proxy_get_proxy_name(ffd->proxy));
}
grpc_core::SetEnv("http_proxy", proxy_uri.c_str());
grpc_channel_credentials* creds = grpc_insecure_credentials_create();
f->client = grpc_channel_create(ffd->server_addr.c_str(), creds, client_args);
f->client = grpc_channel_create(ffd->server_addr.c_str(), creds,
grpc_core::ChannelArgs::FromC(client_args)
.Set(GRPC_ARG_HTTP_PROXY, proxy_uri)
.ToC()
.get());
grpc_channel_credentials_release(creds);
GPR_ASSERT(f->client);
}

@ -0,0 +1,176 @@
/*
*
* Copyright 2015 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.
*
*/
#include <string.h>
#include <memory>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include <grpc/grpc.h>
#include <grpc/status.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/channel/channel_args_preconditioning.h"
#include "src/core/lib/channel/channelz.h"
#include "src/core/lib/config/core_configuration.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/iomgr/endpoint.h"
#include "src/core/lib/iomgr/endpoint_pair.h"
#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/iomgr/exec_ctx.h"
#include "src/core/lib/surface/channel.h"
#include "src/core/lib/surface/channel_stack_type.h"
#include "src/core/lib/surface/completion_queue.h"
#include "src/core/lib/surface/server.h"
#include "src/core/lib/transport/transport.h"
#include "src/core/lib/transport/transport_fwd.h"
#include "test/core/end2end/end2end_tests.h"
#include "test/core/util/test_config.h"
/* chttp2 transport that is immediately available (used for testing
connected_channel without a client_channel) */
struct custom_fixture_data {
grpc_endpoint_pair ep;
};
static void server_setup_transport(void* ts, grpc_transport* transport) {
grpc_end2end_test_fixture* f = static_cast<grpc_end2end_test_fixture*>(ts);
grpc_core::ExecCtx exec_ctx;
custom_fixture_data* fixture_data =
static_cast<custom_fixture_data*>(f->fixture_data);
grpc_endpoint_add_to_pollset(fixture_data->ep.server, grpc_cq_pollset(f->cq));
grpc_core::Server* core_server = grpc_core::Server::FromC(f->server);
grpc_error_handle error = core_server->SetupTransport(
transport, nullptr, core_server->channel_args(), nullptr);
if (error == GRPC_ERROR_NONE) {
grpc_chttp2_transport_start_reading(transport, nullptr, nullptr, nullptr);
} else {
GRPC_ERROR_UNREF(error);
grpc_transport_destroy(transport);
}
}
typedef struct {
grpc_end2end_test_fixture* f;
const grpc_channel_args* client_args;
} sp_client_setup;
static void client_setup_transport(void* ts, grpc_transport* transport) {
sp_client_setup* cs = static_cast<sp_client_setup*>(ts);
auto args = grpc_core::ChannelArgs::FromC(cs->client_args)
.Set(GRPC_ARG_MINIMAL_STACK, true)
.Set(GRPC_ARG_DEFAULT_AUTHORITY, "test-authority");
auto channel = grpc_core::Channel::Create(
"socketpair-target", args, GRPC_CLIENT_DIRECT_CHANNEL, transport);
if (channel.ok()) {
cs->f->client = channel->release()->c_ptr();
grpc_chttp2_transport_start_reading(transport, nullptr, nullptr, nullptr);
} else {
cs->f->client = grpc_lame_client_channel_create(
nullptr, static_cast<grpc_status_code>(channel.status().code()),
"lame channel");
grpc_transport_destroy(transport);
}
}
static grpc_end2end_test_fixture chttp2_create_fixture_socketpair(
const grpc_channel_args* /*client_args*/,
const grpc_channel_args* /*server_args*/) {
custom_fixture_data* fixture_data = static_cast<custom_fixture_data*>(
gpr_malloc(sizeof(custom_fixture_data)));
grpc_end2end_test_fixture f;
memset(&f, 0, sizeof(f));
f.fixture_data = fixture_data;
f.cq = grpc_completion_queue_create_for_next(nullptr);
fixture_data->ep = grpc_iomgr_create_endpoint_pair("fixture", nullptr);
return f;
}
static void chttp2_init_client_socketpair(
grpc_end2end_test_fixture* f, const grpc_channel_args* client_args) {
grpc_core::ExecCtx exec_ctx;
auto* fixture_data = static_cast<custom_fixture_data*>(f->fixture_data);
grpc_transport* transport;
sp_client_setup cs;
auto final_client_args = grpc_core::CoreConfiguration::Get()
.channel_args_preconditioning()
.PreconditionChannelArgs(client_args)
.Set(GRPC_ARG_MINIMAL_STACK, true);
auto c_client_args = final_client_args.ToC();
cs.client_args = c_client_args.get();
cs.f = f;
transport = grpc_create_chttp2_transport(final_client_args,
fixture_data->ep.client, true);
client_setup_transport(&cs, transport);
GPR_ASSERT(f->client);
}
static void chttp2_init_server_socketpair(
grpc_end2end_test_fixture* f, const grpc_channel_args* server_args) {
grpc_core::ExecCtx exec_ctx;
auto* fixture_data = static_cast<custom_fixture_data*>(f->fixture_data);
grpc_transport* transport;
GPR_ASSERT(!f->server);
f->server = grpc_server_create(server_args, nullptr);
grpc_server_register_completion_queue(f->server, f->cq, nullptr);
grpc_server_start(f->server);
auto final_server_args = grpc_core::CoreConfiguration::Get()
.channel_args_preconditioning()
.PreconditionChannelArgs(server_args)
.Set(GRPC_ARG_MINIMAL_STACK, true);
transport = grpc_create_chttp2_transport(final_server_args,
fixture_data->ep.server, false);
server_setup_transport(f, transport);
}
static void chttp2_tear_down_socketpair(grpc_end2end_test_fixture* f) {
grpc_core::ExecCtx exec_ctx;
gpr_free(f->fixture_data);
}
/* All test configurations */
static grpc_end2end_test_config configs[] = {
{"chttp2/socketpair+minstack",
FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER |
FEATURE_MASK_DOES_NOT_SUPPORT_DEADLINES,
nullptr, chttp2_create_fixture_socketpair, chttp2_init_client_socketpair,
chttp2_init_server_socketpair, chttp2_tear_down_socketpair},
};
int main(int argc, char** argv) {
size_t i;
grpc::testing::TestEnvironment env(&argc, argv);
grpc_end2end_tests_pre_init();
grpc_init();
for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) {
grpc_end2end_tests(argc, argv, configs[i]);
}
grpc_shutdown();
return 0;
}

@ -33,6 +33,7 @@ def _fixture_options(
is_inproc = False,
is_1byte = False,
is_http2 = True,
is_minstack = False,
supports_proxy_auth = False,
supports_write_buffering = True,
client_channel = True,
@ -51,6 +52,7 @@ def _fixture_options(
is_inproc = is_inproc,
is_1byte = is_1byte,
is_http2 = is_http2,
is_minstack = is_minstack,
supports_proxy_auth = supports_proxy_auth,
supports_write_buffering = supports_write_buffering,
client_channel = client_channel,
@ -95,6 +97,12 @@ END2END_FIXTURES = {
dns_resolver = False,
client_channel = False,
),
"h2_sockpair_with_minstack": _fixture_options(
fullstack = False,
dns_resolver = False,
client_channel = False,
is_minstack = True,
),
"h2_sockpair+trace": _fixture_options(
fullstack = False,
dns_resolver = False,
@ -165,6 +173,7 @@ def _test_options(
traceable = False,
exclude_inproc = False,
exclude_1byte = False,
exclude_minstack = False,
needs_http2 = False,
needs_proxy_auth = False,
needs_write_buffering = False,
@ -182,6 +191,7 @@ def _test_options(
traceable = traceable,
exclude_inproc = exclude_inproc,
exclude_1byte = exclude_1byte,
exclude_minstack = exclude_minstack,
needs_http2 = needs_http2,
needs_proxy_auth = needs_proxy_auth,
needs_write_buffering = needs_write_buffering,
@ -217,7 +227,7 @@ END2END_TESTS = {
"cancel_in_a_vacuum": _test_options(),
"cancel_with_status": _test_options(),
"client_streaming": _test_options(),
"compressed_payload": _test_options(proxyable = False, exclude_inproc = True),
"compressed_payload": _test_options(proxyable = False, exclude_inproc = True, exclude_minstack = True),
"connectivity": _test_options(
needs_fullstack = True,
needs_names = True,
@ -249,11 +259,12 @@ END2END_TESTS = {
"max_concurrent_streams": _test_options(
proxyable = False,
exclude_inproc = True,
exclude_minstack = True,
),
"max_connection_age": _test_options(exclude_inproc = True),
"max_connection_idle": _test_options(needs_fullstack = True, proxyable = False),
"max_message_length": _test_options(),
"negative_deadline": _test_options(),
"max_message_length": _test_options(exclude_minstack = True),
"negative_deadline": _test_options(exclude_minstack = True),
"no_error_on_hotpath": _test_options(proxyable = False),
"no_logging": _test_options(traceable = False),
"no_op": _test_options(),
@ -389,6 +400,9 @@ def _compatible(fopt, topt):
if topt.exclude_1byte:
if fopt.is_1byte:
return False
if topt.exclude_minstack:
if fopt.is_minstack:
return False
if topt.needs_http2:
if not fopt.is_http2:
return False

@ -257,6 +257,10 @@ void cancel_after_accept(grpc_end2end_test_config config) {
unsigned i;
for (i = 0; i < GPR_ARRAY_SIZE(cancellation_modes); i++) {
if (config.feature_mask & FEATURE_MASK_DOES_NOT_SUPPORT_DEADLINES &&
cancellation_modes[i].expect_status == GRPC_STATUS_DEADLINE_EXCEEDED) {
continue;
}
test_cancel_after_accept(config, cancellation_modes[i],
false /* use_service_config */);
if (config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL &&

@ -231,6 +231,10 @@ void cancel_after_client_done(grpc_end2end_test_config config) {
unsigned i;
for (i = 0; i < GPR_ARRAY_SIZE(cancellation_modes); i++) {
if (config.feature_mask & FEATURE_MASK_DOES_NOT_SUPPORT_DEADLINES &&
cancellation_modes[i].expect_status == GRPC_STATUS_DEADLINE_EXCEEDED) {
continue;
}
test_cancel_after_accept_and_writes_closed(config, cancellation_modes[i]);
}
}

@ -187,6 +187,11 @@ void cancel_after_invoke(grpc_end2end_test_config config) {
for (j = 3; j < 6; j++) {
for (i = 0; i < GPR_ARRAY_SIZE(cancellation_modes); i++) {
if (config.feature_mask & FEATURE_MASK_DOES_NOT_SUPPORT_DEADLINES &&
cancellation_modes[i].expect_status ==
GRPC_STATUS_DEADLINE_EXCEEDED) {
continue;
}
test_cancel_after_invoke(config, cancellation_modes[i], j);
}
}

@ -292,6 +292,10 @@ void cancel_after_round_trip(grpc_end2end_test_config config) {
unsigned i;
for (i = 0; i < GPR_ARRAY_SIZE(cancellation_modes); i++) {
if (config.feature_mask & FEATURE_MASK_DOES_NOT_SUPPORT_DEADLINES &&
cancellation_modes[i].expect_status == GRPC_STATUS_DEADLINE_EXCEEDED) {
continue;
}
test_cancel_after_round_trip(config, cancellation_modes[i],
false /* use_service_config */);
if (config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL &&

@ -160,7 +160,11 @@ static void test_cancel_before_invoke(grpc_end2end_test_config config,
error = grpc_call_start_batch(c, ops, test_ops, tag(1), nullptr);
GPR_ASSERT(GRPC_CALL_OK == error);
cqv.Expect(tag(1), true);
// Filter based stack tracks this as a failed op, promise based stack tracks
// it as a successful one with a failed request. The latter probably makes
// more sense, but since we can't tell from outside which case we have we
// accept either.
cqv.Expect(tag(1), grpc_core::CqVerifier::AnyStatus());
cqv.Verify();
GPR_ASSERT(status == GRPC_STATUS_CANCELLED);

@ -20,6 +20,8 @@
#include <stdio.h>
#include <string.h>
#include <string>
#include <grpc/grpc.h>
#include <grpc/impl/codegen/propagation_bits.h>
#include <grpc/slice.h>
@ -28,6 +30,7 @@
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
#include "src/core/lib/surface/event_string.h"
#include "test/core/end2end/cq_verifier.h"
#include "test/core/end2end/end2end_tests.h"
#include "test/core/util/test_config.h"
@ -68,6 +71,7 @@ static void shutdown_server(grpc_end2end_test_fixture* f) {
grpc_server_shutdown_and_notify(f->server, f->cq, tag(1000));
grpc_event ev = grpc_completion_queue_next(
f->cq, grpc_timeout_seconds_to_deadline(5), nullptr);
gpr_log(GPR_DEBUG, "shutdown event: %s", grpc_event_string(&ev).c_str());
GPR_ASSERT(ev.type == GRPC_OP_COMPLETE);
GPR_ASSERT(ev.tag == tag(1000));
grpc_server_destroy(f->server);

@ -19,6 +19,10 @@
#include <stdint.h>
#include <string.h>
#include <string>
#include "absl/strings/str_cat.h"
#include <grpc/byte_buffer.h>
#include <grpc/grpc.h>
#include <grpc/impl/codegen/propagation_bits.h>
@ -94,8 +98,18 @@ static void test_invoke_request_with_flags(
grpc_slice_from_copied_string("hello world");
grpc_byte_buffer* request_payload =
grpc_raw_byte_buffer_create(&request_payload_slice, 1);
grpc_end2end_test_fixture f =
begin_test(config, "test_invoke_request_with_flags", nullptr, nullptr);
grpc_end2end_test_fixture f = begin_test(
config,
absl::StrCat("test_invoke_request_with_flags[",
absl::Hex(flags_for_op[GRPC_OP_SEND_INITIAL_METADATA]), ",",
absl::Hex(flags_for_op[GRPC_OP_SEND_MESSAGE]), ",",
absl::Hex(flags_for_op[GRPC_OP_SEND_CLOSE_FROM_CLIENT]), ",",
absl::Hex(flags_for_op[GRPC_OP_RECV_INITIAL_METADATA]), ",",
absl::Hex(flags_for_op[GRPC_OP_RECV_STATUS_ON_CLIENT]),
"]=>",
grpc_call_error_to_string(call_start_batch_expected_result))
.c_str(),
nullptr, nullptr);
grpc_core::CqVerifier cqv(f.cq);
grpc_op ops[6];
grpc_op* op;
@ -154,6 +168,9 @@ static void test_invoke_request_with_flags(
GPR_ASSERT(expectation == error);
if (expectation == GRPC_CALL_OK) {
if (config.feature_mask & FEATURE_MASK_DOES_NOT_SUPPORT_DEADLINES) {
GPR_ASSERT(GRPC_CALL_OK == grpc_call_cancel(c, nullptr));
}
cqv.Expect(tag(1), true);
cqv.Verify();
grpc_slice_unref(details);

@ -41,6 +41,7 @@ static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
grpc_channel_args* client_args,
grpc_channel_args* server_args) {
grpc_end2end_test_fixture f;
gpr_log(GPR_INFO, "%s", std::string(100, '*').c_str());
gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
f = config.create_fixture(client_args, server_args);
config.init_server(&f, server_args);

@ -225,6 +225,8 @@ static void test(grpc_end2end_test_config config, bool request_status_early,
cqv.Expect(tag(2), true);
cqv.Verify();
GPR_ASSERT(response_payload2_recv != nullptr);
}
// Cancel the call so that the client sets up an error status.

@ -49,6 +49,7 @@
#include "src/core/lib/security/security_connector/security_connector.h"
#include "src/core/lib/security/transport/auth_filters.h"
#include "src/core/lib/slice/slice.h"
#include "src/core/lib/transport/call_fragments.h"
#include "src/core/lib/transport/metadata_batch.h"
#include "src/core/lib/transport/transport.h"
#include "test/core/promise/test_context.h"
@ -153,10 +154,8 @@ TEST_F(ClientAuthFilterTest, CallCredsFails) {
TestContext<Arena> context(arena_.get());
TestContext<grpc_call_context_element> promise_call_context(call_context_);
auto promise = filter->MakeCallPromise(
CallArgs{
ClientMetadataHandle::TestOnlyWrap(&initial_metadata_batch_),
nullptr,
},
CallArgs{ClientMetadataHandle::TestOnlyWrap(&initial_metadata_batch_),
nullptr, nullptr, nullptr},
[&](CallArgs /*call_args*/) {
return ArenaPromise<ServerMetadataHandle>(
[&]() -> Poll<ServerMetadataHandle> {
@ -185,10 +184,8 @@ TEST_F(ClientAuthFilterTest, RewritesInvalidStatusFromCallCreds) {
TestContext<Arena> context(arena_.get());
TestContext<grpc_call_context_element> promise_call_context(call_context_);
auto promise = filter->MakeCallPromise(
CallArgs{
ClientMetadataHandle::TestOnlyWrap(&initial_metadata_batch_),
nullptr,
},
CallArgs{ClientMetadataHandle::TestOnlyWrap(&initial_metadata_batch_),
nullptr, nullptr, nullptr},
[&](CallArgs /*call_args*/) {
return ArenaPromise<ServerMetadataHandle>(
[&]() -> Poll<ServerMetadataHandle> {

@ -71,10 +71,8 @@ TEST(ClientAuthorityFilterTest, PromiseCompletesImmediatelyAndSetsAuthority) {
// TODO(ctiller): use Activity here, once it's ready.
TestContext<Arena> context(arena.get());
auto promise = filter.MakeCallPromise(
CallArgs{
ClientMetadataHandle::TestOnlyWrap(&initial_metadata_batch),
nullptr,
},
CallArgs{ClientMetadataHandle::TestOnlyWrap(&initial_metadata_batch),
nullptr, nullptr, nullptr},
[&](CallArgs call_args) {
EXPECT_EQ(call_args.client_initial_metadata
->get_pointer(HttpAuthorityMetadata())
@ -105,10 +103,8 @@ TEST(ClientAuthorityFilterTest,
// TODO(ctiller): use Activity here, once it's ready.
TestContext<Arena> context(arena.get());
auto promise = filter.MakeCallPromise(
CallArgs{
ClientMetadataHandle::TestOnlyWrap(&initial_metadata_batch),
nullptr,
},
CallArgs{ClientMetadataHandle::TestOnlyWrap(&initial_metadata_batch),
nullptr, nullptr, nullptr},
[&](CallArgs call_args) {
EXPECT_EQ(call_args.client_initial_metadata
->get_pointer(HttpAuthorityMetadata())

@ -80,6 +80,7 @@
#include "src/core/lib/slice/slice.h"
#include "src/core/lib/slice/slice_internal.h"
#include "src/core/lib/surface/channel_stack_type.h"
#include "src/core/lib/transport/call_fragments.h"
#include "src/core/lib/transport/handshaker.h"
#include "src/core/lib/transport/metadata_batch.h"
#include "src/core/lib/transport/transport.h"
@ -116,8 +117,9 @@ const grpc_transport_vtable kFakeTransportVTable = {
[](grpc_transport*, grpc_stream*, grpc_stream_refcount*, const void*,
Arena*) -> int { abort(); },
// make_call_promise
[](grpc_transport*,
ClientMetadataHandle) -> ArenaPromise<ServerMetadataHandle> { abort(); },
[](grpc_transport*, CallArgs) -> ArenaPromise<ServerMetadataHandle> {
abort();
},
// set_pollset
[](grpc_transport*, grpc_stream*, grpc_pollset*) { abort(); },
// set_pollset_set
@ -317,11 +319,12 @@ const grpc_channel_filter* FindFilter(absl::string_view name) {
class MainLoop {
public:
MainLoop(RefCountedPtr<grpc_channel_stack> channel_stack,
MainLoop(bool is_client, RefCountedPtr<grpc_channel_stack> channel_stack,
const ChannelArgs& channel_args)
: memory_allocator_(channel_args.GetObject<ResourceQuota>()
->memory_quota()
->CreateMemoryAllocator("test")),
is_client_(is_client),
channel_stack_(std::move(channel_stack)) {}
~MainLoop() {
@ -349,9 +352,9 @@ class MainLoop {
calls_.erase(action.call());
break;
case filter_fuzzer::Action::kCreateCall:
calls_.emplace(
action.call(),
std::make_unique<Call>(this, action.call(), action.create_call()));
calls_.emplace(action.call(), std::make_unique<Call>(
this, action.call(),
action.create_call(), is_client_));
break;
case filter_fuzzer::Action::kReceiveInitialMetadata:
if (auto* call = GetCall(action.call())) {
@ -407,6 +410,10 @@ class MainLoop {
}
void Drop() override { delete this; }
std::string ActivityDebugTag() const override {
return "WakeCall(" + std::to_string(id_) + ")";
}
private:
MainLoop* const main_loop_;
uint32_t id_;
@ -460,14 +467,20 @@ class MainLoop {
};
Call(MainLoop* main_loop, uint32_t id,
const filter_fuzzer::Metadata& client_initial_metadata)
const filter_fuzzer::Metadata& client_initial_metadata, bool is_client)
: main_loop_(main_loop), id_(id) {
ScopedContext context(this);
auto* server_initial_metadata = arena_->New<Latch<ServerMetadata*>>();
promise_ = main_loop_->channel_stack_->MakeCallPromise(
CallArgs{std::move(*LoadMetadata(client_initial_metadata,
&client_initial_metadata_)),
server_initial_metadata});
CallArgs call_args{std::move(*LoadMetadata(client_initial_metadata,
&client_initial_metadata_)),
server_initial_metadata, nullptr, nullptr};
if (is_client) {
promise_ = main_loop_->channel_stack_->MakeClientCallPromise(
std::move(call_args));
} else {
promise_ = main_loop_->channel_stack_->MakeServerCallPromise(
std::move(call_args));
}
Step();
}
@ -574,7 +587,7 @@ class MainLoop {
};
template <typename R>
absl::optional<MetadataHandle<R>> LoadMetadata(
absl::optional<FragmentHandle<R>> LoadMetadata(
const filter_fuzzer::Metadata& metadata, std::unique_ptr<R>* out) {
if (*out != nullptr) return absl::nullopt;
*out = std::make_unique<R>(arena_.get());
@ -582,7 +595,7 @@ class MainLoop {
(*out)->Append(md.key(), Slice::FromCopiedString(md.value()),
[](absl::string_view, const Slice&) {});
}
return MetadataHandle<R>::TestOnlyWrap(out->get());
return FragmentHandle<R>::TestOnlyWrap(out->get());
}
void Step() {
@ -626,6 +639,7 @@ class MainLoop {
}
MemoryAllocator memory_allocator_;
const bool is_client_;
RefCountedPtr<grpc_channel_stack> channel_stack_;
std::map<uint32_t, std::unique_ptr<Call>> calls_;
std::vector<uint32_t> wakeups_;
@ -681,7 +695,7 @@ DEFINE_PROTO_FUZZER(const filter_fuzzer::Msg& msg) {
}();
if (stack.ok()) {
grpc_core::MainLoop main_loop(std::move(*stack), channel_args);
grpc_core::MainLoop main_loop(is_client, std::move(*stack), channel_args);
for (const auto& action : msg.actions()) {
grpc_timer_manager_tick();
main_loop.Run(action, &globals);

@ -344,6 +344,7 @@ class TestWakeable final : public Wakeable {
drops_->fetch_add(1, std::memory_order_relaxed);
delete this;
}
std::string ActivityDebugTag() const override { return "TestWakeable"; }
private:
std::atomic<int>* const wakeups_;

@ -33,12 +33,20 @@ namespace grpc_core {
static auto* g_memory_allocator = new MemoryAllocator(
ResourceQuota::Default()->memory_quota()->CreateMemoryAllocator("test"));
TEST(ArenaPromiseTest, DefaultInitializationYieldsNoValue) {
auto arena = MakeScopedArena(1024, g_memory_allocator);
TestContext<Arena> context(arena.get());
ArenaPromise<int> p;
EXPECT_FALSE(p.has_value());
}
TEST(ArenaPromiseTest, AllocatedWorks) {
ExecCtx exec_ctx;
auto arena = MakeScopedArena(1024, g_memory_allocator);
TestContext<Arena> context(arena.get());
int x = 42;
ArenaPromise<int> p([x] { return Poll<int>(x); });
EXPECT_TRUE(p.has_value());
EXPECT_EQ(p(), Poll<int>(42));
p = ArenaPromise<int>([] { return Poll<int>(43); });
EXPECT_EQ(p(), Poll<int>(43));

@ -54,8 +54,8 @@ TEST(CallPushPullTest, OneReady) {
TEST(CallPushPullTest, OneFailed) {
auto a = CallPushPull(
[]() -> Poll<absl::Status> { return absl::UnknownError("bah"); },
[]() -> Poll<absl::Status> { return Pending{}; },
[]() -> Poll<absl::Status> { return Pending{}; });
[]() -> Poll<absl::Status> { return absl::OkStatus(); },
[]() -> Poll<absl::Status> { return absl::OkStatus(); });
EXPECT_EQ(a(), Poll<absl::Status>(absl::UnknownError("bah")));
auto b = CallPushPull(
[]() -> Poll<absl::Status> { return Pending{}; },

@ -89,6 +89,7 @@ grpc_cc_test(
srcs = ["lame_client_test.cc"],
external_deps = ["gtest"],
language = "C++",
tags = ["lame_client_test"],
deps = [
"//:gpr",
"//:grpc",

@ -30,6 +30,7 @@
#include "src/core/lib/channel/channel_fwd.h"
#include "src/core/lib/channel/channel_stack.h"
#include "src/core/lib/experiments/experiments.h"
#include "src/core/lib/gprpp/orphanable.h"
#include "src/core/lib/iomgr/closure.h"
#include "src/core/lib/iomgr/error.h"
@ -123,8 +124,11 @@ TEST(LameClientTest, MainTest) {
tag(1), nullptr);
ASSERT_EQ(GRPC_CALL_OK, error);
/* the call should immediately fail */
cqv.Expect(tag(1), false);
// Filter stack code considers this a failed to receive initial metadata
// result, where as promise based code interprets this as a trailers only
// failed request. Both are rational interpretations, so we accept the one
// that is implemented for each stack.
cqv.Expect(tag(1), grpc_core::IsPromiseBasedClientCallEnabled());
cqv.Verify();
memset(ops, 0, sizeof(ops));

@ -65,6 +65,21 @@ grpc_cc_test(
],
)
grpc_cc_test(
name = "call_fragments_test",
srcs = ["call_fragments_test.cc"],
external_deps = [
"gtest",
],
language = "C++",
deps = [
"//:gpr",
"//:grpc",
"//test/core/promise:test_context",
"//test/core/util:grpc_test_util",
],
)
grpc_cc_test(
name = "metadata_map_test",
srcs = ["metadata_map_test.cc"],

@ -0,0 +1,96 @@
//
// 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.
//
#include "src/core/lib/transport/call_fragments.h"
#include <algorithm>
#include <memory>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <grpc/event_engine/memory_allocator.h>
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/resource_quota/memory_quota.h"
#include "src/core/lib/resource_quota/resource_quota.h"
#include "test/core/promise/test_context.h"
#include "test/core/util/test_config.h"
using testing::Each;
namespace grpc_core {
namespace testing {
class CallFragmentsTest : public ::testing::Test {
protected:
CallFragmentsTest() {}
~CallFragmentsTest() override {}
private:
MemoryAllocator memory_allocator_ =
ResourceQuota::Default()->memory_quota()->CreateMemoryAllocator("test");
ScopedArenaPtr arena_ = MakeScopedArena(4096, &memory_allocator_);
FragmentAllocator fragment_allocator_;
TestContext<Arena> arena_context_{arena_.get()};
TestContext<FragmentAllocator> fragment_allocator_context_{
&fragment_allocator_};
};
// Ensure test fixture can init/destroy successfully.
TEST_F(CallFragmentsTest, Nothing) {}
// Ensure we can create/destroy some client metadata.
TEST_F(CallFragmentsTest, ClientMetadata) {
GetContext<FragmentAllocator>()->MakeClientMetadata();
}
// Ensure we can create/destroy some server metadata.
TEST_F(CallFragmentsTest, ServerMetadata) {
GetContext<FragmentAllocator>()->MakeServerMetadata();
}
// Ensure repeated allocation/deallocations reuse memory.
TEST_F(CallFragmentsTest, RepeatedAllocationsReuseMemory) {
void* p = GetContext<FragmentAllocator>()->MakeClientMetadata().get();
void* q = GetContext<FragmentAllocator>()->MakeClientMetadata().get();
EXPECT_EQ(p, q);
}
// Ensure repeated allocation reinitializes.
TEST_F(CallFragmentsTest, RepeatedAllocationsReinitialize) {
std::vector<void*> addresses;
for (int i = 0; i < 4; i++) {
ClientMetadataHandle metadata =
GetContext<FragmentAllocator>()->MakeClientMetadata();
EXPECT_EQ(metadata->get_pointer(HttpPathMetadata()), nullptr);
metadata->Set(HttpPathMetadata(), Slice::FromCopiedString("/"));
EXPECT_EQ(metadata->get_pointer(HttpPathMetadata())->as_string_view(), "/");
addresses.push_back(metadata.get());
}
EXPECT_THAT(addresses, Each(addresses[0]));
}
} // namespace testing
} // namespace grpc_core
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
grpc::testing::TestEnvironment env(&argc, argv);
return RUN_ALL_TESTS();
};

@ -2256,6 +2256,7 @@ src/core/lib/promise/intra_activity_waiter.h \
src/core/lib/promise/latch.h \
src/core/lib/promise/loop.h \
src/core/lib/promise/map.h \
src/core/lib/promise/pipe.h \
src/core/lib/promise/poll.h \
src/core/lib/promise/promise.h \
src/core/lib/promise/race.h \
@ -2432,6 +2433,8 @@ src/core/lib/surface/call.h \
src/core/lib/surface/call_details.cc \
src/core/lib/surface/call_log_batch.cc \
src/core/lib/surface/call_test_only.h \
src/core/lib/surface/call_trace.cc \
src/core/lib/surface/call_trace.h \
src/core/lib/surface/channel.cc \
src/core/lib/surface/channel.h \
src/core/lib/surface/channel_init.cc \
@ -2459,6 +2462,8 @@ src/core/lib/surface/validate_metadata.h \
src/core/lib/surface/version.cc \
src/core/lib/transport/bdp_estimator.cc \
src/core/lib/transport/bdp_estimator.h \
src/core/lib/transport/call_fragments.cc \
src/core/lib/transport/call_fragments.h \
src/core/lib/transport/connectivity_state.cc \
src/core/lib/transport/connectivity_state.h \
src/core/lib/transport/error_utils.cc \

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save