Introduce ChannelArgs C++ type (#28860)

* progress

* basics

* Automated change: Fix sanity tests

* fix

* fix

* backout protochange

* consistency in naming

* Make things load bearing, fix bugs

* Automated change: Fix sanity tests

Co-authored-by: ctiller <ctiller@users.noreply.github.com>
pull/29105/head
Craig Tiller 3 years ago committed by GitHub
parent 28db143b98
commit f82245555e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      BUILD
  2. 65
      CMakeLists.txt
  3. 26
      build_autogenerated.yaml
  4. 4
      gRPC-C++.podspec
  5. 4
      gRPC-Core.podspec
  6. 2
      grpc.gemspec
  7. 2
      package.xml
  8. 73
      src/core/lib/avl/avl.h
  9. 153
      src/core/lib/channel/channel_args.cc
  10. 172
      src/core/lib/channel/channel_args.h
  11. 12
      src/core/lib/channel/channel_args_preconditioning.cc
  12. 3
      src/core/lib/channel/channel_args_preconditioning.h
  13. 35
      src/core/lib/resource_quota/api.cc
  14. 6
      src/core/lib/resource_quota/resource_quota.h
  15. 76
      test/core/avl/avl_fuzzer.cc
  16. 11
      test/core/avl/avl_fuzzer.proto
  17. 9
      test/core/avl/avl_fuzzer_corpus/crash-060a9a897130ba7bb2f4313daa604c47f7c7c907
  18. 5
      test/core/avl/avl_fuzzer_corpus/crash-1fbe8edb82f9a7aa4c2dffe4a6eaa40c34b1e360
  19. 1
      test/core/channel/BUILD
  20. 154
      test/core/channel/channel_args_test.cc
  21. 2
      tools/doxygen/Doxyfile.c++.internal
  22. 2
      tools/doxygen/Doxyfile.core.internal
  23. 48
      tools/run_tests/generated/tests.json

@ -1769,6 +1769,9 @@ grpc_cc_library(
hdrs = [
"src/core/lib/avl/avl.h",
],
external_deps = [
"absl/container:inlined_vector",
],
deps = [
"gpr_platform",
],
@ -2390,12 +2393,18 @@ grpc_cc_library(
external_deps = [
"absl/strings",
"absl/strings:str_format",
"absl/types:variant",
"absl/types:optional",
],
language = "c++",
deps = [
"avl",
"channel_stack_type",
"gpr_base",
"grpc_codegen",
"match",
"ref_counted",
"ref_counted_ptr",
"useful",
],
)

65
CMakeLists.txt generated

@ -631,7 +631,6 @@ if(gRPC_BUILD_TESTS)
add_dependencies(buildtests_c bin_decoder_test)
add_dependencies(buildtests_c bin_encoder_test)
add_dependencies(buildtests_c buffer_list_test)
add_dependencies(buildtests_c channel_args_test)
add_dependencies(buildtests_c channel_stack_test)
add_dependencies(buildtests_c check_gcp_environment_linux_test)
add_dependencies(buildtests_c check_gcp_environment_windows_test)
@ -806,6 +805,7 @@ if(gRPC_BUILD_TESTS)
add_dependencies(buildtests_cxx certificate_provider_registry_test)
add_dependencies(buildtests_cxx certificate_provider_store_test)
add_dependencies(buildtests_cxx cfstream_test)
add_dependencies(buildtests_cxx channel_args_test)
add_dependencies(buildtests_cxx channel_arguments_test)
add_dependencies(buildtests_cxx channel_creds_registry_test)
add_dependencies(buildtests_cxx channel_filter_test)
@ -4676,33 +4676,6 @@ target_link_libraries(buffer_list_test
)
endif()
if(gRPC_BUILD_TESTS)
add_executable(channel_args_test
test/core/channel/channel_args_test.cc
)
target_include_directories(channel_args_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}
)
target_link_libraries(channel_args_test
${_gRPC_ALLTARGETS_LIBRARIES}
grpc_test_util
)
endif()
if(gRPC_BUILD_TESTS)
@ -7722,6 +7695,7 @@ target_include_directories(avl_test
target_link_libraries(avl_test
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
absl::inlined_vector
)
@ -8485,6 +8459,41 @@ target_link_libraries(cfstream_test
)
endif()
if(gRPC_BUILD_TESTS)
add_executable(channel_args_test
test/core/channel/channel_args_test.cc
third_party/googletest/googletest/src/gtest-all.cc
third_party/googletest/googlemock/src/gmock-all.cc
)
target_include_directories(channel_args_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(channel_args_test
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
grpc_test_util
)
endif()
if(gRPC_BUILD_TESTS)

@ -726,7 +726,9 @@ libs:
- src/core/lib/gprpp/chunked_vector.h
- src/core/lib/gprpp/cpp_impl_of.h
- src/core/lib/gprpp/dual_ref_counted.h
- src/core/lib/gprpp/match.h
- src/core/lib/gprpp/orphanable.h
- 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
@ -1901,7 +1903,9 @@ libs:
- src/core/lib/gprpp/chunked_vector.h
- src/core/lib/gprpp/cpp_impl_of.h
- src/core/lib/gprpp/dual_ref_counted.h
- src/core/lib/gprpp/match.h
- src/core/lib/gprpp/orphanable.h
- 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
@ -3322,15 +3326,6 @@ targets:
- test/core/iomgr/buffer_list_test.cc
deps:
- grpc_test_util
- name: channel_args_test
build: test
language: c
headers: []
src:
- test/core/channel/channel_args_test.cc
deps:
- grpc_test_util
uses_polling: false
- name: channel_stack_test
build: test
language: c
@ -4538,7 +4533,8 @@ targets:
- src/core/lib/avl/avl.h
src:
- test/core/avl/avl_test.cc
deps: []
deps:
- absl/container:inlined_vector
uses_polling: false
- name: aws_request_signer_test
gtest: true
@ -4854,6 +4850,16 @@ targets:
- test/cpp/end2end/test_service_impl.cc
deps:
- grpc++_test_util
- name: channel_args_test
gtest: true
build: test
language: c++
headers: []
src:
- test/core/channel/channel_args_test.cc
deps:
- grpc_test_util
uses_polling: false
- name: channel_arguments_test
gtest: true
build: test

4
gRPC-C++.podspec generated

@ -686,9 +686,11 @@ Pod::Spec.new do |s|
'src/core/lib/gprpp/global_config_generic.h',
'src/core/lib/gprpp/host_port.h',
'src/core/lib/gprpp/manual_constructor.h',
'src/core/lib/gprpp/match.h',
'src/core/lib/gprpp/memory.h',
'src/core/lib/gprpp/mpscq.h',
'src/core/lib/gprpp/orphanable.h',
'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/stat.h',
@ -1489,9 +1491,11 @@ Pod::Spec.new do |s|
'src/core/lib/gprpp/global_config_generic.h',
'src/core/lib/gprpp/host_port.h',
'src/core/lib/gprpp/manual_constructor.h',
'src/core/lib/gprpp/match.h',
'src/core/lib/gprpp/memory.h',
'src/core/lib/gprpp/mpscq.h',
'src/core/lib/gprpp/orphanable.h',
'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/stat.h',

4
gRPC-Core.podspec generated

@ -1083,10 +1083,12 @@ Pod::Spec.new do |s|
'src/core/lib/gprpp/host_port.cc',
'src/core/lib/gprpp/host_port.h',
'src/core/lib/gprpp/manual_constructor.h',
'src/core/lib/gprpp/match.h',
'src/core/lib/gprpp/memory.h',
'src/core/lib/gprpp/mpscq.cc',
'src/core/lib/gprpp/mpscq.h',
'src/core/lib/gprpp/orphanable.h',
'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/stat.h',
@ -2087,9 +2089,11 @@ Pod::Spec.new do |s|
'src/core/lib/gprpp/global_config_generic.h',
'src/core/lib/gprpp/host_port.h',
'src/core/lib/gprpp/manual_constructor.h',
'src/core/lib/gprpp/match.h',
'src/core/lib/gprpp/memory.h',
'src/core/lib/gprpp/mpscq.h',
'src/core/lib/gprpp/orphanable.h',
'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/stat.h',

2
grpc.gemspec generated

@ -1002,10 +1002,12 @@ Gem::Specification.new do |s|
s.files += %w( src/core/lib/gprpp/host_port.cc )
s.files += %w( src/core/lib/gprpp/host_port.h )
s.files += %w( src/core/lib/gprpp/manual_constructor.h )
s.files += %w( src/core/lib/gprpp/match.h )
s.files += %w( src/core/lib/gprpp/memory.h )
s.files += %w( src/core/lib/gprpp/mpscq.cc )
s.files += %w( src/core/lib/gprpp/mpscq.h )
s.files += %w( src/core/lib/gprpp/orphanable.h )
s.files += %w( src/core/lib/gprpp/overload.h )
s.files += %w( src/core/lib/gprpp/ref_counted.h )
s.files += %w( src/core/lib/gprpp/ref_counted_ptr.h )
s.files += %w( src/core/lib/gprpp/stat.h )

2
package.xml generated

@ -982,10 +982,12 @@
<file baseinstalldir="/" name="src/core/lib/gprpp/host_port.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/gprpp/host_port.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/gprpp/manual_constructor.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/gprpp/match.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/gprpp/memory.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/gprpp/mpscq.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/gprpp/mpscq.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/gprpp/orphanable.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/gprpp/overload.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/gprpp/ref_counted.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/gprpp/ref_counted_ptr.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/gprpp/stat.h" role="src" />

@ -22,6 +22,8 @@
#include <algorithm>
#include <memory>
#include "absl/container/inlined_vector.h"
namespace grpc_core {
template <class K, class V = void>
@ -32,8 +34,12 @@ class AVL {
AVL Add(K key, V value) const {
return AVL(AddKey(root_, std::move(key), std::move(value)));
}
AVL Remove(const K& key) const { return AVL(RemoveKey(root_, key)); }
const V* Lookup(const K& key) const {
template <typename SomethingLikeK>
AVL Remove(const SomethingLikeK& key) const {
return AVL(RemoveKey(root_, key));
}
template <typename SomethingLikeK>
const V* Lookup(const SomethingLikeK& key) const {
NodePtr n = Get(root_, key);
return n ? &n->kv.second : nullptr;
}
@ -50,7 +56,36 @@ class AVL {
ForEachImpl(root_.get(), std::forward<F>(f));
}
bool SameIdentity(AVL avl) const { return root_ == avl.root_; }
bool SameIdentity(const AVL& avl) const { return root_ == avl.root_; }
bool operator==(const AVL& other) const {
Iterator a(root_);
Iterator b(other.root_);
for (;;) {
Node* p = a.current();
Node* q = b.current();
if (p == nullptr) return q == nullptr;
if (q == nullptr) return false;
if (p->kv != q->kv) return false;
a.MoveNext();
b.MoveNext();
}
}
bool operator<(const AVL& other) const {
Iterator a(root_);
Iterator b(other.root_);
for (;;) {
Node* p = a.current();
Node* q = b.current();
if (p == nullptr) return q != nullptr;
if (q == nullptr) return false;
if (p->kv < q->kv) return true;
if (p->kv != q->kv) return false;
a.MoveNext();
b.MoveNext();
}
}
private:
struct Node;
@ -68,6 +103,32 @@ class AVL {
};
NodePtr root_;
class Iterator {
public:
explicit Iterator(const NodePtr& root) {
auto* n = root.get();
while (n != nullptr) {
stack_.push_back(n);
n = n->left.get();
}
}
Node* current() const { return stack_.empty() ? nullptr : stack_.back(); }
void MoveNext() {
auto* n = stack_.back();
stack_.pop_back();
if (n->right != nullptr) {
n = n->right.get();
while (n != nullptr) {
stack_.push_back(n);
n = n->left.get();
}
}
}
private:
absl::InlinedVector<Node*, 8> stack_;
};
explicit AVL(NodePtr root) : root_(std::move(root)) {}
template <class F>
@ -86,7 +147,8 @@ class AVL {
1 + std::max(Height(left), Height(right)));
}
static NodePtr Get(const NodePtr& node, const K& key) {
template <typename SomethingLikeK>
static NodePtr Get(const NodePtr& node, const SomethingLikeK& key) {
if (node == nullptr) {
return nullptr;
}
@ -198,7 +260,8 @@ class AVL {
return node;
}
static NodePtr RemoveKey(const NodePtr& node, const K& key) {
template <typename SomethingLikeK>
static NodePtr RemoveKey(const NodePtr& node, const SomethingLikeK& key) {
if (node == nullptr) {
return nullptr;
}

@ -37,6 +37,104 @@
#include "src/core/lib/gpr/string.h"
#include "src/core/lib/gpr/useful.h"
#include "src/core/lib/gprpp/match.h"
static int cmp_ptr(void* a_ptr, const grpc_arg_pointer_vtable* a_vtable,
void* b_ptr, const grpc_arg_pointer_vtable* b_vtable) {
int c = grpc_core::QsortCompare(a_ptr, b_ptr);
if (c == 0) return 0;
c = grpc_core::QsortCompare(a_vtable, b_vtable);
if (c != 0) return c;
return a_vtable->cmp(a_ptr, b_ptr);
}
namespace grpc_core {
bool ChannelArgs::Pointer::operator==(const Pointer& rhs) const {
return cmp_ptr(p_, vtable_, rhs.p_, rhs.vtable_) == 0;
}
bool ChannelArgs::Pointer::operator<(const Pointer& rhs) const {
return cmp_ptr(p_, vtable_, rhs.p_, rhs.vtable_) < 0;
}
ChannelArgs::ChannelArgs() = default;
ChannelArgs ChannelArgs::Set(grpc_arg arg) const {
switch (arg.type) {
case GRPC_ARG_INTEGER:
return Set(arg.key, arg.value.integer);
case GRPC_ARG_STRING:
if (arg.value.string != nullptr) return Set(arg.key, arg.value.string);
return Set(arg.key, "");
case GRPC_ARG_POINTER:
return Set(arg.key,
Pointer(arg.value.pointer.vtable->copy(arg.value.pointer.p),
arg.value.pointer.vtable));
}
GPR_UNREACHABLE_CODE(return ChannelArgs());
}
ChannelArgs ChannelArgs::FromC(const grpc_channel_args* args) {
ChannelArgs result;
if (args != nullptr) {
for (size_t i = 0; i < args->num_args; i++) {
result = result.Set(args->args[i]);
}
}
return result;
}
const grpc_channel_args* ChannelArgs::ToC() const {
std::vector<grpc_arg> c_args;
args_.ForEach([&c_args](const std::string& key, const Value& value) {
char* name = const_cast<char*>(key.c_str());
c_args.push_back(Match(
value,
[name](int i) { return grpc_channel_arg_integer_create(name, i); },
[name](const std::string& s) {
return grpc_channel_arg_string_create(name,
const_cast<char*>(s.c_str()));
},
[name](const Pointer& p) {
return grpc_channel_arg_pointer_create(name, p.c_pointer(),
p.c_vtable());
}));
});
return grpc_channel_args_copy_and_add(nullptr, c_args.data(), c_args.size());
}
ChannelArgs ChannelArgs::Set(absl::string_view key, Value value) const {
return ChannelArgs(args_.Add(std::string(key), std::move(value)));
}
ChannelArgs ChannelArgs::Remove(absl::string_view key) const {
return ChannelArgs(args_.Remove(key));
}
absl::optional<int> ChannelArgs::GetInt(absl::string_view name) const {
auto* v = Get(name);
if (v == nullptr) return absl::nullopt;
if (!absl::holds_alternative<int>(*v)) return absl::nullopt;
return absl::get<int>(*v);
}
absl::optional<absl::string_view> ChannelArgs::GetString(
absl::string_view name) const {
auto* v = Get(name);
if (v == nullptr) return absl::nullopt;
if (!absl::holds_alternative<std::string>(*v)) return absl::nullopt;
return absl::get<std::string>(*v);
}
void* ChannelArgs::GetVoidPointer(absl::string_view name) const {
auto* v = Get(name);
if (v == nullptr) return nullptr;
if (!absl::holds_alternative<Pointer>(*v)) return nullptr;
return absl::get<Pointer>(*v).c_pointer();
}
} // namespace grpc_core
static grpc_arg copy_arg(const grpc_arg* src) {
grpc_arg dst;
@ -156,16 +254,8 @@ static int cmp_arg(const grpc_arg* a, const grpc_arg* b) {
case GRPC_ARG_INTEGER:
return grpc_core::QsortCompare(a->value.integer, b->value.integer);
case GRPC_ARG_POINTER:
c = grpc_core::QsortCompare(a->value.pointer.p, b->value.pointer.p);
if (c != 0) {
c = grpc_core::QsortCompare(a->value.pointer.vtable,
b->value.pointer.vtable);
if (c == 0) {
c = a->value.pointer.vtable->cmp(a->value.pointer.p,
b->value.pointer.p);
}
}
return c;
return cmp_ptr(a->value.pointer.p, a->value.pointer.vtable,
b->value.pointer.p, b->value.pointer.vtable);
}
GPR_UNREACHABLE_CODE(return 0);
}
@ -384,9 +474,9 @@ const grpc_channel_args* RemoveGrpcInternalArgs(const grpc_channel_args* src) {
return dst;
}
const grpc_channel_args* UniquifyChannelArgKeys(const grpc_channel_args* src) {
if (src == nullptr) return nullptr;
std::map<absl::string_view, const grpc_arg*> values;
ChannelArgs UniquifyChannelArgKeys(const grpc_channel_args* src) {
if (src == nullptr) return ChannelArgs();
ChannelArgs output;
std::map<absl::string_view, std::vector<std::string>> concatenated_values;
for (size_t i = 0; i < src->num_args; i++) {
absl::string_view key = src->args[i].key;
@ -401,46 +491,23 @@ const grpc_channel_args* UniquifyChannelArgKeys(const grpc_channel_args* src) {
concatenated_values[key].push_back(src->args[i].value.string);
}
continue;
} else if (absl::StartsWith(key, "grpc.internal.")) {
continue;
}
auto it = values.find(key);
if (it == values.end()) {
values[key] = &src->args[i];
if (!output.Contains(key)) {
output = output.Set(src->args[i]);
} else {
// Traditional grpc_channel_args_find behavior was to pick the first
// value.
// For compatibility with existing users, we will do the same here.
}
}
if (values.size() + concatenated_values.size() == src->num_args) {
return grpc_channel_args_copy(src);
}
// Concatenate the concatenated values.
std::map<absl::string_view, std::string> concatenated_values_str;
for (const auto& concatenated_value : concatenated_values) {
concatenated_values_str[concatenated_value.first] =
absl::StrJoin(concatenated_value.second, " ");
}
// Create the result
std::vector<grpc_arg> argv;
argv.reserve(values.size());
for (const auto& a : values) {
argv.push_back(*a.second);
}
for (const auto& a : concatenated_values_str) {
argv.push_back(
grpc_channel_arg_string_create(const_cast<char*>(a.first.data()),
const_cast<char*>(a.second.c_str())));
output = output.Set(concatenated_value.first,
absl::StrJoin(concatenated_value.second, " "));
}
grpc_channel_args args = {argv.size(), argv.data()};
// Log that we're mutating things
gpr_log(GPR_INFO,
"Uniquification pass on channel args is mutating them: {%s} is being "
"changed to {%s}",
grpc_channel_args_string(src).c_str(),
grpc_channel_args_string(&args).c_str());
// Return the result (note we need to copy because we're borrowing the args
// from src still!)
return grpc_channel_args_copy(&args);
return output;
}
} // namespace grpc_core

@ -23,10 +23,170 @@
#include <string>
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "absl/types/variant.h"
#include <grpc/impl/codegen/grpc_types.h>
#include "src/core/lib/avl/avl.h"
#include "src/core/lib/gpr/useful.h"
#include "src/core/lib/gprpp/ref_counted.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/surface/channel_stack_type.h"
namespace grpc_core {
// Define a traits object for vtable lookup - allows us to integrate with
// existing code easily (just define the trait!) and allows some magic in
// ChannelArgs to automatically derive a vtable from a T*.
// To participate as a pointer, instances should expose the function:
// // Gets the vtable for this type
// static const grpc_channel_arg_vtable* vtable();
// // Performs any mutations required for channel args to own a pointer
// static void* take_unowned_pointer(T* p);
template <typename T, typename Ignored = void /* for SFINAE */>
struct ChannelArgTypeTraits;
template <typename T>
struct ChannelArgTypeTraits<
T, absl::enable_if_t<std::is_base_of<RefCounted<T>, T>::value, void>> {
static const grpc_arg_pointer_vtable* vtable() {
static const grpc_arg_pointer_vtable tbl = {
// copy
[](void* p) -> void* { return static_cast<T*>(p)->Ref().release(); },
// destroy
[](void* p) { static_cast<T*>(p)->Unref(); },
// compare
[](void* p1, void* p2) {
return QsortCompare(*static_cast<const T*>(p1),
*static_cast<const T*>(p2));
},
};
return &tbl;
};
};
class ChannelArgs {
public:
class Pointer {
public:
Pointer(void* p, const grpc_arg_pointer_vtable* vtable)
: p_(p), vtable_(vtable == nullptr ? empty_vtable() : vtable) {}
~Pointer() { vtable_->destroy(p_); }
Pointer(const Pointer& other)
: p_(other.vtable_->copy(other.p_)), vtable_(other.vtable_) {}
Pointer& operator=(Pointer other) {
std::swap(p_, other.p_);
std::swap(vtable_, other.vtable_);
return *this;
}
Pointer(Pointer&& other) noexcept : p_(other.p_), vtable_(other.vtable_) {
other.p_ = nullptr;
other.vtable_ = empty_vtable();
}
Pointer& operator=(Pointer&& other) noexcept {
std::swap(p_, other.p_);
std::swap(vtable_, other.vtable_);
return *this;
}
bool operator==(const Pointer& rhs) const;
bool operator<(const Pointer& rhs) const;
bool operator!=(const Pointer& rhs) const { return !(*this == rhs); }
void* c_pointer() const { return p_; }
const grpc_arg_pointer_vtable* c_vtable() const { return vtable_; }
private:
void* p_;
const grpc_arg_pointer_vtable* vtable_;
static const grpc_arg_pointer_vtable* empty_vtable() {
static const grpc_arg_pointer_vtable vtable = {
// copy
[](void* p) { return p; },
// destroy
[](void*) {},
// cmp
[](void* p1, void* p2) -> int { return QsortCompare(p1, p2); },
};
return &vtable;
}
};
using Value = absl::variant<int, std::string, Pointer>;
ChannelArgs();
static ChannelArgs FromC(const grpc_channel_args* args);
const grpc_channel_args* ToC() const;
const Value* Get(absl::string_view name) const { return args_.Lookup(name); }
GRPC_MUST_USE_RESULT ChannelArgs Set(absl::string_view name,
Value value) const;
GRPC_MUST_USE_RESULT ChannelArgs Set(grpc_arg arg) const;
template <typename T>
GRPC_MUST_USE_RESULT absl::enable_if_t<
std::is_same<const grpc_arg_pointer_vtable*,
decltype(ChannelArgTypeTraits<T>::vtable())>::value,
ChannelArgs>
Set(absl::string_view name, T* value) const {
return Set(name,
Pointer(ChannelArgTypeTraits<T>::take_unowned_pointer(value),
ChannelArgTypeTraits<T>::vtable()));
}
template <typename T>
GRPC_MUST_USE_RESULT absl::enable_if_t<
std::is_same<const grpc_arg_pointer_vtable*,
decltype(ChannelArgTypeTraits<T>::vtable())>::value,
ChannelArgs>
Set(absl::string_view name, RefCountedPtr<T> value) const {
return Set(name,
Pointer(value.release(), ChannelArgTypeTraits<T>::vtable()));
}
GRPC_MUST_USE_RESULT ChannelArgs Remove(absl::string_view name) const;
bool Contains(absl::string_view name) const { return Get(name) != nullptr; }
absl::optional<int> GetInt(absl::string_view name) const;
absl::optional<absl::string_view> GetString(absl::string_view name) const;
void* GetVoidPointer(absl::string_view name) const;
template <typename T>
T* GetPointer(absl::string_view name) const {
return static_cast<T*>(GetVoidPointer(name));
}
// Object based get/set.
// Deal with the common case that we set a pointer to an object under
// the same name in every usage.
// Expects ChannelArgTypeTraits to exist for T, and T to expose:
// static string_view channel_arg_name();
template <typename T>
GRPC_MUST_USE_RESULT ChannelArgs SetObject(T* p) const {
return Set(T::channel_arg_name(), p);
}
template <typename T>
GRPC_MUST_USE_RESULT ChannelArgs SetObject(RefCountedPtr<T> p) const {
return Set(T::channel_arg_name(), std::move(p));
}
template <typename T>
T* GetObject() {
return GetPointer<T>(T::channel_arg_name());
}
bool operator<(const ChannelArgs& other) const { return args_ < other.args_; }
bool operator==(const ChannelArgs& other) const {
return args_ == other.args_;
}
private:
explicit ChannelArgs(AVL<std::string, Value> args) : args_(args) {}
AVL<std::string, Value> args_;
};
} // namespace grpc_core
// Channel args are intentionally immutable, to avoid the need for locking.
/** Copy the arguments in \a src into a new instance */
@ -120,15 +280,9 @@ grpc_arg grpc_channel_arg_pointer_create(char* name, void* value,
std::string grpc_channel_args_string(const grpc_channel_args* args);
namespace grpc_core {
/** Remove any channel args prefixed with 'grpc.internal.'
* These are used for internal implementation details and are not intended to
* be exposed to users.
* Returns a new channel args instance.
* Does not take ownership of \a src.
* Should be called by any public API that receives channel args. */
const grpc_channel_args* RemoveGrpcInternalArgs(const grpc_channel_args* src);
/** Ensure no duplicate channel args, in preparation for moving to a map<> */
const grpc_channel_args* UniquifyChannelArgKeys(const grpc_channel_args* src);
/** Ensure no duplicate channel args (with some backwards compatibility hacks),
* and return a C++ object */
ChannelArgs UniquifyChannelArgKeys(const grpc_channel_args* src);
} // namespace grpc_core
// Takes ownership of the old_args

@ -25,10 +25,6 @@ void ChannelArgsPreconditioning::Builder::RegisterStage(Stage stage) {
}
ChannelArgsPreconditioning ChannelArgsPreconditioning::Builder::Build() {
// TODO(ctiller): should probably make this registered too.
stages_.emplace_back(RemoveGrpcInternalArgs);
stages_.emplace_back(UniquifyChannelArgKeys);
ChannelArgsPreconditioning preconditioning;
preconditioning.stages_ = std::move(stages_);
return preconditioning;
@ -36,13 +32,11 @@ ChannelArgsPreconditioning ChannelArgsPreconditioning::Builder::Build() {
const grpc_channel_args* ChannelArgsPreconditioning::PreconditionChannelArgs(
const grpc_channel_args* args) const {
const grpc_channel_args* owned_args = nullptr;
ChannelArgs channel_args = UniquifyChannelArgKeys(args);
for (auto& stage : stages_) {
args = stage(args);
grpc_channel_args_destroy(owned_args);
owned_args = args;
channel_args = stage(std::move(channel_args));
}
return args;
return channel_args.ToC();
}
} // namespace grpc_core

@ -33,8 +33,7 @@ class ChannelArgsPreconditioning {
// Take channel args and mutate them.
// Does not take ownership of the channel args passed in.
// Returns a new channel args object that is owned by the caller.
using Stage =
std::function<const grpc_channel_args*(const grpc_channel_args*)>;
using Stage = std::function<ChannelArgs(ChannelArgs)>;
class Builder {
public:

@ -20,6 +20,7 @@
#include "src/core/lib/gpr/useful.h"
#include "src/core/lib/iomgr/exec_ctx.h"
#include "src/core/lib/resource_quota/resource_quota.h"
namespace grpc_core {
@ -30,30 +31,13 @@ ResourceQuotaRefPtr ResourceQuotaFromChannelArgs(
->Ref();
}
namespace {
grpc_arg MakeArg(ResourceQuota* quota) {
return grpc_channel_arg_pointer_create(
const_cast<char*>(GRPC_ARG_RESOURCE_QUOTA), quota,
grpc_resource_quota_arg_vtable());
}
const grpc_channel_args* EnsureResourceQuotaInChannelArgs(
const grpc_channel_args* args) {
const grpc_arg* existing =
grpc_channel_args_find(args, GRPC_ARG_RESOURCE_QUOTA);
if (existing != nullptr && existing->type == GRPC_ARG_POINTER &&
existing->value.pointer.p != nullptr) {
return grpc_channel_args_copy(args);
}
ChannelArgs EnsureResourceQuotaInChannelArgs(ChannelArgs args) {
if (args.GetObject<ResourceQuota>() != nullptr) return args;
// If there's no existing quota, add it to the default one - shared between
// all channel args declared thusly. This prevents us from accidentally not
// sharing subchannels due to their channel args not specifying a quota.
const char* remove[] = {GRPC_ARG_RESOURCE_QUOTA};
auto new_arg = MakeArg(ResourceQuota::Default().get());
return grpc_channel_args_copy_and_add_and_remove(args, remove, 1, &new_arg,
1);
return args.SetObject(ResourceQuota::Default());
}
} // namespace
void RegisterResourceQuota(CoreConfiguration::Builder* builder) {
builder->channel_args_preconditioning()->RegisterStage(
@ -63,16 +47,7 @@ void RegisterResourceQuota(CoreConfiguration::Builder* builder) {
} // namespace grpc_core
extern "C" const grpc_arg_pointer_vtable* grpc_resource_quota_arg_vtable() {
static const grpc_arg_pointer_vtable vtable = {
// copy
[](void* p) -> void* {
return static_cast<grpc_core::ResourceQuota*>(p)->Ref().release();
},
// destroy
[](void* p) { static_cast<grpc_core::ResourceQuota*>(p)->Unref(); },
// compare
[](void* p, void* q) { return grpc_core::QsortCompare(p, q); }};
return &vtable;
return grpc_core::ChannelArgTypeTraits<grpc_core::ResourceQuota>::vtable();
}
extern "C" grpc_resource_quota* grpc_resource_quota_create(const char* name) {

@ -37,6 +37,10 @@ class ResourceQuota : public RefCounted<ResourceQuota>,
ResourceQuota(const ResourceQuota&) = delete;
ResourceQuota& operator=(const ResourceQuota&) = delete;
static absl::string_view channel_arg_name() {
return GRPC_ARG_RESOURCE_QUOTA;
}
MemoryQuotaRefPtr memory_quota() { return memory_quota_; }
const RefCountedPtr<ThreadQuota>& thread_quota() { return thread_quota_; }
@ -44,6 +48,8 @@ class ResourceQuota : public RefCounted<ResourceQuota>,
// The default global resource quota
static ResourceQuotaRefPtr Default();
bool operator<(const ResourceQuota& other) const { return this < &other; }
private:
MemoryQuotaRefPtr memory_quota_;
RefCountedPtr<ThreadQuota> thread_quota_;

@ -23,29 +23,27 @@ namespace grpc_core {
class Fuzzer {
public:
void Run(const avl_fuzzer::Msg& msg) {
CheckEqual();
for (const auto& action : msg.actions()) {
switch (action.action_case()) {
case avl_fuzzer::Action::kSet:
avl_ = avl_.Add(action.key(), action.set());
map_[action.key()] = action.set();
break;
case avl_fuzzer::Action::kDel:
avl_ = avl_.Remove(action.key());
map_.erase(action.key());
break;
case avl_fuzzer::Action::kGet: {
auto* p = avl_.Lookup(action.key());
auto it = map_.find(action.key());
if (it == map_.end() && p != nullptr) abort();
if (it != map_.end() && p == nullptr) abort();
if (it != map_.end() && it->second != *p) abort();
} break;
case avl_fuzzer::Action::ACTION_NOT_SET:
break;
}
CheckEqual();
Fuzzer() { CheckEqual(); }
~Fuzzer() { CheckEqual(); }
void Run(const avl_fuzzer::Action& action) {
switch (action.action_case()) {
case avl_fuzzer::Action::kSet:
avl_ = avl_.Add(action.key(), action.set());
map_[action.key()] = action.set();
break;
case avl_fuzzer::Action::kDel:
avl_ = avl_.Remove(action.key());
map_.erase(action.key());
break;
case avl_fuzzer::Action::kGet: {
auto* p = avl_.Lookup(action.key());
auto it = map_.find(action.key());
if (it == map_.end() && p != nullptr) abort();
if (it != map_.end() && p == nullptr) abort();
if (it != map_.end() && it->second != *p) abort();
} break;
case avl_fuzzer::Action::ACTION_NOT_SET:
break;
}
}
@ -65,8 +63,38 @@ class Fuzzer {
std::map<int, int> map_;
};
AVL<int, int> AvlFromProto(
const ::google::protobuf::RepeatedPtrField<avl_fuzzer::KeyValue>& p) {
AVL<int, int> a;
for (const auto& kv : p) {
a = a.Add(kv.key(), kv.value());
}
return a;
}
std::map<int, int> MapFromProto(
const ::google::protobuf::RepeatedPtrField<avl_fuzzer::KeyValue>& p) {
std::map<int, int> a;
for (const auto& kv : p) {
a[kv.key()] = kv.value();
}
return a;
}
} // namespace grpc_core
DEFINE_PROTO_FUZZER(const avl_fuzzer::Msg& msg) {
grpc_core::Fuzzer().Run(msg);
grpc_core::Fuzzer fuzzer;
for (const auto& action : msg.actions()) {
grpc_core::Fuzzer().Run(action);
}
for (const auto& cmp : msg.compares()) {
auto left_avl = grpc_core::AvlFromProto(cmp.left());
auto left_map = grpc_core::MapFromProto(cmp.left());
auto right_avl = grpc_core::AvlFromProto(cmp.right());
auto right_map = grpc_core::MapFromProto(cmp.right());
if ((left_avl == right_avl) != (left_map == right_map)) abort();
if ((left_avl < right_avl) != (left_map < right_map)) abort();
}
}

@ -27,6 +27,17 @@ message Action {
}
}
message KeyValue {
int32 key = 1;
int32 value = 2;
}
message Compares {
repeated KeyValue left = 1;
repeated KeyValue right = 2;
}
message Msg {
repeated Action actions = 2;
repeated Compares compares = 3;
}

@ -0,0 +1,9 @@
actions {
del {
}
}
compares {
left {
key: 50397184
}
}

@ -21,6 +21,7 @@ licenses(["notice"])
grpc_cc_test(
name = "channel_args_test",
srcs = ["channel_args_test.cc"],
external_deps = ["gtest"],
language = "C++",
uses_polling = False,
deps = [

@ -20,6 +20,8 @@
#include <string.h>
#include <gtest/gtest.h>
#include <grpc/grpc_security.h>
#include <grpc/impl/codegen/grpc_types.h>
#include <grpc/impl/codegen/log.h>
@ -27,11 +29,101 @@
#include "src/core/lib/channel/channel_stack.h"
#include "src/core/lib/gpr/useful.h"
#include "src/core/lib/gprpp/ref_counted.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/iomgr/exec_ctx.h"
#include "src/core/lib/surface/channel.h"
#include "test/core/util/test_config.h"
static void test_create(void) {
namespace grpc_core {
TEST(ChannelArgsTest, Noop) { ChannelArgs(); }
TEST(ChannelArgsTest, SetGetRemove) {
const grpc_arg_pointer_vtable malloc_vtable = {
// copy
[](void* p) { return p; },
// destroy
[](void*) {},
// equal
[](void* p1, void* p2) { return QsortCompare(p1, p2); },
};
void* ptr = gpr_malloc(42);
ChannelArgs a;
ChannelArgs b = a.Set("answer", 42);
ChannelArgs c = b.Set("foo", "bar");
ChannelArgs d = c.Set("ptr", ChannelArgs::Pointer(ptr, &malloc_vtable));
ChannelArgs e = d.Set("alpha", "beta");
ChannelArgs f = e.Remove("answer");
EXPECT_EQ(a.Get("answer"), nullptr);
EXPECT_EQ(*b.Get("answer"), ChannelArgs::Value(42));
EXPECT_EQ(*c.Get("answer"), ChannelArgs::Value(42));
EXPECT_EQ(c.GetInt("answer"), 42);
EXPECT_EQ(c.GetString("answer"), absl::nullopt);
EXPECT_EQ(f.Get("answer"), nullptr);
EXPECT_EQ(*c.Get("foo"), ChannelArgs::Value("bar"));
EXPECT_EQ(c.GetString("foo"), "bar");
EXPECT_EQ(c.GetString("answer"), absl::nullopt);
EXPECT_EQ(*d.Get("ptr"),
ChannelArgs::Value(ChannelArgs::Pointer(ptr, &malloc_vtable)));
EXPECT_EQ(*e.Get("alpha"), ChannelArgs::Value("beta"));
gpr_free(ptr);
}
TEST(ChannelArgsTest, StoreRefCountedPtr) {
struct Test : public RefCounted<Test> {
explicit Test(int n) : n(n) {}
int n;
bool operator<(const Test& rhs) const { return n < rhs.n; }
};
auto p = MakeRefCounted<Test>(123);
ChannelArgs a;
a = a.Set("test", std::move(p));
EXPECT_EQ(a.GetPointer<Test>("test")->n, 123);
}
TEST(ChannelArgsTest, ObjectApi) {
struct MyFancyObject : public RefCounted<MyFancyObject> {
explicit MyFancyObject(int n) : n(n) {}
static absl::string_view channel_arg_name() {
return "grpc.internal.my-fancy-object";
}
int n;
bool operator<(const MyFancyObject& rhs) const { return n < rhs.n; }
};
auto p = MakeRefCounted<MyFancyObject>(42);
ChannelArgs a;
a = a.SetObject(std::move(p));
EXPECT_EQ(a.GetObject<MyFancyObject>()->n, 42);
}
TEST(ChannelArgsTest, ToAndFromC) {
const grpc_arg_pointer_vtable malloc_vtable = {
// copy
[](void* p) { return p; },
// destroy
[](void*) {},
// equal
[](void* p1, void* p2) { return QsortCompare(p1, p2); },
};
void* ptr = gpr_malloc(42);
ChannelArgs a = ChannelArgs()
.Set("answer", 42)
.Set("foo", "bar")
.Set("ptr", ChannelArgs::Pointer(ptr, &malloc_vtable))
.Set("alpha", "beta");
const grpc_channel_args* c = a.ToC();
ChannelArgs b = ChannelArgs::FromC(c);
grpc_channel_args_destroy(c);
EXPECT_EQ(a, b);
gpr_free(ptr);
}
} // namespace grpc_core
TEST(GrpcChannelArgsTest, Create) {
grpc_core::ExecCtx exec_ctx;
grpc_arg to_add[2];
grpc_channel_args* ch_args;
@ -80,7 +172,7 @@ static int fake_pointer_cmp(void* a, void* b) {
static const grpc_arg_pointer_vtable fake_pointer_arg_vtable = {
fake_pointer_arg_copy, fake_pointer_arg_destroy, fake_pointer_cmp};
static void test_channel_create_with_args(void) {
TEST(GrpcChannelArgsTest, ChannelCreateWithArgs) {
grpc_arg client_a[3];
client_a[0] =
@ -131,53 +223,7 @@ grpc_channel_args* mutate_channel_args(const char* target,
return new_args;
}
// Minimal stack should not have client_idle filter
static bool channel_has_client_idle_filter(grpc_channel* c) {
grpc_channel_stack* stack = grpc_channel_get_channel_stack(c);
for (size_t i = 0; i < stack->count; i++) {
if (strcmp(grpc_channel_stack_element(stack, i)->filter->name,
"client_idle") == 0) {
return true;
}
}
return false;
}
static void test_channel_create_with_global_mutator(void) {
grpc_channel_args_set_client_channel_creation_mutator(mutate_channel_args);
// We also add some custom args to make sure the ownership is correct.
grpc_arg client_a[3];
client_a[0] =
grpc_channel_arg_integer_create(const_cast<char*>("arg_int"), 0);
client_a[1] = grpc_channel_arg_string_create(
const_cast<char*>("arg_str"), const_cast<char*>("arg_str_val"));
// allocated and adds custom pointer arg
fake_class* fc = static_cast<fake_class*>(gpr_malloc(sizeof(fake_class)));
fc->foo = 42;
client_a[2] = grpc_channel_arg_pointer_create(
const_cast<char*>("arg_pointer"), fc, &fake_pointer_arg_vtable);
// creates channels
grpc_channel_args client_args = {GPR_ARRAY_SIZE(client_a), client_a};
grpc_channel_credentials* creds = grpc_insecure_credentials_create();
grpc_channel* c = grpc_channel_create("no_op_mutator", creds, &client_args);
grpc_channel_credentials_release(creds);
GPR_ASSERT(channel_has_client_idle_filter(c));
grpc_channel_destroy(c);
grpc_channel_credentials* another_creds = grpc_insecure_credentials_create();
c = grpc_channel_create("minimal_stack_mutator", another_creds, &client_args);
grpc_channel_credentials_release(another_creds);
GPR_ASSERT(channel_has_client_idle_filter(c) == false);
grpc_channel_destroy(c);
gpr_free(fc);
auto mutator = grpc_channel_args_get_client_channel_creation_mutator();
GPR_ASSERT(mutator == &mutate_channel_args);
}
static void test_server_create_with_args(void) {
TEST(GrpcChannelArgsTest, TestServerCreateWithArgs) {
grpc_arg server_a[3];
// adds integer arg
@ -207,14 +253,10 @@ static void test_server_create_with_args(void) {
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
grpc::testing::TestEnvironment env(argc, argv);
grpc_init();
test_create();
test_channel_create_with_args();
test_server_create_with_args();
// This has to be the last test.
// TODO(markdroth): re-enable this test once client_idle is re-enabled
// test_channel_create_with_global_mutator();
auto r = RUN_ALL_TESTS();
grpc_shutdown();
return 0;
return r;
}

@ -1981,10 +1981,12 @@ src/core/lib/gprpp/global_config_generic.h \
src/core/lib/gprpp/host_port.cc \
src/core/lib/gprpp/host_port.h \
src/core/lib/gprpp/manual_constructor.h \
src/core/lib/gprpp/match.h \
src/core/lib/gprpp/memory.h \
src/core/lib/gprpp/mpscq.cc \
src/core/lib/gprpp/mpscq.h \
src/core/lib/gprpp/orphanable.h \
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/stat.h \

@ -1775,10 +1775,12 @@ src/core/lib/gprpp/global_config_generic.h \
src/core/lib/gprpp/host_port.cc \
src/core/lib/gprpp/host_port.h \
src/core/lib/gprpp/manual_constructor.h \
src/core/lib/gprpp/match.h \
src/core/lib/gprpp/memory.h \
src/core/lib/gprpp/mpscq.cc \
src/core/lib/gprpp/mpscq.h \
src/core/lib/gprpp/orphanable.h \
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/stat.h \

@ -525,30 +525,6 @@
],
"uses_polling": true
},
{
"args": [],
"benchmark": false,
"ci_platforms": [
"linux",
"mac",
"posix",
"windows"
],
"cpu_cost": 1.0,
"exclude_configs": [],
"exclude_iomgrs": [],
"flaky": false,
"gtest": false,
"language": "c",
"name": "channel_args_test",
"platforms": [
"linux",
"mac",
"posix",
"windows"
],
"uses_polling": false
},
{
"args": [],
"benchmark": false,
@ -3303,6 +3279,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": "channel_args_test",
"platforms": [
"linux",
"mac",
"posix",
"windows"
],
"uses_polling": false
},
{
"args": [],
"benchmark": false,

Loading…
Cancel
Save