Make deadline the first metadata trait (#27468)

pull/27475/head
Craig Tiller 3 years ago committed by GitHub
parent dbade1f5e7
commit 91a2db9bc2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      BUILD
  2. 46
      CMakeLists.txt
  3. 13
      build_autogenerated.yaml
  4. 3
      gRPC-C++.podspec
  5. 3
      gRPC-Core.podspec
  6. 1
      grpc.gemspec
  7. 2
      grpc.gyp
  8. 1
      package.xml
  9. 8
      src/core/ext/filters/deadline/deadline_filter.cc
  10. 17
      src/core/ext/transport/chttp2/transport/chttp2_transport.cc
  11. 3
      src/core/ext/transport/chttp2/transport/hpack_encoder.cc
  12. 2
      src/core/ext/transport/chttp2/transport/hpack_encoder.h
  13. 6
      src/core/ext/transport/chttp2/transport/incoming_metadata.cc
  14. 13
      src/core/ext/transport/inproc/inproc_transport.cc
  15. 14
      src/core/lib/gprpp/bitset.h
  16. 8
      src/core/lib/gprpp/table.h
  17. 12
      src/core/lib/surface/call.cc
  18. 7
      src/core/lib/surface/server.cc
  19. 4
      src/core/lib/transport/metadata_batch.cc
  20. 140
      src/core/lib/transport/metadata_batch.h
  21. 50
      src/core/lib/transport/transport_op_string.cc
  22. 7
      test/core/gprpp/bitset_test.cc
  23. 2
      test/core/gprpp/table_test.cc
  24. 14
      test/core/transport/BUILD
  25. 86
      test/core/transport/metadata_map_test.cc
  26. 2
      test/cpp/microbenchmarks/bm_chttp2_hpack.cc
  27. 1
      tools/doxygen/Doxyfile.c++.internal
  28. 1
      tools/doxygen/Doxyfile.core.internal
  29. 24
      tools/run_tests/generated/tests.json

@ -1722,6 +1722,7 @@ grpc_cc_library(
"ref_counted_ptr",
"slice",
"slice_refcount",
"table",
"useful",
],
)

46
CMakeLists.txt generated

