[transport] Move call spine related types into their own file (#35689)

Part of a continued hygiene effort for transport.h

(pure code movement, no refactoring at this time)

Closes #35689

COPYBARA_INTEGRATE_REVIEW=https://github.com/grpc/grpc/pull/35689 from ctiller:channel-ez 3366310cc3
PiperOrigin-RevId: 602416330
pull/35729/head
Craig Tiller 10 months ago committed by Copybara-Service
parent 5b444972f3
commit fb14685fd6
  1. 2
      BUILD
  2. 3
      CMakeLists.txt
  3. 2
      Makefile
  4. 2
      Package.swift
  5. 6
      build_autogenerated.yaml
  6. 1
      config.m4
  7. 1
      config.w32
  8. 2
      gRPC-C++.podspec
  9. 3
      gRPC-Core.podspec
  10. 2
      grpc.gemspec
  11. 3
      grpc.gyp
  12. 2
      package.xml
  13. 24
      src/core/BUILD
  14. 103
      src/core/lib/transport/call_spine.cc
  15. 418
      src/core/lib/transport/call_spine.h
  16. 86
      src/core/lib/transport/transport.cc
  17. 387
      src/core/lib/transport/transport.h
  18. 1
      src/python/grpcio/grpc_core_dependencies.py
  19. 2
      tools/doxygen/Doxyfile.c++.internal
  20. 2
      tools/doxygen/Doxyfile.core.internal

@ -1533,6 +1533,7 @@ grpc_cc_library(
"//src/core:bitset",
"//src/core:call_filters",
"//src/core:call_final_info",
"//src/core:call_spine",
"//src/core:cancel_callback",
"//src/core:channel_args",
"//src/core:channel_args_endpoint_config",
@ -1584,7 +1585,6 @@ grpc_cc_library(
"//src/core:pollset_set",
"//src/core:posix_event_engine_base_hdrs",
"//src/core:posix_event_engine_endpoint",
"//src/core:prioritized_race",
"//src/core:promise_status",
"//src/core:race",
"//src/core:random_early_detection",

3
CMakeLists.txt generated

@ -2528,6 +2528,7 @@ add_library(grpc
src/core/lib/transport/bdp_estimator.cc
src/core/lib/transport/call_filters.cc
src/core/lib/transport/call_final_info.cc
src/core/lib/transport/call_spine.cc
src/core/lib/transport/connectivity_state.cc
src/core/lib/transport/error_utils.cc
src/core/lib/transport/handshaker.cc
@ -3243,6 +3244,7 @@ add_library(grpc_unsecure
src/core/lib/transport/bdp_estimator.cc
src/core/lib/transport/call_filters.cc
src/core/lib/transport/call_final_info.cc
src/core/lib/transport/call_spine.cc
src/core/lib/transport/connectivity_state.cc
src/core/lib/transport/error_utils.cc
src/core/lib/transport/handshaker.cc
@ -5330,6 +5332,7 @@ add_library(grpc_authorization_provider
src/core/lib/transport/batch_builder.cc
src/core/lib/transport/call_filters.cc
src/core/lib/transport/call_final_info.cc
src/core/lib/transport/call_spine.cc
src/core/lib/transport/connectivity_state.cc
src/core/lib/transport/error_utils.cc
src/core/lib/transport/handshaker.cc

2
Makefile generated

@ -1710,6 +1710,7 @@ LIBGRPC_SRC = \
src/core/lib/transport/bdp_estimator.cc \
src/core/lib/transport/call_filters.cc \
src/core/lib/transport/call_final_info.cc \
src/core/lib/transport/call_spine.cc \
src/core/lib/transport/connectivity_state.cc \
src/core/lib/transport/error_utils.cc \
src/core/lib/transport/handshaker.cc \
@ -2259,6 +2260,7 @@ LIBGRPC_UNSECURE_SRC = \
src/core/lib/transport/bdp_estimator.cc \
src/core/lib/transport/call_filters.cc \
src/core/lib/transport/call_final_info.cc \
src/core/lib/transport/call_spine.cc \
src/core/lib/transport/connectivity_state.cc \
src/core/lib/transport/error_utils.cc \
src/core/lib/transport/handshaker.cc \

2
Package.swift generated

@ -1903,6 +1903,8 @@ let package = Package(
"src/core/lib/transport/call_filters.h",
"src/core/lib/transport/call_final_info.cc",
"src/core/lib/transport/call_final_info.h",
"src/core/lib/transport/call_spine.cc",
"src/core/lib/transport/call_spine.h",
"src/core/lib/transport/connectivity_state.cc",
"src/core/lib/transport/connectivity_state.h",
"src/core/lib/transport/custom_metadata.h",

@ -1179,6 +1179,7 @@ libs:
- src/core/lib/transport/bdp_estimator.h
- src/core/lib/transport/call_filters.h
- src/core/lib/transport/call_final_info.h
- src/core/lib/transport/call_spine.h
- src/core/lib/transport/connectivity_state.h
- src/core/lib/transport/custom_metadata.h
- src/core/lib/transport/error_utils.h
@ -1982,6 +1983,7 @@ libs:
- src/core/lib/transport/bdp_estimator.cc
- src/core/lib/transport/call_filters.cc
- src/core/lib/transport/call_final_info.cc
- src/core/lib/transport/call_spine.cc
- src/core/lib/transport/connectivity_state.cc
- src/core/lib/transport/error_utils.cc
- src/core/lib/transport/handshaker.cc
@ -2628,6 +2630,7 @@ libs:
- src/core/lib/transport/bdp_estimator.h
- src/core/lib/transport/call_filters.h
- src/core/lib/transport/call_final_info.h
- src/core/lib/transport/call_spine.h
- src/core/lib/transport/connectivity_state.h
- src/core/lib/transport/custom_metadata.h
- src/core/lib/transport/error_utils.h
@ -3047,6 +3050,7 @@ libs:
- src/core/lib/transport/bdp_estimator.cc
- src/core/lib/transport/call_filters.cc
- src/core/lib/transport/call_final_info.cc
- src/core/lib/transport/call_spine.cc
- src/core/lib/transport/connectivity_state.cc
- src/core/lib/transport/error_utils.cc
- src/core/lib/transport/handshaker.cc
@ -4669,6 +4673,7 @@ libs:
- src/core/lib/transport/batch_builder.h
- src/core/lib/transport/call_filters.h
- src/core/lib/transport/call_final_info.h
- src/core/lib/transport/call_spine.h
- src/core/lib/transport/connectivity_state.h
- src/core/lib/transport/custom_metadata.h
- src/core/lib/transport/error_utils.h
@ -4965,6 +4970,7 @@ libs:
- src/core/lib/transport/batch_builder.cc
- src/core/lib/transport/call_filters.cc
- src/core/lib/transport/call_final_info.cc
- src/core/lib/transport/call_spine.cc
- src/core/lib/transport/connectivity_state.cc
- src/core/lib/transport/error_utils.cc
- src/core/lib/transport/handshaker.cc

1
config.m4 generated

@ -838,6 +838,7 @@ if test "$PHP_GRPC" != "no"; then
src/core/lib/transport/bdp_estimator.cc \
src/core/lib/transport/call_filters.cc \
src/core/lib/transport/call_final_info.cc \
src/core/lib/transport/call_spine.cc \
src/core/lib/transport/connectivity_state.cc \
src/core/lib/transport/error_utils.cc \
src/core/lib/transport/handshaker.cc \

1
config.w32 generated

@ -803,6 +803,7 @@ if (PHP_GRPC != "no") {
"src\\core\\lib\\transport\\bdp_estimator.cc " +
"src\\core\\lib\\transport\\call_filters.cc " +
"src\\core\\lib\\transport\\call_final_info.cc " +
"src\\core\\lib\\transport\\call_spine.cc " +
"src\\core\\lib\\transport\\connectivity_state.cc " +
"src\\core\\lib\\transport\\error_utils.cc " +
"src\\core\\lib\\transport\\handshaker.cc " +

2
gRPC-C++.podspec generated

@ -1283,6 +1283,7 @@ Pod::Spec.new do |s|
'src/core/lib/transport/bdp_estimator.h',
'src/core/lib/transport/call_filters.h',
'src/core/lib/transport/call_final_info.h',
'src/core/lib/transport/call_spine.h',
'src/core/lib/transport/connectivity_state.h',
'src/core/lib/transport/custom_metadata.h',
'src/core/lib/transport/error_utils.h',
@ -2537,6 +2538,7 @@ Pod::Spec.new do |s|
'src/core/lib/transport/bdp_estimator.h',
'src/core/lib/transport/call_filters.h',
'src/core/lib/transport/call_final_info.h',
'src/core/lib/transport/call_spine.h',
'src/core/lib/transport/connectivity_state.h',
'src/core/lib/transport/custom_metadata.h',
'src/core/lib/transport/error_utils.h',

3
gRPC-Core.podspec generated

@ -2012,6 +2012,8 @@ Pod::Spec.new do |s|
'src/core/lib/transport/call_filters.h',
'src/core/lib/transport/call_final_info.cc',
'src/core/lib/transport/call_final_info.h',
'src/core/lib/transport/call_spine.cc',
'src/core/lib/transport/call_spine.h',
'src/core/lib/transport/connectivity_state.cc',
'src/core/lib/transport/connectivity_state.h',
'src/core/lib/transport/custom_metadata.h',
@ -3314,6 +3316,7 @@ Pod::Spec.new do |s|
'src/core/lib/transport/bdp_estimator.h',
'src/core/lib/transport/call_filters.h',
'src/core/lib/transport/call_final_info.h',
'src/core/lib/transport/call_spine.h',
'src/core/lib/transport/connectivity_state.h',
'src/core/lib/transport/custom_metadata.h',
'src/core/lib/transport/error_utils.h',

2
grpc.gemspec generated

@ -1905,6 +1905,8 @@ Gem::Specification.new do |s|
s.files += %w( src/core/lib/transport/call_filters.h )
s.files += %w( src/core/lib/transport/call_final_info.cc )
s.files += %w( src/core/lib/transport/call_final_info.h )
s.files += %w( src/core/lib/transport/call_spine.cc )
s.files += %w( src/core/lib/transport/call_spine.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/custom_metadata.h )

3
grpc.gyp generated

@ -1024,6 +1024,7 @@
'src/core/lib/transport/bdp_estimator.cc',
'src/core/lib/transport/call_filters.cc',
'src/core/lib/transport/call_final_info.cc',
'src/core/lib/transport/call_spine.cc',
'src/core/lib/transport/connectivity_state.cc',
'src/core/lib/transport/error_utils.cc',
'src/core/lib/transport/handshaker.cc',
@ -1513,6 +1514,7 @@
'src/core/lib/transport/bdp_estimator.cc',
'src/core/lib/transport/call_filters.cc',
'src/core/lib/transport/call_final_info.cc',
'src/core/lib/transport/call_spine.cc',
'src/core/lib/transport/connectivity_state.cc',
'src/core/lib/transport/error_utils.cc',
'src/core/lib/transport/handshaker.cc',
@ -2282,6 +2284,7 @@
'src/core/lib/transport/batch_builder.cc',
'src/core/lib/transport/call_filters.cc',
'src/core/lib/transport/call_final_info.cc',
'src/core/lib/transport/call_spine.cc',
'src/core/lib/transport/connectivity_state.cc',
'src/core/lib/transport/error_utils.cc',
'src/core/lib/transport/handshaker.cc',

2
package.xml generated

@ -1887,6 +1887,8 @@
<file baseinstalldir="/" name="src/core/lib/transport/call_filters.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/transport/call_final_info.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/transport/call_final_info.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/transport/call_spine.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/transport/call_spine.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/custom_metadata.h" role="src" />

@ -6788,6 +6788,30 @@ grpc_cc_library(
],
)
grpc_cc_library(
name = "call_spine",
srcs = [
"lib/transport/call_spine.cc",
],
hdrs = [
"lib/transport/call_spine.h",
],
deps = [
"1999",
"for_each",
"if",
"latch",
"message",
"metadata",
"pipe",
"prioritized_race",
"promise_status",
"status_flag",
"try_seq",
"//:gpr",
],
)
grpc_cc_library(
name = "metadata_batch",
srcs = [

@ -0,0 +1,103 @@
// Copyright 2024 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_spine.h"
namespace grpc_core {
void ForwardCall(CallHandler call_handler, CallInitiator call_initiator,
ClientMetadataHandle client_initial_metadata) {
// Send initial metadata.
call_initiator.SpawnGuarded(
"send_initial_metadata",
[client_initial_metadata = std::move(client_initial_metadata),
call_initiator]() mutable {
return call_initiator.PushClientInitialMetadata(
std::move(client_initial_metadata));
});
// Read messages from handler into initiator.
call_handler.SpawnGuarded("read_messages", [call_handler,
call_initiator]() mutable {
return Seq(ForEach(OutgoingMessages(call_handler),
[call_initiator](MessageHandle msg) mutable {
// Need to spawn a job into the initiator's activity to
// push the message in.
return call_initiator.SpawnWaitable(
"send_message",
[msg = std::move(msg), call_initiator]() mutable {
return call_initiator.CancelIfFails(
call_initiator.PushMessage(std::move(msg)));
});
}),
[call_initiator](StatusFlag result) mutable {
call_initiator.SpawnInfallible(
"finish-downstream", [call_initiator, result]() mutable {
if (result.ok()) {
call_initiator.FinishSends();
} else {
call_initiator.Cancel();
}
return Empty{};
});
return result;
});
});
call_initiator.SpawnInfallible("read_the_things", [call_initiator,
call_handler]() mutable {
return Seq(
call_initiator.CancelIfFails(TrySeq(
call_initiator.PullServerInitialMetadata(),
[call_handler,
call_initiator](absl::optional<ServerMetadataHandle> md) mutable {
const bool has_md = md.has_value();
call_handler.SpawnGuarded(
"recv_initial_metadata",
[md = std::move(md), call_handler]() mutable {
return call_handler.PushServerInitialMetadata(
std::move(md));
});
return If(
has_md,
ForEach(OutgoingMessages(call_initiator),
[call_handler](MessageHandle msg) mutable {
return call_handler.SpawnWaitable(
"recv_message",
[msg = std::move(msg), call_handler]() mutable {
return call_handler.CancelIfFails(
call_handler.PushMessage(std::move(msg)));
});
}),
[]() -> StatusFlag { return Success{}; });
})),
call_initiator.PullServerTrailingMetadata(),
[call_handler](ServerMetadataHandle md) mutable {
call_handler.SpawnGuarded(
"recv_trailing_metadata",
[md = std::move(md), call_handler]() mutable {
return call_handler.PushServerTrailingMetadata(std::move(md));
});
return Empty{};
});
});
}
CallInitiatorAndHandler MakeCall(
grpc_event_engine::experimental::EventEngine* event_engine, Arena* arena) {
auto spine = CallSpine::Create(event_engine, arena);
return {CallInitiator(spine), CallHandler(spine)};
}
} // namespace grpc_core

@ -0,0 +1,418 @@
// Copyright 2024 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef GRPC_SRC_CORE_LIB_TRANSPORT_CALL_SPINE_H
#define GRPC_SRC_CORE_LIB_TRANSPORT_CALL_SPINE_H
#include <grpc/support/port_platform.h>
#include <grpc/support/log.h>
#include "src/core/lib/promise/detail/status.h"
#include "src/core/lib/promise/for_each.h"
#include "src/core/lib/promise/if.h"
#include "src/core/lib/promise/latch.h"
#include "src/core/lib/promise/party.h"
#include "src/core/lib/promise/pipe.h"
#include "src/core/lib/promise/prioritized_race.h"
#include "src/core/lib/promise/status_flag.h"
#include "src/core/lib/promise/try_seq.h"
#include "src/core/lib/transport/message.h"
#include "src/core/lib/transport/metadata.h"
namespace grpc_core {
// The common middle part of a call - a reference is held by each of
// CallInitiator and CallHandler - which provide interfaces that are appropriate
// for each side of a call.
// The spine will ultimately host the pipes, filters, and context for one part
// of a call: ie top-half client channel, sub channel call, server call.
// TODO(ctiller): eventually drop this when we don't need to reference into
// legacy promise calls anymore
class CallSpineInterface {
public:
virtual ~CallSpineInterface() = default;
virtual Pipe<ClientMetadataHandle>& client_initial_metadata() = 0;
virtual Pipe<ServerMetadataHandle>& server_initial_metadata() = 0;
virtual Pipe<MessageHandle>& client_to_server_messages() = 0;
virtual Pipe<MessageHandle>& server_to_client_messages() = 0;
virtual Pipe<ServerMetadataHandle>& server_trailing_metadata() = 0;
virtual Latch<ServerMetadataHandle>& cancel_latch() = 0;
// Add a callback to be called when server trailing metadata is received.
void OnDone(absl::AnyInvocable<void()> fn) {
if (on_done_ == nullptr) {
on_done_ = std::move(fn);
return;
}
on_done_ = [first = std::move(fn), next = std::move(on_done_)]() mutable {
first();
next();
};
}
void CallOnDone() {
if (on_done_ != nullptr) std::exchange(on_done_, nullptr)();
}
virtual Party& party() = 0;
virtual void IncrementRefCount() = 0;
virtual void Unref() = 0;
// Cancel the call with the given metadata.
// Regarding the `MUST_USE_RESULT absl::nullopt_t`:
// Most cancellation calls right now happen in pipe interceptors;
// there `nullopt` indicates terminate processing of this pipe and close with
// error.
// It's convenient then to have the Cancel operation (setting the latch to
// terminate the call) be the last thing that occurs in a pipe interceptor,
// and this construction supports that (and has helped the author not write
// some bugs).
GRPC_MUST_USE_RESULT absl::nullopt_t Cancel(ServerMetadataHandle metadata) {
GPR_DEBUG_ASSERT(GetContext<Activity>() == &party());
auto& c = cancel_latch();
if (c.is_set()) return absl::nullopt;
c.Set(std::move(metadata));
CallOnDone();
client_initial_metadata().sender.CloseWithError();
server_initial_metadata().sender.CloseWithError();
client_to_server_messages().sender.CloseWithError();
server_to_client_messages().sender.CloseWithError();
return absl::nullopt;
}
auto WaitForCancel() {
GPR_DEBUG_ASSERT(GetContext<Activity>() == &party());
return cancel_latch().Wait();
}
// Wrap a promise so that if it returns failure it automatically cancels
// the rest of the call.
// The resulting (returned) promise will resolve to Empty.
template <typename Promise>
auto CancelIfFails(Promise promise) {
GPR_DEBUG_ASSERT(GetContext<Activity>() == &party());
using P = promise_detail::PromiseLike<Promise>;
using ResultType = typename P::Result;
return Map(std::move(promise), [this](ResultType r) {
if (!IsStatusOk(r)) {
std::ignore = Cancel(StatusCast<ServerMetadataHandle>(r));
}
return r;
});
}
// Spawn a promise that returns Empty{} and save some boilerplate handling
// that detail.
template <typename PromiseFactory>
void SpawnInfallible(absl::string_view name, PromiseFactory promise_factory) {
party().Spawn(name, std::move(promise_factory), [](Empty) {});
}
// Spawn a promise that returns some status-like type; if the status
// represents failure automatically cancel the rest of the call.
template <typename PromiseFactory>
void SpawnGuarded(absl::string_view name, PromiseFactory promise_factory) {
using FactoryType =
promise_detail::OncePromiseFactory<void, PromiseFactory>;
using PromiseType = typename FactoryType::Promise;
using ResultType = typename PromiseType::Result;
static_assert(
std::is_same<bool,
decltype(IsStatusOk(std::declval<ResultType>()))>::value,
"SpawnGuarded promise must return a status-like object");
party().Spawn(name, std::move(promise_factory), [this](ResultType r) {
if (!IsStatusOk(r)) {
if (grpc_trace_promise_primitives.enabled()) {
gpr_log(GPR_DEBUG, "SpawnGuarded sees failure: %s",
r.ToString().c_str());
}
std::ignore = Cancel(StatusCast<ServerMetadataHandle>(std::move(r)));
}
});
}
private:
absl::AnyInvocable<void()> on_done_{nullptr};
};
class CallSpine final : public CallSpineInterface, public Party {
public:
static RefCountedPtr<CallSpine> Create(
grpc_event_engine::experimental::EventEngine* event_engine,
Arena* arena) {
return RefCountedPtr<CallSpine>(arena->New<CallSpine>(event_engine, arena));
}
Pipe<ClientMetadataHandle>& client_initial_metadata() override {
return client_initial_metadata_;
}
Pipe<ServerMetadataHandle>& server_initial_metadata() override {
return server_initial_metadata_;
}
Pipe<MessageHandle>& client_to_server_messages() override {
return client_to_server_messages_;
}
Pipe<MessageHandle>& server_to_client_messages() override {
return server_to_client_messages_;
}
Pipe<ServerMetadataHandle>& server_trailing_metadata() override {
return server_trailing_metadata_;
}
Latch<ServerMetadataHandle>& cancel_latch() override { return cancel_latch_; }
Party& party() override { return *this; }
void IncrementRefCount() override { Party::IncrementRefCount(); }
void Unref() override { Party::Unref(); }
private:
friend class Arena;
CallSpine(grpc_event_engine::experimental::EventEngine* event_engine,
Arena* arena)
: Party(arena, 1), event_engine_(event_engine) {}
class ScopedContext : public ScopedActivity,
public promise_detail::Context<Arena> {
public:
explicit ScopedContext(CallSpine* spine)
: ScopedActivity(&spine->party()), Context<Arena>(spine->arena()) {}
};
bool RunParty() override {
ScopedContext context(this);
return Party::RunParty();
}
void PartyOver() override {
Arena* a = arena();
{
ScopedContext context(this);
CancelRemainingParticipants();
a->DestroyManagedNewObjects();
}
this->~CallSpine();
a->Destroy();
}
grpc_event_engine::experimental::EventEngine* event_engine() const override {
return event_engine_;
}
// Initial metadata from client to server
Pipe<ClientMetadataHandle> client_initial_metadata_{arena()};
// Initial metadata from server to client
Pipe<ServerMetadataHandle> server_initial_metadata_{arena()};
// Messages travelling from the application to the transport.
Pipe<MessageHandle> client_to_server_messages_{arena()};
// Messages travelling from the transport to the application.
Pipe<MessageHandle> server_to_client_messages_{arena()};
// Trailing metadata from server to client
Pipe<ServerMetadataHandle> server_trailing_metadata_{arena()};
// Latch that can be set to terminate the call
Latch<ServerMetadataHandle> cancel_latch_;
// Event engine associated with this call
grpc_event_engine::experimental::EventEngine* const event_engine_;
};
class CallInitiator {
public:
explicit CallInitiator(RefCountedPtr<CallSpineInterface> spine)
: spine_(std::move(spine)) {}
auto PushClientInitialMetadata(ClientMetadataHandle md) {
GPR_DEBUG_ASSERT(GetContext<Activity>() == &spine_->party());
return Map(spine_->client_initial_metadata().sender.Push(std::move(md)),
[](bool ok) { return StatusFlag(ok); });
}
auto PullServerInitialMetadata() {
GPR_DEBUG_ASSERT(GetContext<Activity>() == &spine_->party());
return Map(spine_->server_initial_metadata().receiver.Next(),
[](NextResult<ServerMetadataHandle> md)
-> ValueOrFailure<absl::optional<ServerMetadataHandle>> {
if (!md.has_value()) {
if (md.cancelled()) return Failure{};
return absl::optional<ServerMetadataHandle>();
}
return absl::optional<ServerMetadataHandle>(std::move(*md));
});
}
auto PullServerTrailingMetadata() {
GPR_DEBUG_ASSERT(GetContext<Activity>() == &spine_->party());
return PrioritizedRace(
Map(spine_->server_trailing_metadata().receiver.Next(),
[spine = spine_](
NextResult<ServerMetadataHandle> md) -> ServerMetadataHandle {
GPR_ASSERT(md.has_value());
return std::move(*md);
}),
spine_->WaitForCancel());
}
auto PullMessage() {
GPR_DEBUG_ASSERT(GetContext<Activity>() == &spine_->party());
return spine_->server_to_client_messages().receiver.Next();
}
auto PushMessage(MessageHandle message) {
GPR_DEBUG_ASSERT(GetContext<Activity>() == &spine_->party());
return Map(
spine_->client_to_server_messages().sender.Push(std::move(message)),
[](bool r) { return StatusFlag(r); });
}
void FinishSends() {
GPR_DEBUG_ASSERT(GetContext<Activity>() == &spine_->party());
spine_->client_to_server_messages().sender.Close();
}
template <typename Promise>
auto CancelIfFails(Promise promise) {
return spine_->CancelIfFails(std::move(promise));
}
void Cancel() {
GPR_DEBUG_ASSERT(GetContext<Activity>() == &spine_->party());
std::ignore =
spine_->Cancel(ServerMetadataFromStatus(absl::CancelledError()));
}
template <typename PromiseFactory>
void SpawnGuarded(absl::string_view name, PromiseFactory promise_factory) {
spine_->SpawnGuarded(name, std::move(promise_factory));
}
template <typename PromiseFactory>
void SpawnInfallible(absl::string_view name, PromiseFactory promise_factory) {
spine_->SpawnInfallible(name, std::move(promise_factory));
}
template <typename PromiseFactory>
auto SpawnWaitable(absl::string_view name, PromiseFactory promise_factory) {
return spine_->party().SpawnWaitable(name, std::move(promise_factory));
}
Arena* arena() { return spine_->party().arena(); }
private:
RefCountedPtr<CallSpineInterface> spine_;
};
class CallHandler {
public:
explicit CallHandler(RefCountedPtr<CallSpineInterface> spine)
: spine_(std::move(spine)) {}
auto PullClientInitialMetadata() {
GPR_DEBUG_ASSERT(GetContext<Activity>() == &spine_->party());
return Map(spine_->client_initial_metadata().receiver.Next(),
[](NextResult<ClientMetadataHandle> md)
-> ValueOrFailure<ClientMetadataHandle> {
if (!md.has_value()) return Failure{};
return std::move(*md);
});
}
auto PushServerInitialMetadata(absl::optional<ServerMetadataHandle> md) {
GPR_DEBUG_ASSERT(GetContext<Activity>() == &spine_->party());
return If(
md.has_value(),
[&md, this]() {
return Map(
spine_->server_initial_metadata().sender.Push(std::move(*md)),
[](bool ok) { return StatusFlag(ok); });
},
[this]() {
spine_->server_initial_metadata().sender.Close();
return []() -> StatusFlag { return Success{}; };
});
}
auto PushServerTrailingMetadata(ServerMetadataHandle md) {
GPR_DEBUG_ASSERT(GetContext<Activity>() == &spine_->party());
spine_->server_initial_metadata().sender.Close();
spine_->server_to_client_messages().sender.Close();
spine_->client_to_server_messages().receiver.CloseWithError();
spine_->CallOnDone();
return Map(spine_->server_trailing_metadata().sender.Push(std::move(md)),
[](bool ok) { return StatusFlag(ok); });
}
auto PullMessage() {
GPR_DEBUG_ASSERT(GetContext<Activity>() == &spine_->party());
return spine_->client_to_server_messages().receiver.Next();
}
auto PushMessage(MessageHandle message) {
GPR_DEBUG_ASSERT(GetContext<Activity>() == &spine_->party());
return Map(
spine_->server_to_client_messages().sender.Push(std::move(message)),
[](bool ok) { return StatusFlag(ok); });
}
void Cancel(ServerMetadataHandle status) {
GPR_DEBUG_ASSERT(GetContext<Activity>() == &spine_->party());
std::ignore = spine_->Cancel(std::move(status));
}
void OnDone(absl::AnyInvocable<void()> fn) { spine_->OnDone(std::move(fn)); }
template <typename Promise>
auto CancelIfFails(Promise promise) {
return spine_->CancelIfFails(std::move(promise));
}
template <typename PromiseFactory>
void SpawnGuarded(absl::string_view name, PromiseFactory promise_factory) {
spine_->SpawnGuarded(name, std::move(promise_factory));
}
template <typename PromiseFactory>
void SpawnInfallible(absl::string_view name, PromiseFactory promise_factory) {
spine_->SpawnInfallible(name, std::move(promise_factory));
}
template <typename PromiseFactory>
auto SpawnWaitable(absl::string_view name, PromiseFactory promise_factory) {
return spine_->party().SpawnWaitable(name, std::move(promise_factory));
}
Arena* arena() { return spine_->party().arena(); }
private:
RefCountedPtr<CallSpineInterface> spine_;
};
struct CallInitiatorAndHandler {
CallInitiator initiator;
CallHandler handler;
};
CallInitiatorAndHandler MakeCall(
grpc_event_engine::experimental::EventEngine* event_engine, Arena* arena);
template <typename CallHalf>
auto OutgoingMessages(CallHalf h) {
struct Wrapper {
CallHalf h;
auto Next() { return h.PullMessage(); }
};
return Wrapper{std::move(h)};
}
// Forward a call from `call_handler` to `call_initiator` (with initial metadata
// `client_initial_metadata`)
void ForwardCall(CallHandler call_handler, CallInitiator call_initiator,
ClientMetadataHandle client_initial_metadata);
} // namespace grpc_core
#endif // GRPC_SRC_CORE_LIB_TRANSPORT_CALL_SPINE_H

@ -215,89 +215,3 @@ grpc_transport_stream_op_batch* grpc_make_transport_stream_op(
op->op.on_complete = &op->outer_on_complete;
return &op->op;
}
namespace grpc_core {
void ForwardCall(CallHandler call_handler, CallInitiator call_initiator,
ClientMetadataHandle client_initial_metadata) {
// Send initial metadata.
call_initiator.SpawnGuarded(
"send_initial_metadata",
[client_initial_metadata = std::move(client_initial_metadata),
call_initiator]() mutable {
return call_initiator.PushClientInitialMetadata(
std::move(client_initial_metadata));
});
// Read messages from handler into initiator.
call_handler.SpawnGuarded("read_messages", [call_handler,
call_initiator]() mutable {
return Seq(ForEach(OutgoingMessages(call_handler),
[call_initiator](MessageHandle msg) mutable {
// Need to spawn a job into the initiator's activity to
// push the message in.
return call_initiator.SpawnWaitable(
"send_message",
[msg = std::move(msg), call_initiator]() mutable {
return call_initiator.CancelIfFails(
call_initiator.PushMessage(std::move(msg)));
});
}),
[call_initiator](StatusFlag result) mutable {
call_initiator.SpawnInfallible(
"finish-downstream", [call_initiator, result]() mutable {
if (result.ok()) {
call_initiator.FinishSends();
} else {
call_initiator.Cancel();
}
return Empty{};
});
return result;
});
});
call_initiator.SpawnInfallible("read_the_things", [call_initiator,
call_handler]() mutable {
return Seq(
call_initiator.CancelIfFails(TrySeq(
call_initiator.PullServerInitialMetadata(),
[call_handler,
call_initiator](absl::optional<ServerMetadataHandle> md) mutable {
const bool has_md = md.has_value();
call_handler.SpawnGuarded(
"recv_initial_metadata",
[md = std::move(md), call_handler]() mutable {
return call_handler.PushServerInitialMetadata(
std::move(md));
});
return If(
has_md,
ForEach(OutgoingMessages(call_initiator),
[call_handler](MessageHandle msg) mutable {
return call_handler.SpawnWaitable(
"recv_message",
[msg = std::move(msg), call_handler]() mutable {
return call_handler.CancelIfFails(
call_handler.PushMessage(std::move(msg)));
});
}),
[]() -> StatusFlag { return Success{}; });
})),
call_initiator.PullServerTrailingMetadata(),
[call_handler](ServerMetadataHandle md) mutable {
call_handler.SpawnGuarded(
"recv_trailing_metadata",
[md = std::move(md), call_handler]() mutable {
return call_handler.PushServerTrailingMetadata(std::move(md));
});
return Empty{};
});
});
}
CallInitiatorAndHandler MakeCall(
grpc_event_engine::experimental::EventEngine* event_engine, Arena* arena) {
auto spine = CallSpine::Create(event_engine, arena);
return {CallInitiator(spine), CallHandler(spine)};
}
} // namespace grpc_core

@ -42,7 +42,6 @@
#include "src/core/lib/channel/context.h"
#include "src/core/lib/debug/trace.h"
#include "src/core/lib/gprpp/debug_location.h"
#include "src/core/lib/gprpp/orphanable.h"
#include "src/core/lib/gprpp/ref_counted.h"
#include "src/core/lib/iomgr/call_combiner.h"
@ -53,16 +52,12 @@
#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/detail/status.h"
#include "src/core/lib/promise/if.h"
#include "src/core/lib/promise/latch.h"
#include "src/core/lib/promise/party.h"
#include "src/core/lib/promise/pipe.h"
#include "src/core/lib/promise/prioritized_race.h"
#include "src/core/lib/promise/status_flag.h"
#include "src/core/lib/resource_quota/arena.h"
#include "src/core/lib/slice/slice_buffer.h"
#include "src/core/lib/transport/call_final_info.h"
#include "src/core/lib/transport/call_spine.h"
#include "src/core/lib/transport/connectivity_state.h"
#include "src/core/lib/transport/message.h"
#include "src/core/lib/transport/metadata.h"
@ -160,386 +155,6 @@ struct CallArgs {
using NextPromiseFactory =
std::function<ArenaPromise<ServerMetadataHandle>(CallArgs)>;
// The common middle part of a call - a reference is held by each of
// CallInitiator and CallHandler - which provide interfaces that are appropriate
// for each side of a call.
// The spine will ultimately host the pipes, filters, and context for one part
// of a call: ie top-half client channel, sub channel call, server call.
// TODO(ctiller): eventually drop this when we don't need to reference into
// legacy promise calls anymore
class CallSpineInterface {
public:
virtual ~CallSpineInterface() = default;
virtual Pipe<ClientMetadataHandle>& client_initial_metadata() = 0;
virtual Pipe<ServerMetadataHandle>& server_initial_metadata() = 0;
virtual Pipe<MessageHandle>& client_to_server_messages() = 0;
virtual Pipe<MessageHandle>& server_to_client_messages() = 0;
virtual Pipe<ServerMetadataHandle>& server_trailing_metadata() = 0;
virtual Latch<ServerMetadataHandle>& cancel_latch() = 0;
// Add a callback to be called when server trailing metadata is received.
void OnDone(absl::AnyInvocable<void()> fn) {
if (on_done_ == nullptr) {
on_done_ = std::move(fn);
return;
}
on_done_ = [first = std::move(fn), next = std::move(on_done_)]() mutable {
first();
next();
};
}
void CallOnDone() {
if (on_done_ != nullptr) std::exchange(on_done_, nullptr)();
}
virtual Party& party() = 0;
virtual void IncrementRefCount() = 0;
virtual void Unref() = 0;
// Cancel the call with the given metadata.
// Regarding the `MUST_USE_RESULT absl::nullopt_t`:
// Most cancellation calls right now happen in pipe interceptors;
// there `nullopt` indicates terminate processing of this pipe and close with
// error.
// It's convenient then to have the Cancel operation (setting the latch to
// terminate the call) be the last thing that occurs in a pipe interceptor,
// and this construction supports that (and has helped the author not write
// some bugs).
GRPC_MUST_USE_RESULT absl::nullopt_t Cancel(ServerMetadataHandle metadata) {
GPR_DEBUG_ASSERT(GetContext<Activity>() == &party());
auto& c = cancel_latch();
if (c.is_set()) return absl::nullopt;
c.Set(std::move(metadata));
CallOnDone();
client_initial_metadata().sender.CloseWithError();
server_initial_metadata().sender.CloseWithError();
client_to_server_messages().sender.CloseWithError();
server_to_client_messages().sender.CloseWithError();
return absl::nullopt;
}
auto WaitForCancel() {
GPR_DEBUG_ASSERT(GetContext<Activity>() == &party());
return cancel_latch().Wait();
}
// Wrap a promise so that if it returns failure it automatically cancels
// the rest of the call.
// The resulting (returned) promise will resolve to Empty.
template <typename Promise>
auto CancelIfFails(Promise promise) {
GPR_DEBUG_ASSERT(GetContext<Activity>() == &party());
using P = promise_detail::PromiseLike<Promise>;
using ResultType = typename P::Result;
return Map(std::move(promise), [this](ResultType r) {
if (!IsStatusOk(r)) {
std::ignore = Cancel(StatusCast<ServerMetadataHandle>(r));
}
return r;
});
}
// Spawn a promise that returns Empty{} and save some boilerplate handling
// that detail.
template <typename PromiseFactory>
void SpawnInfallible(absl::string_view name, PromiseFactory promise_factory) {
party().Spawn(name, std::move(promise_factory), [](Empty) {});
}
// Spawn a promise that returns some status-like type; if the status
// represents failure automatically cancel the rest of the call.
template <typename PromiseFactory>
void SpawnGuarded(absl::string_view name, PromiseFactory promise_factory) {
using FactoryType =
promise_detail::OncePromiseFactory<void, PromiseFactory>;
using PromiseType = typename FactoryType::Promise;
using ResultType = typename PromiseType::Result;
static_assert(
std::is_same<bool,
decltype(IsStatusOk(std::declval<ResultType>()))>::value,
"SpawnGuarded promise must return a status-like object");
party().Spawn(name, std::move(promise_factory), [this](ResultType r) {
if (!IsStatusOk(r)) {
if (grpc_trace_promise_primitives.enabled()) {
gpr_log(GPR_DEBUG, "SpawnGuarded sees failure: %s",
r.ToString().c_str());
}
std::ignore = Cancel(StatusCast<ServerMetadataHandle>(std::move(r)));
}
});
}
private:
absl::AnyInvocable<void()> on_done_{nullptr};
};
class CallSpine final : public CallSpineInterface, public Party {
public:
static RefCountedPtr<CallSpine> Create(
grpc_event_engine::experimental::EventEngine* event_engine,
Arena* arena) {
return RefCountedPtr<CallSpine>(arena->New<CallSpine>(event_engine, arena));
}
Pipe<ClientMetadataHandle>& client_initial_metadata() override {
return client_initial_metadata_;
}
Pipe<ServerMetadataHandle>& server_initial_metadata() override {
return server_initial_metadata_;
}
Pipe<MessageHandle>& client_to_server_messages() override {
return client_to_server_messages_;
}
Pipe<MessageHandle>& server_to_client_messages() override {
return server_to_client_messages_;
}
Pipe<ServerMetadataHandle>& server_trailing_metadata() override {
return server_trailing_metadata_;
}
Latch<ServerMetadataHandle>& cancel_latch() override { return cancel_latch_; }
Party& party() override { return *this; }
void IncrementRefCount() override { Party::IncrementRefCount(); }
void Unref() override { Party::Unref(); }
private:
friend class Arena;
CallSpine(grpc_event_engine::experimental::EventEngine* event_engine,
Arena* arena)
: Party(arena, 1), event_engine_(event_engine) {}
class ScopedContext : public ScopedActivity,
public promise_detail::Context<Arena> {
public:
explicit ScopedContext(CallSpine* spine)
: ScopedActivity(&spine->party()), Context<Arena>(spine->arena()) {}
};
bool RunParty() override {
ScopedContext context(this);
return Party::RunParty();
}
void PartyOver() override {
Arena* a = arena();
{
ScopedContext context(this);
CancelRemainingParticipants();
a->DestroyManagedNewObjects();
}
this->~CallSpine();
a->Destroy();
}
grpc_event_engine::experimental::EventEngine* event_engine() const override {
return event_engine_;
}
// Initial metadata from client to server
Pipe<ClientMetadataHandle> client_initial_metadata_{arena()};
// Initial metadata from server to client
Pipe<ServerMetadataHandle> server_initial_metadata_{arena()};
// Messages travelling from the application to the transport.
Pipe<MessageHandle> client_to_server_messages_{arena()};
// Messages travelling from the transport to the application.
Pipe<MessageHandle> server_to_client_messages_{arena()};
// Trailing metadata from server to client
Pipe<ServerMetadataHandle> server_trailing_metadata_{arena()};
// Latch that can be set to terminate the call
Latch<ServerMetadataHandle> cancel_latch_;
// Event engine associated with this call
grpc_event_engine::experimental::EventEngine* const event_engine_;
};
class CallInitiator {
public:
explicit CallInitiator(RefCountedPtr<CallSpineInterface> spine)
: spine_(std::move(spine)) {}
auto PushClientInitialMetadata(ClientMetadataHandle md) {
GPR_DEBUG_ASSERT(GetContext<Activity>() == &spine_->party());
return Map(spine_->client_initial_metadata().sender.Push(std::move(md)),
[](bool ok) { return StatusFlag(ok); });
}
auto PullServerInitialMetadata() {
GPR_DEBUG_ASSERT(GetContext<Activity>() == &spine_->party());
return Map(spine_->server_initial_metadata().receiver.Next(),
[](NextResult<ServerMetadataHandle> md)
-> ValueOrFailure<absl::optional<ServerMetadataHandle>> {
if (!md.has_value()) {
if (md.cancelled()) return Failure{};
return absl::optional<ServerMetadataHandle>();
}
return absl::optional<ServerMetadataHandle>(std::move(*md));
});
}
auto PullServerTrailingMetadata() {
GPR_DEBUG_ASSERT(GetContext<Activity>() == &spine_->party());
return PrioritizedRace(
Map(spine_->server_trailing_metadata().receiver.Next(),
[spine = spine_](
NextResult<ServerMetadataHandle> md) -> ServerMetadataHandle {
GPR_ASSERT(md.has_value());
return std::move(*md);
}),
spine_->WaitForCancel());
}
auto PullMessage() {
GPR_DEBUG_ASSERT(GetContext<Activity>() == &spine_->party());
return spine_->server_to_client_messages().receiver.Next();
}
auto PushMessage(MessageHandle message) {
GPR_DEBUG_ASSERT(GetContext<Activity>() == &spine_->party());
return Map(
spine_->client_to_server_messages().sender.Push(std::move(message)),
[](bool r) { return StatusFlag(r); });
}
void FinishSends() {
GPR_DEBUG_ASSERT(GetContext<Activity>() == &spine_->party());
spine_->client_to_server_messages().sender.Close();
}
template <typename Promise>
auto CancelIfFails(Promise promise) {
return spine_->CancelIfFails(std::move(promise));
}
void Cancel() {
GPR_DEBUG_ASSERT(GetContext<Activity>() == &spine_->party());
std::ignore =
spine_->Cancel(ServerMetadataFromStatus(absl::CancelledError()));
}
template <typename PromiseFactory>
void SpawnGuarded(absl::string_view name, PromiseFactory promise_factory) {
spine_->SpawnGuarded(name, std::move(promise_factory));
}
template <typename PromiseFactory>
void SpawnInfallible(absl::string_view name, PromiseFactory promise_factory) {
spine_->SpawnInfallible(name, std::move(promise_factory));
}
template <typename PromiseFactory>
auto SpawnWaitable(absl::string_view name, PromiseFactory promise_factory) {
return spine_->party().SpawnWaitable(name, std::move(promise_factory));
}
Arena* arena() { return spine_->party().arena(); }
private:
RefCountedPtr<CallSpineInterface> spine_;
};
class CallHandler {
public:
explicit CallHandler(RefCountedPtr<CallSpineInterface> spine)
: spine_(std::move(spine)) {}
auto PullClientInitialMetadata() {
GPR_DEBUG_ASSERT(GetContext<Activity>() == &spine_->party());
return Map(spine_->client_initial_metadata().receiver.Next(),
[](NextResult<ClientMetadataHandle> md)
-> ValueOrFailure<ClientMetadataHandle> {
if (!md.has_value()) return Failure{};
return std::move(*md);
});
}
auto PushServerInitialMetadata(absl::optional<ServerMetadataHandle> md) {
GPR_DEBUG_ASSERT(GetContext<Activity>() == &spine_->party());
return If(
md.has_value(),
[&md, this]() {
return Map(
spine_->server_initial_metadata().sender.Push(std::move(*md)),
[](bool ok) { return StatusFlag(ok); });
},
[this]() {
spine_->server_initial_metadata().sender.Close();
return []() -> StatusFlag { return Success{}; };
});
}
auto PushServerTrailingMetadata(ServerMetadataHandle md) {
GPR_DEBUG_ASSERT(GetContext<Activity>() == &spine_->party());
spine_->server_initial_metadata().sender.Close();
spine_->server_to_client_messages().sender.Close();
spine_->client_to_server_messages().receiver.CloseWithError();
spine_->CallOnDone();
return Map(spine_->server_trailing_metadata().sender.Push(std::move(md)),
[](bool ok) { return StatusFlag(ok); });
}
auto PullMessage() {
GPR_DEBUG_ASSERT(GetContext<Activity>() == &spine_->party());
return spine_->client_to_server_messages().receiver.Next();
}
auto PushMessage(MessageHandle message) {
GPR_DEBUG_ASSERT(GetContext<Activity>() == &spine_->party());
return Map(
spine_->server_to_client_messages().sender.Push(std::move(message)),
[](bool ok) { return StatusFlag(ok); });
}
void Cancel(ServerMetadataHandle status) {
GPR_DEBUG_ASSERT(GetContext<Activity>() == &spine_->party());
std::ignore = spine_->Cancel(std::move(status));
}
void OnDone(absl::AnyInvocable<void()> fn) { spine_->OnDone(std::move(fn)); }
template <typename Promise>
auto CancelIfFails(Promise promise) {
return spine_->CancelIfFails(std::move(promise));
}
template <typename PromiseFactory>
void SpawnGuarded(absl::string_view name, PromiseFactory promise_factory) {
spine_->SpawnGuarded(name, std::move(promise_factory));
}
template <typename PromiseFactory>
void SpawnInfallible(absl::string_view name, PromiseFactory promise_factory) {
spine_->SpawnInfallible(name, std::move(promise_factory));
}
template <typename PromiseFactory>
auto SpawnWaitable(absl::string_view name, PromiseFactory promise_factory) {
return spine_->party().SpawnWaitable(name, std::move(promise_factory));
}
Arena* arena() { return spine_->party().arena(); }
private:
RefCountedPtr<CallSpineInterface> spine_;
};
struct CallInitiatorAndHandler {
CallInitiator initiator;
CallHandler handler;
};
CallInitiatorAndHandler MakeCall(
grpc_event_engine::experimental::EventEngine* event_engine, Arena* arena);
template <typename CallHalf>
auto OutgoingMessages(CallHalf h) {
struct Wrapper {
CallHalf h;
auto Next() { return h.PullMessage(); }
};
return Wrapper{std::move(h)};
}
// Forward a call from `call_handler` to `call_initiator` (with initial metadata
// `client_initial_metadata`)
void ForwardCall(CallHandler call_handler, CallInitiator call_initiator,
ClientMetadataHandle client_initial_metadata);
} // namespace grpc_core
// forward declarations

@ -812,6 +812,7 @@ CORE_SOURCE_FILES = [
'src/core/lib/transport/bdp_estimator.cc',
'src/core/lib/transport/call_filters.cc',
'src/core/lib/transport/call_final_info.cc',
'src/core/lib/transport/call_spine.cc',
'src/core/lib/transport/connectivity_state.cc',
'src/core/lib/transport/error_utils.cc',
'src/core/lib/transport/handshaker.cc',

@ -2904,6 +2904,8 @@ src/core/lib/transport/call_filters.cc \
src/core/lib/transport/call_filters.h \
src/core/lib/transport/call_final_info.cc \
src/core/lib/transport/call_final_info.h \
src/core/lib/transport/call_spine.cc \
src/core/lib/transport/call_spine.h \
src/core/lib/transport/connectivity_state.cc \
src/core/lib/transport/connectivity_state.h \
src/core/lib/transport/custom_metadata.h \

@ -2685,6 +2685,8 @@ src/core/lib/transport/call_filters.cc \
src/core/lib/transport/call_filters.h \
src/core/lib/transport/call_final_info.cc \
src/core/lib/transport/call_final_info.h \
src/core/lib/transport/call_spine.cc \
src/core/lib/transport/call_spine.h \
src/core/lib/transport/connectivity_state.cc \
src/core/lib/transport/connectivity_state.h \
src/core/lib/transport/custom_metadata.h \

Loading…
Cancel
Save