@ -917,6 +917,7 @@ if(gRPC_BUILD_TESTS)
add_dependencies(buildtests_cxx match_test)
add_dependencies(buildtests_cxx matchers_test)
add_dependencies(buildtests_cxx message_allocator_end2end_test)
add_dependencies(buildtests_cxx metadata_map_test)
add_dependencies(buildtests_cxx miscompile_with_no_unique_address_test)
add_dependencies(buildtests_cxx mock_stream_test)
add_dependencies(buildtests_cxx mock_test)
@ -2157,6 +2158,7 @@ target_link_libraries(grpc
absl::bind_front
absl::statusor
absl::variant
absl::utility
gpr
${_gRPC_SSL_LIBRARIES}
address_sorting
@ -2715,6 +2717,7 @@ target_link_libraries(grpc_unsecure
absl::bind_front
absl::statusor
absl::variant
absl::utility
gpr
address_sorting
)
@ -13325,6 +13328,41 @@ target_link_libraries(message_allocator_end2end_test
)
endif()
if(gRPC_BUILD_TESTS)
add_executable(metadata_map_test
test/core/transport/metadata_map_test.cc
third_party/googletest/googletest/src/gtest-all.cc
third_party/googletest/googlemock/src/gmock-all.cc
)
target_include_directories(metadata_map_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(metadata_map_test
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
grpc_test_util
)
endif()
if(gRPC_BUILD_TESTS)
@ -17254,7 +17292,7 @@ generate_pkgconfig(
"gRPC"
"high performance general RPC framework"
"${gRPC_CORE_VERSION}"
"gpr openssl absl_base absl_bind_front absl_cord absl_core_headers absl_flat_hash_map absl_inlined_vector absl_memory absl_optional absl_status absl_statusor absl_str_format absl_strings absl_synchronization absl_time absl_variant"
"gpr openssl absl_base absl_bind_front absl_cord absl_core_headers absl_flat_hash_map absl_inlined_vector absl_memory absl_optional absl_status absl_statusor absl_str_format absl_strings absl_synchronization absl_time absl_utility absl_variant"
"-lgrpc -laddress_sorting -lre2 -lupb -lcares -lz"
""
"grpc.pc")
@ -17264,7 +17302,7 @@ generate_pkgconfig(
"gRPC unsecure"
"high performance general RPC framework without SSL"
"${gRPC_CORE_VERSION}"
"gpr absl_base absl_bind_front absl_cord absl_core_headers absl_flat_hash_map absl_inlined_vector absl_memory absl_optional absl_status absl_statusor absl_str_format absl_strings absl_synchronization absl_time absl_variant"
"gpr absl_base absl_bind_front absl_cord absl_core_headers absl_flat_hash_map absl_inlined_vector absl_memory absl_optional absl_status absl_statusor absl_str_format absl_strings absl_synchronization absl_time absl_utility absl_variant"
"-lgrpc_unsecure"
""
"grpc_unsecure.pc")
@ -17274,7 +17312,7 @@ generate_pkgconfig(
"gRPC++"
"C++ wrapper for gRPC"
"${gRPC_CPP_VERSION}"
"grpc absl_base absl_bind_front absl_cord absl_core_headers absl_flat_hash_map absl_inlined_vector absl_memory absl_optional absl_status absl_statusor absl_str_format absl_strings absl_synchronization absl_time absl_variant"
"grpc absl_base absl_bind_front absl_cord absl_core_headers absl_flat_hash_map absl_inlined_vector absl_memory absl_optional absl_status absl_statusor absl_str_format absl_strings absl_synchronization absl_time absl_utility absl_variant"
"-lgrpc++"
""
"grpc++.pc")
@ -17284,7 +17322,7 @@ generate_pkgconfig(
"gRPC++ unsecure"
"C++ wrapper for gRPC without SSL"
"${gRPC_CPP_VERSION}"
"grpc_unsecure absl_base absl_bind_front absl_cord absl_core_headers absl_flat_hash_map absl_inlined_vector absl_memory absl_optional absl_status absl_statusor absl_str_format absl_strings absl_synchronization absl_time absl_variant"
"grpc_unsecure absl_base absl_bind_front absl_cord absl_core_headers absl_flat_hash_map absl_inlined_vector absl_memory absl_optional absl_status absl_statusor absl_str_format absl_strings absl_synchronization absl_time absl_utility absl_variant"
"-lgrpc++_unsecure"
""
"grpc++_unsecure.pc")

@ -761,6 +761,7 @@ libs:
- src/core/lib/gprpp/overload.h
- src/core/lib/gprpp/ref_counted.h
- src/core/lib/gprpp/ref_counted_ptr.h
- src/core/lib/gprpp/table.h
- src/core/lib/http/format_request.h
- src/core/lib/http/httpcli.h
- src/core/lib/http/parser.h
@ -1535,6 +1536,7 @@ libs:
- absl/functional:bind_front
- absl/status:statusor
- absl/types:variant
- absl/utility:utility
- gpr
- libssl
- address_sorting
@ -1815,6 +1817,7 @@ libs:
- src/core/lib/gprpp/overload.h
- src/core/lib/gprpp/ref_counted.h
- src/core/lib/gprpp/ref_counted_ptr.h
- src/core/lib/gprpp/table.h
- src/core/lib/http/format_request.h
- src/core/lib/http/httpcli.h
- src/core/lib/http/parser.h
@ -2232,6 +2235,7 @@ libs:
- absl/functional:bind_front
- absl/status:statusor
- absl/types:variant
- absl/utility:utility
- gpr
- address_sorting
baselib: true
@ -6622,6 +6626,15 @@ targets:
- test/cpp/end2end/test_service_impl.cc
deps:
- grpc++_test_util
- name: metadata_map_test
gtest: true
build: test
language: c++
headers: []
src:
- test/core/transport/metadata_map_test.cc
deps:
- grpc_test_util
- name: miscompile_with_no_unique_address_test
gtest: true
build: test

3
gRPC-C++.podspec generated

@ -203,6 +203,7 @@ Pod::Spec.new do |s|
ss.dependency 'abseil/time/time', abseil_version
ss.dependency 'abseil/types/optional', abseil_version
ss.dependency 'abseil/types/variant', abseil_version
ss.dependency 'abseil/utility/utility', abseil_version
ss.source_files = 'src/core/ext/filters/client_channel/backend_metric.h',
'src/core/ext/filters/client_channel/backup_poller.h',
@ -570,6 +571,7 @@ Pod::Spec.new do |s|
'src/core/lib/gprpp/stat.h',
'src/core/lib/gprpp/status_helper.h',
'src/core/lib/gprpp/sync.h',
'src/core/lib/gprpp/table.h',
'src/core/lib/gprpp/thd.h',
'src/core/lib/gprpp/time_util.h',
'src/core/lib/http/format_request.h',
@ -1242,6 +1244,7 @@ Pod::Spec.new do |s|
'src/core/lib/gprpp/stat.h',
'src/core/lib/gprpp/status_helper.h',
'src/core/lib/gprpp/sync.h',
'src/core/lib/gprpp/table.h',
'src/core/lib/gprpp/thd.h',
'src/core/lib/gprpp/time_util.h',
'src/core/lib/http/format_request.h',

3
gRPC-Core.podspec generated

@ -193,6 +193,7 @@ Pod::Spec.new do |s|
ss.dependency 'abseil/time/time', abseil_version
ss.dependency 'abseil/types/optional', abseil_version
ss.dependency 'abseil/types/variant', abseil_version
ss.dependency 'abseil/utility/utility', abseil_version
ss.compiler_flags = '-DBORINGSSL_PREFIX=GRPC -Wno-unreachable-code -Wno-shorten-64-to-32'
ss.source_files = 'src/core/ext/filters/census/grpc_context.cc',
@ -950,6 +951,7 @@ Pod::Spec.new do |s|
'src/core/lib/gprpp/status_helper.cc',
'src/core/lib/gprpp/status_helper.h',
'src/core/lib/gprpp/sync.h',
'src/core/lib/gprpp/table.h',
'src/core/lib/gprpp/thd.h',
'src/core/lib/gprpp/thd_posix.cc',
'src/core/lib/gprpp/thd_windows.cc',
@ -1830,6 +1832,7 @@ Pod::Spec.new do |s|
'src/core/lib/gprpp/stat.h',
'src/core/lib/gprpp/status_helper.h',
'src/core/lib/gprpp/sync.h',
'src/core/lib/gprpp/table.h',
'src/core/lib/gprpp/thd.h',
'src/core/lib/gprpp/time_util.h',
'src/core/lib/http/format_request.h',

1
grpc.gemspec generated

@ -863,6 +863,7 @@ Gem::Specification.new do |s|
s.files += %w( src/core/lib/gprpp/status_helper.cc )
s.files += %w( src/core/lib/gprpp/status_helper.h )
s.files += %w( src/core/lib/gprpp/sync.h )
s.files += %w( src/core/lib/gprpp/table.h )
s.files += %w( src/core/lib/gprpp/thd.h )
s.files += %w( src/core/lib/gprpp/thd_posix.cc )
s.files += %w( src/core/lib/gprpp/thd_windows.cc )

2
grpc.gyp generated

@ -482,6 +482,7 @@
'absl/functional:bind_front',
'absl/status:statusor',
'absl/types:variant',
'absl/utility:utility',
'gpr',
'address_sorting',
],
@ -1145,6 +1146,7 @@
'absl/functional:bind_front',
'absl/status:statusor',
'absl/types:variant',
'absl/utility:utility',
'gpr',
'address_sorting',
],

1
package.xml generated

@ -843,6 +843,7 @@
<file baseinstalldir="/" name="src/core/lib/gprpp/status_helper.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/gprpp/status_helper.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/gprpp/sync.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/gprpp/table.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/gprpp/thd.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/gprpp/thd_posix.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/gprpp/thd_windows.cc" role="src" />

@ -114,9 +114,7 @@ class TimerState {
// synchronized.
static void start_timer_if_needed(grpc_call_element* elem,
grpc_millis deadline) {
if (deadline == GRPC_MILLIS_INF_FUTURE) {
return;
}
if (deadline == GRPC_MILLIS_INF_FUTURE) return;
grpc_deadline_state* deadline_state =
static_cast<grpc_deadline_state*>(elem->call_data);
GPR_ASSERT(deadline_state->timer_state == nullptr);
@ -295,7 +293,9 @@ static void deadline_client_start_transport_stream_op_batch(
static void recv_initial_metadata_ready(void* arg, grpc_error_handle error) {
grpc_call_element* elem = static_cast<grpc_call_element*>(arg);
server_call_data* calld = static_cast<server_call_data*>(elem->call_data);
start_timer_if_needed(elem, calld->recv_initial_metadata->deadline());
start_timer_if_needed(
elem, calld->recv_initial_metadata->get(grpc_core::GrpcTimeoutMetadata())
.value_or(GRPC_MILLIS_INF_FUTURE));
// Invoke the next callback.
grpc_core::Closure::Run(DEBUG_LOCATION,
calld->next_recv_initial_metadata_ready,

@ -1431,7 +1431,10 @@ static void perform_stream_op_locked(void* stream_op,
s->send_initial_metadata =
op_payload->send_initial_metadata.send_initial_metadata;
if (t->is_client) {
s->deadline = std::min(s->deadline, s->send_initial_metadata->deadline());
s->deadline = std::min(
s->deadline,
s->send_initial_metadata->get(grpc_core::GrpcTimeoutMetadata())
.value_or(GRPC_MILLIS_INF_FUTURE));
}
if (contains_non_ok_status(s->send_initial_metadata)) {
s->seen_error = true;
@ -1623,14 +1626,14 @@ static void perform_stream_op(grpc_transport* gt, grpc_stream* gs,
if (!t->is_client) {
if (op->send_initial_metadata) {
grpc_millis deadline =
op->payload->send_initial_metadata.send_initial_metadata->deadline();
GPR_ASSERT(deadline == GRPC_MILLIS_INF_FUTURE);
GPR_ASSERT(!op->payload->send_initial_metadata.send_initial_metadata
->get(grpc_core::GrpcTimeoutMetadata())
.has_value());
}
if (op->send_trailing_metadata) {
grpc_millis deadline = op->payload->send_trailing_metadata
.send_trailing_metadata->deadline();
GPR_ASSERT(deadline == GRPC_MILLIS_INF_FUTURE);
GPR_ASSERT(!op->payload->send_trailing_metadata.send_trailing_metadata
->get(grpc_core::GrpcTimeoutMetadata())
.has_value());
}
}

@ -448,7 +448,8 @@ void HPackCompressor::Framer::EncodeDynamic(grpc_mdelem elem) {
}
}
void HPackCompressor::Framer::EncodeDeadline(grpc_millis deadline) {
void HPackCompressor::Framer::Encode(GrpcTimeoutMetadata,
grpc_millis deadline) {
char timeout_str[GRPC_HTTP2_TIMEOUT_ENCODE_MIN_BUFSIZE];
grpc_mdelem mdelem;
grpc_http2_encode_timeout(deadline - grpc_core::ExecCtx::Get()->Now(),

@ -117,7 +117,7 @@ class HPackCompressor {
Framer& operator=(const Framer&) = delete;
void Encode(grpc_mdelem md);
void EncodeDeadline(grpc_millis deadline);
void Encode(GrpcTimeoutMetadata, grpc_millis deadline);
private:
struct FramePrefix {

@ -53,7 +53,11 @@ grpc_error_handle grpc_chttp2_incoming_metadata_buffer_replace_or_add(
void grpc_chttp2_incoming_metadata_buffer_set_deadline(
grpc_chttp2_incoming_metadata_buffer* buffer, grpc_millis deadline) {
buffer->batch.SetDeadline(deadline);
if (deadline != GRPC_MILLIS_INF_FUTURE) {
buffer->batch.Set(grpc_core::GrpcTimeoutMetadata(), deadline);
} else {
buffer->batch.Remove(grpc_core::GrpcTimeoutMetadata());
}
}
void grpc_chttp2_incoming_metadata_buffer_publish(

@ -694,8 +694,11 @@ void op_state_machine_locked(inproc_stream* s, grpc_error_handle error) {
.recv_initial_metadata,
s->recv_initial_md_op->payload->recv_initial_metadata.recv_flags,
nullptr);
s->recv_initial_md_op->payload->recv_initial_metadata
.recv_initial_metadata->SetDeadline(s->deadline);
if (s->deadline != GRPC_MILLIS_INF_FUTURE) {
s->recv_initial_md_op->payload->recv_initial_metadata
.recv_initial_metadata->Set(grpc_core::GrpcTimeoutMetadata(),
s->deadline);
}
if (s->recv_initial_md_op->payload->recv_initial_metadata
.trailing_metadata_available != nullptr) {
*s->recv_initial_md_op->payload->recv_initial_metadata
@ -1016,8 +1019,10 @@ void perform_stream_op(grpc_transport* gt, grpc_stream* gs,
if (s->t->is_client) {
grpc_millis* dl =
(other == nullptr) ? &s->write_buffer_deadline : &other->deadline;
*dl = std::min(*dl, op->payload->send_initial_metadata
.send_initial_metadata->deadline());
*dl = std::min(
*dl, op->payload->send_initial_metadata.send_initial_metadata
->get(grpc_core::GrpcTimeoutMetadata())
.value_or(GRPC_MILLIS_INF_FUTURE));
s->initial_md_sent = true;
}
}

@ -169,6 +169,20 @@ class BitSet {
Uint<kUnitBits> units_[kUnits];
};
// Zero-size specialization of BitSet.
// Useful for generic programming.
// Make a compile time error out of get/set type accesses, and hard-codes
// queries that do make sense.
template <>
class BitSet<0> {
public:
constexpr BitSet() {}
bool all() const { return true; }
bool none() const { return true; }
uint32_t count() const { return 0; }
};
} // namespace grpc_core
#endif // GRPC_CORE_LIB_GPRPP_BITSET_H

@ -375,7 +375,7 @@ class Table {
// destructor.
template <size_t... I>
void Destruct(absl::index_sequence<I...>) {
table_detail::do_these_things(
table_detail::do_these_things<int>(
{(table_detail::DestructIfNotNull(get<I>()), 1)...});
}
@ -383,21 +383,21 @@ class Table {
// or_clear as per CopyIf().
template <bool or_clear, size_t... I>
void Copy(absl::index_sequence<I...>, const Table& rhs) {
table_detail::do_these_things({(CopyIf<or_clear, I>(rhs), 1)...});
table_detail::do_these_things<int>({(CopyIf<or_clear, I>(rhs), 1)...});
}
// For each field (element I=0, 1, ...) move that field into this table -
// or_clear as per MoveIf().
template <bool or_clear, size_t... I>
void Move(absl::index_sequence<I...>, Table&& rhs) {
table_detail::do_these_things(
table_detail::do_these_things<int>(
{(MoveIf<or_clear, I>(std::forward<Table>(rhs)), 1)...});
}
// For each field (element I=0, 1, ...) if that field is present, call f.
template <typename F, size_t... I>
void ForEachImpl(F f, absl::index_sequence<I...>) const {
table_detail::do_these_things({(CallIf<I>(&f), 1)...});
table_detail::do_these_things<int>({(CallIf<I>(&f), 1)...});
}
// Bit field indicating which elements are set.

@ -1470,9 +1470,10 @@ static void receiving_initial_metadata_ready(void* bctlp,
GPR_TIMER_SCOPE("validate_filtered_metadata", 0);
validate_filtered_metadata(bctl);
grpc_millis deadline = md->deadline();
if (deadline != GRPC_MILLIS_INF_FUTURE && !call->is_client) {
call->send_deadline = deadline;
absl::optional<grpc_millis> deadline =
md->get(grpc_core::GrpcTimeoutMetadata());
if (deadline.has_value() && !call->is_client) {
call->send_deadline = *deadline;
}
} else {
if (bctl->batch_error.ok()) {
@ -1653,8 +1654,9 @@ static grpc_call_error call_start_batch(grpc_call* call, const grpc_op* ops,
goto done_with_error;
}
/* TODO(ctiller): just make these the same variable? */
if (call->is_client) {
call->metadata_batch[0][0].SetDeadline(call->send_deadline);
if (call->is_client && call->send_deadline != GRPC_MILLIS_INF_FUTURE) {
call->metadata_batch[0][0].Set(grpc_core::GrpcTimeoutMetadata(),
call->send_deadline);
}
stream_op_payload->send_initial_metadata.send_initial_metadata =
&call->metadata_batch[0 /* is_receiving */][0 /* is_trailing */];

@ -1379,7 +1379,6 @@ void Server::CallData::RecvInitialMetadataReady(void* arg,
grpc_error_handle error) {
grpc_call_element* elem = static_cast<grpc_call_element*>(arg);
CallData* calld = static_cast<CallData*>(elem->call_data);
grpc_millis op_deadline;
if (error == GRPC_ERROR_NONE) {
GPR_DEBUG_ASSERT(
calld->recv_initial_metadata_->legacy_index()->named.path != nullptr);
@ -1395,9 +1394,9 @@ void Server::CallData::RecvInitialMetadataReady(void* arg,
} else {
GRPC_ERROR_REF(error);
}
op_deadline = calld->recv_initial_metadata_->deadline();
if (op_deadline != GRPC_MILLIS_INF_FUTURE) {
calld->deadline_ = op_deadline;
auto op_deadline = calld->recv_initial_metadata_->get(GrpcTimeoutMetadata());
if (op_deadline.has_value()) {
calld->deadline_ = *op_deadline;
}
if (calld->host_.has_value() && calld->path_.has_value()) {
/* do nothing */

@ -45,7 +45,9 @@ void grpc_metadata_batch_copy(grpc_metadata_batch* src,
grpc_metadata_batch* dst,
grpc_linked_mdelem* storage) {
dst->Clear();
dst->SetDeadline(src->deadline());
if (auto* p = src->get_pointer(grpc_core::GrpcTimeoutMetadata())) {
dst->Set(grpc_core::GrpcTimeoutMetadata(), *p);
}
size_t i = 0;
src->ForEach([&](grpc_mdelem md) {
// If the mdelem is not external, take a ref.

@ -30,6 +30,7 @@
#include <grpc/slice.h>
#include <grpc/support/time.h>
#include "src/core/lib/gprpp/table.h"
#include "src/core/lib/iomgr/exec_ctx.h"
#include "src/core/lib/transport/metadata.h"
#include "src/core/lib/transport/static_metadata.h"
@ -66,6 +67,18 @@ grpc_error_handle grpc_attach_md_to_error(grpc_error_handle src,
namespace grpc_core {
// grpc-timeout metadata trait.
// ValueType is defined as grpc_millis - an absolute timestamp (i.e. a
// deadline!), that is converted to a duration by transports before being
// sent.
// TODO(ctiller): Move this elsewhere. During the transition we need to be able
// to name this in MetadataMap, but ultimately once the transition is done we
// should not need to.
struct GrpcTimeoutMetadata {
using ValueType = grpc_millis;
static const char* key() { return "grpc-timeout"; }
};
// MetadataMap encodes the mapping of metadata keys to metadata values.
// Right now the API presented is the minimal one that will allow us to
// substitute this type for grpc_metadata_batch in a relatively easy fashion. At
@ -73,9 +86,20 @@ namespace grpc_core {
// again, whilst minimally holding the performance bar already set (and
// hopefully improving some things).
// In the meantime, we're not going to invest much time in ephemeral API
// documentation, so if you must use one of these API's and it's not obvious
// documentation, so if you must use one of these APIs and it's not obvious
// how, reach out to ctiller.
template <typename... TraitsIgnored>
//
// MetadataMap takes a list of traits. Each of these trait objects defines
// one metadata field that is used by core, and so should have more specialized
// handling than just using the generic APIs.
//
// Each trait object has the following signature:
// // Traits for the grpc-xyz metadata field:
// struct GrpcXyzMetadata {
// using ValueType = ...;
// static constexpr char* key() { return "grpc-xyz"; }
// };
template <typename... Traits>
class MetadataMap {
public:
MetadataMap();
@ -86,14 +110,72 @@ class MetadataMap {
MetadataMap(MetadataMap&&) noexcept;
MetadataMap& operator=(MetadataMap&&) noexcept;
// Encode this metadata map into some encoder.
// For each field that is set in the MetadataMap, call
// encoder->Encode.
//
// For fields for which we have traits, this will be a method with
// the signature:
// void Encode(TraitsType, typename TraitsType::ValueType value);
// For fields for which we do not have traits, this will be a method
// with the signature:
// void Encode(grpc_mdelem md);
// TODO(ctiller): It's expected that the latter Encode method will
// become Encode(Slice, Slice) by the end of the current metadata API
// transitions.
template <typename Encoder>
void Encode(Encoder* encoder) const {
for (auto* l = list_.head; l; l = l->next) {
encoder->Encode(l->md);
}
if (deadline_ != GRPC_MILLIS_INF_FUTURE) encoder->EncodeDeadline(deadline_);
table_.ForEach(EncodeWrapper<Encoder>{encoder});
}
// Get the pointer to the value of some known metadata.
// Returns nullptr if the metadata is not present.
// Causes a compilation error if Which is not an element of Traits.
template <typename Which>
const typename Which::ValueType* get_pointer(Which) const {
if (auto* p = table_.template get<Value<Which>>()) return &p->value;
return nullptr;
}
// Get the pointer to the value of some known metadata.
// Returns nullptr if the metadata is not present.
// Causes a compilation error if Which is not an element of Traits.
template <typename Which>
typename Which::ValueType* get_pointer(Which) {
if (auto* p = table_.template get<Value<Which>>()) return &p->value;
return nullptr;
}
// Get the value of some known metadata.
// Returns nullopt if the metadata is not present.
// Causes a compilation error if Which is not an element of Traits.
template <typename Which>
absl::optional<typename Which::ValueType> get(Which) const {
if (auto* p = table_.template get<Value<Which>>()) return p->value;
return absl::nullopt;
}
// Set the value of some known metadata.
// Returns a pointer to the new value.
template <typename Which, typename... Args>
typename Which::ValueType* Set(Which, Args&&... args) {
return &table_.template set<Value<Which>>(std::forward<Args>(args)...)
->value;
}
// Remove a specific piece of known metadata.
template <typename Which>
void Remove(Which) {
table_.template clear<Value<Which>>();
}
//
// All APIs below this point are subject to change.
//
template <typename F>
void ForEach(F f) const {
for (auto* l = list_.head; l; l = l->next) {
@ -132,9 +214,7 @@ class MetadataMap {
void Clear();
bool empty() const { return count() == 0; }
size_t count() const {
return list_.count + (deadline_ == GRPC_MILLIS_INF_FUTURE ? 0 : 1);
}
size_t count() const { return list_.count + table_.count(); }
size_t non_deadline_count() const { return list_.count; }
size_t default_count() const { return list_.default_count; }
@ -175,13 +255,37 @@ class MetadataMap {
void AssertOk() {}
#endif
grpc_millis deadline() const { return deadline_; }
void SetDeadline(grpc_millis deadline) { deadline_ = deadline; }
void ClearDeadline() { SetDeadline(GRPC_MILLIS_INF_FUTURE); }
// TODO(ctiller): the following explicit deadline handling methods are
// deprecated in terms of the traits based APIs.
grpc_millis deadline() const {
return get(GrpcTimeoutMetadata()).value_or(GRPC_MILLIS_INF_FUTURE);
};
const grpc_metadata_batch_callouts* legacy_index() const { return &idx_; }
private:
// Generate a strong type for metadata values per trait.
template <typename Which>
struct Value {
Value() = default;
explicit Value(const typename Which::ValueType& value) : value(value) {}
Value(const Value&) = default;
Value& operator=(const Value&) = default;
Value(Value&&) noexcept = default;
Value& operator=(Value&&) noexcept = default;
GPR_NO_UNIQUE_ADDRESS typename Which::ValueType value;
};
// Callable for the ForEach in Encode() -- for each value, call the
// appropriate encoder method.
template <typename Encoder>
struct EncodeWrapper {
Encoder* encoder;
template <typename Which>
void operator()(const Value<Which>& which) {
encoder->Encode(Which(), which.value);
}
};
void AssertValidCallouts();
grpc_error_handle LinkCallout(grpc_linked_mdelem* storage,
grpc_metadata_batch_callouts_index idx)
@ -272,13 +376,11 @@ class MetadataMap {
assert_valid_list(list);
}
// Table of known metadata types.
Table<Value<Traits>...> table_;
/** Metadata elements in this batch */
grpc_mdelem_list list_;
grpc_metadata_batch_callouts idx_;
/** Used to calculate grpc-timeout at the point of sending,
or GRPC_MILLIS_INF_FUTURE if this batch does not need to send a
grpc-timeout */
grpc_millis deadline_;
};
template <typename... Traits>
@ -307,29 +409,26 @@ template <typename... Traits>
MetadataMap<Traits...>::MetadataMap() {
memset(&list_, 0, sizeof(list_));
memset(&idx_, 0, sizeof(idx_));
deadline_ = GRPC_MILLIS_INF_FUTURE;
}
template <typename... Traits>
MetadataMap<Traits...>::MetadataMap(MetadataMap&& other) noexcept {
MetadataMap<Traits...>::MetadataMap(MetadataMap&& other) noexcept
: table_(std::move(other.table_)) {
list_ = other.list_;
idx_ = other.idx_;
deadline_ = other.deadline_;
memset(&other.list_, 0, sizeof(list_));
memset(&other.idx_, 0, sizeof(idx_));
other.deadline_ = GRPC_MILLIS_INF_FUTURE;
}
template <typename... Traits>
MetadataMap<Traits...>& MetadataMap<Traits...>::operator=(
MetadataMap&& other) noexcept {
Clear();
table_ = std::move(other.table_);
list_ = other.list_;
idx_ = other.idx_;
deadline_ = other.deadline_;
memset(&other.list_, 0, sizeof(list_));
memset(&other.idx_, 0, sizeof(idx_));
other.deadline_ = GRPC_MILLIS_INF_FUTURE;
return *this;
}
@ -567,7 +666,8 @@ bool MetadataMap<Traits...>::ReplaceIfExists(grpc_slice key, grpc_slice value) {
} // namespace grpc_core
using grpc_metadata_batch = grpc_core::MetadataMap<>;
using grpc_metadata_batch =
grpc_core::MetadataMap<grpc_core::GrpcTimeoutMetadata>;
inline void grpc_metadata_batch_clear(grpc_metadata_batch* batch) {
batch->Clear();

@ -40,28 +40,38 @@
/* These routines are here to facilitate debugging - they produce string
representations of various transport data structures */
static void put_metadata(grpc_mdelem md, std::vector<std::string>* out) {
out->push_back("key=");
char* dump = grpc_dump_slice(GRPC_MDKEY(md), GPR_DUMP_HEX | GPR_DUMP_ASCII);
out->push_back(dump);
gpr_free(dump);
out->push_back(" value=");
dump = grpc_dump_slice(GRPC_MDVALUE(md), GPR_DUMP_HEX | GPR_DUMP_ASCII);
out->push_back(dump);
gpr_free(dump);
}
static void put_metadata_list(const grpc_metadata_batch& md,
std::vector<std::string>* out) {
bool first = true;
md.ForEach([&](grpc_mdelem elem) {
if (!first) out->push_back(", ");
first = false;
put_metadata(elem, out);
});
if (md.deadline() != GRPC_MILLIS_INF_FUTURE) {
out->push_back(absl::StrFormat(" deadline=%" PRId64, md.deadline()));
}
class Encoder {
public:
explicit Encoder(std::vector<std::string>* out) : out_(out) {}
void Encode(const grpc_mdelem& md) {
MaybeAddComma();
out_->push_back("key=");
char* dump =
grpc_dump_slice(GRPC_MDKEY(md), GPR_DUMP_HEX | GPR_DUMP_ASCII);
out_->push_back(dump);
gpr_free(dump);
out_->push_back(" value=");
dump = grpc_dump_slice(GRPC_MDVALUE(md), GPR_DUMP_HEX | GPR_DUMP_ASCII);
out_->push_back(dump);
gpr_free(dump);
}
void Encode(grpc_core::GrpcTimeoutMetadata, grpc_millis deadline) {
MaybeAddComma();
out_->push_back(absl::StrFormat("deadline=%" PRId64, deadline));
}
private:
void MaybeAddComma() {
if (out_->size() != initial_size_) out_->push_back(", ");
}
std::vector<std::string>* const out_;
const size_t initial_size_ = out_->size();
};
Encoder encoder(out);
md.Encode(&encoder);
}
std::string grpc_transport_stream_op_batch_string(

@ -90,6 +90,13 @@ TYPED_TEST(BitSetTest, Count) {
}
}
TEST(EmptyBitSet, Empty) {
BitSet<0> b;
EXPECT_TRUE(b.all());
EXPECT_TRUE(b.none());
EXPECT_EQ(b.count(), 0);
}
} // namespace testing
} // namespace grpc_core

@ -24,6 +24,8 @@
namespace grpc_core {
namespace testing {
TEST(Table, InstantiateEmpty) { Table<>(); }
TEST(Table, NoOp) {
Table<int, double, std::string> t;
EXPECT_EQ(t.get<int>(), nullptr);

@ -77,6 +77,20 @@ grpc_cc_test(
],
)
grpc_cc_test(
name = "metadata_map_test",
srcs = ["metadata_map_test.cc"],
external_deps = [
"gtest",
],
language = "C++",
deps = [
"//:gpr",
"//:grpc",
"//test/core/util:grpc_test_util",
],
)
grpc_cc_test(
name = "metadata_test",
srcs = ["metadata_test.cc"],

@ -0,0 +1,86 @@
//
// 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 <gtest/gtest.h>
#include "src/core/lib/slice/slice_internal.h"
#include "src/core/lib/transport/metadata_batch.h"
#include "test/core/util/test_config.h"
namespace grpc_core {
namespace testing {
TEST(MetadataMapTest, Noop) { MetadataMap<>(); }
TEST(MetadataMapTest, NoopWithDeadline) { MetadataMap<GrpcTimeoutMetadata>(); }
TEST(MetadataMapTest, SimpleOps) {
MetadataMap<GrpcTimeoutMetadata> map;
EXPECT_EQ(map.get_pointer(GrpcTimeoutMetadata()), nullptr);
EXPECT_EQ(map.get(GrpcTimeoutMetadata()), absl::nullopt);
map.Set(GrpcTimeoutMetadata(), 1234);
EXPECT_NE(map.get_pointer(GrpcTimeoutMetadata()), nullptr);
EXPECT_EQ(*map.get_pointer(GrpcTimeoutMetadata()), 1234);
EXPECT_EQ(map.get(GrpcTimeoutMetadata()), 1234);
map.Remove(GrpcTimeoutMetadata());
EXPECT_EQ(map.get_pointer(GrpcTimeoutMetadata()), nullptr);
EXPECT_EQ(map.get(GrpcTimeoutMetadata()), absl::nullopt);
}
// Target for MetadataMap::Encode.
// Writes down some string representation of what it receives, so we can
// EXPECT_EQ it later.
class FakeEncoder {
public:
std::string output() { return output_; }
void Encode(grpc_mdelem md) {
output_ +=
absl::StrCat("LEGACY CALL: key=", StringViewFromSlice(GRPC_MDKEY(md)),
" value=", StringViewFromSlice(GRPC_MDVALUE(md)), "\n");
}
void Encode(GrpcTimeoutMetadata, grpc_millis deadline) {
output_ += absl::StrCat("grpc-timeout: deadline=", deadline, "\n");
}
private:
std::string output_;
};
TEST(MetadataMapTest, EmptyEncodeTest) {
FakeEncoder encoder;
MetadataMap<GrpcTimeoutMetadata> map;
map.Encode(&encoder);
EXPECT_EQ(encoder.output(), "");
}
TEST(MetadataMapTest, TimeoutEncodeTest) {
FakeEncoder encoder;
MetadataMap<GrpcTimeoutMetadata> map;
map.Set(GrpcTimeoutMetadata(), 1234);
map.Encode(&encoder);
EXPECT_EQ(encoder.output(), "grpc-timeout: deadline=1234\n");
}
} // 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();
};

@ -70,7 +70,7 @@ static void BM_HpackEncoderEncodeDeadline(benchmark::State& state) {
grpc_millis saved_now = grpc_core::ExecCtx::Get()->Now();
grpc_metadata_batch b;
b.SetDeadline(saved_now + 30 * 1000);
b.Set(grpc_core::GrpcTimeoutMetadata(), saved_now + 30 * 1000);
grpc_core::HPackCompressor c;
grpc_transport_one_way_stats stats;

@ -1796,6 +1796,7 @@ src/core/lib/gprpp/stat_windows.cc \
src/core/lib/gprpp/status_helper.cc \
src/core/lib/gprpp/status_helper.h \
src/core/lib/gprpp/sync.h \
src/core/lib/gprpp/table.h \
src/core/lib/gprpp/thd.h \
src/core/lib/gprpp/thd_posix.cc \
src/core/lib/gprpp/thd_windows.cc \

@ -1635,6 +1635,7 @@ src/core/lib/gprpp/stat_windows.cc \
src/core/lib/gprpp/status_helper.cc \
src/core/lib/gprpp/status_helper.h \
src/core/lib/gprpp/sync.h \
src/core/lib/gprpp/table.h \
src/core/lib/gprpp/thd.h \
src/core/lib/gprpp/thd_posix.cc \
src/core/lib/gprpp/thd_windows.cc \

@ -5681,6 +5681,30 @@
],
"uses_polling": true
},
{
"args": [],
"benchmark": false,
"ci_platforms": [
"linux",
"mac",
"posix",
"windows"
],
"cpu_cost": 1.0,
"exclude_configs": [],
"exclude_iomgrs": [],
"flaky": false,
"gtest": true,
"language": "c++",
"name": "metadata_map_test",
"platforms": [
"linux",
"mac",
"posix",
"windows"
],
"uses_polling": true
},
{
"args": [],
"benchmark": false,

Loading…
Cancel
Save