From 5d710a5dfe7110c73e5f755e842b1b2360969875 Mon Sep 17 00:00:00 2001 From: josh Date: Wed, 18 Oct 2017 22:00:26 -0600 Subject: [PATCH 01/70] Update Bazel WORKSPACE file and third_party/cares BUILD files to that Bazel can build gRPC as a dependency. The WORKSPACE file now pulls third_party dependencies directly instead of requiring a git submodule init and update. The git hashes pulled by Bazel are exactly the same as the git submodules checked into third_party. A tiny bit of complexity comes from the c-ares project's use of ares_config.h.cmake and ares_build.c.cmake. The current solution to this problem is to check in a valid ares_config.h and a few valid ares_config.h files. This is the same general approach used still, but ares_config.h and ares_build.h are supplied to cares.BUILD from the only remaining local_repository, which serves only to export these two header files. --- WORKSPACE | 61 ++++--- third_party/cares/cares.BUILD | 194 ++++++++++++---------- third_party/cares/cares_local_files.BUILD | 21 +++ 3 files changed, 167 insertions(+), 109 deletions(-) create mode 100644 third_party/cares/cares_local_files.BUILD diff --git a/WORKSPACE b/WORKSPACE index 907cef1fcae..8734ad15640 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -10,7 +10,7 @@ bind( bind( name = "zlib", - actual = "@submodule_zlib//:z", + actual = "@com_github_madler_zlib//:z", ) bind( @@ -30,22 +30,22 @@ bind( bind( name = "cares", - actual = "@submodule_cares//:ares", + actual = "@com_github_cares_cares//:ares", ) bind( name = "gtest", - actual = "@submodule_gtest//:gtest", + actual = "@com_github_google_googletest//:gtest", ) bind( name = "gmock", - actual = "@submodule_gtest//:gmock", + actual = "@com_github_google_googletest//:gmock", ) bind( name = "benchmark", - actual = "@submodule_benchmark//:benchmark", + actual = "@com_github_google_benchmark//:benchmark", ) bind( @@ -53,47 +53,60 @@ bind( actual = "@com_github_gflags_gflags//:gflags", ) -local_repository( +http_archive( name = "boringssl", - path = "third_party/boringssl-with-bazel", + # on the master-with-bazel branch + url = "https://boringssl.googlesource.com/boringssl/+archive/886e7d75368e3f4fab3f4d0d3584e4abfc557755.tar.gz", ) -new_local_repository( - name = "submodule_zlib", +new_http_archive( + name = "com_github_madler_zlib", build_file = "third_party/zlib.BUILD", - path = "third_party/zlib", + strip_prefix = "zlib-cacf7f1d4e3d44d871b605da3b647f07d718623f", + url = "https://github.com/madler/zlib/archive/cacf7f1d4e3d44d871b605da3b647f07d718623f.tar.gz", ) -new_local_repository( +http_archive( name = "com_google_protobuf", - build_file = "third_party/protobuf/BUILD", - path = "third_party/protobuf", + strip_prefix = "protobuf-80a37e0782d2d702d52234b62dd4b9ec74fd2c95", + url = "https://github.com/google/protobuf/archive/80a37e0782d2d702d52234b62dd4b9ec74fd2c95.tar.gz", ) -new_local_repository( - name = "submodule_gtest", +new_http_archive( + name = "com_github_google_googletest", build_file = "third_party/gtest.BUILD", - path = "third_party/googletest", + strip_prefix = "googletest-ec44c6c1675c25b9827aacd08c02433cccde7780", + url = "https://github.com/google/googletest/archive/ec44c6c1675c25b9827aacd08c02433cccde7780.tar.gz", ) -local_repository( +http_archive( name = "com_github_gflags_gflags", - path = "third_party/gflags", + strip_prefix = "gflags-30dbc81fb5ffdc98ea9b14b1918bfe4e8779b26e", + url = "https://github.com/gflags/gflags/archive/30dbc81fb5ffdc98ea9b14b1918bfe4e8779b26e.tar.gz", ) -new_local_repository( - name = "submodule_benchmark", - path = "third_party/benchmark", +new_http_archive( + name = "com_github_google_benchmark", build_file = "third_party/benchmark.BUILD", + strip_prefix = "benchmark-44c25c892a6229b20db7cd9dc05584ea865896de", + url = "https://github.com/google/benchmark/archive/44c25c892a6229b20db7cd9dc05584ea865896de.tar.gz", ) new_local_repository( - name = "submodule_cares", + name = "cares_local_files", + build_file = "third_party/cares/cares_local_files.BUILD", path = "third_party/cares", +) + +new_http_archive( + name = "com_github_cares_cares", build_file = "third_party/cares/cares.BUILD", + strip_prefix = "c-ares-3be1924221e1326df520f8498d704a5c4c8d0cce", + url = "https://github.com/c-ares/c-ares/archive/3be1924221e1326df520f8498d704a5c4c8d0cce.tar.gz", ) -local_repository( +http_archive( name = "com_google_absl", - path = "third_party/abseil-cpp", + strip_prefix = "abseil-cpp-cc4bed2d74f7c8717e31f9579214ab52a9c9c610", + url = "https://github.com/abseil/abseil-cpp/archive/cc4bed2d74f7c8717e31f9579214ab52a9c9c610.tar.gz", ) diff --git a/third_party/cares/cares.BUILD b/third_party/cares/cares.BUILD index 978e9b1ed9d..87dcba40638 100644 --- a/third_party/cares/cares.BUILD +++ b/third_party/cares/cares.BUILD @@ -3,97 +3,112 @@ config_setting( values = {"cpu": "darwin"}, ) +genrule( + name = "ares_build", + srcs = ["@cares_local_files//:ares_build_h"], + outs = ["ares_build.h"], + cmd = "cat $(location @cares_local_files//:ares_build_h) > $@", +) + +cc_library( + name = "ares_build_h", + hdrs = ["ares_build.h"], + data = [":ares_build"], + includes = ["."], +) + +genrule( + name = "ares_config", + srcs = ["@cares_local_files//:ares_config_h"], + outs = ["ares_config.h"], + cmd = "cat $(location @cares_local_files//:ares_config_h) > $@", +) + +cc_library( + name = "ares_config_h", + hdrs = ["ares_config.h"], + data = [":ares_config"], + includes = ["."], +) + cc_library( name = "ares", srcs = [ - "cares/ares__close_sockets.c", - "cares/ares__get_hostent.c", - "cares/ares__read_line.c", - "cares/ares__timeval.c", - "cares/ares_cancel.c", - "cares/ares_create_query.c", - "cares/ares_data.c", - "cares/ares_destroy.c", - "cares/ares_expand_name.c", - "cares/ares_expand_string.c", - "cares/ares_fds.c", - "cares/ares_free_hostent.c", - "cares/ares_free_string.c", - "cares/ares_getenv.c", - "cares/ares_gethostbyaddr.c", - "cares/ares_gethostbyname.c", - "cares/ares_getnameinfo.c", - "cares/ares_getopt.c", - "cares/ares_getsock.c", - "cares/ares_init.c", - "cares/ares_library_init.c", - "cares/ares_llist.c", - "cares/ares_mkquery.c", - "cares/ares_nowarn.c", - "cares/ares_options.c", - "cares/ares_parse_a_reply.c", - "cares/ares_parse_aaaa_reply.c", - "cares/ares_parse_mx_reply.c", - "cares/ares_parse_naptr_reply.c", - "cares/ares_parse_ns_reply.c", - "cares/ares_parse_ptr_reply.c", - "cares/ares_parse_soa_reply.c", - "cares/ares_parse_srv_reply.c", - "cares/ares_parse_txt_reply.c", - "cares/ares_platform.c", - "cares/ares_process.c", - "cares/ares_query.c", - "cares/ares_search.c", - "cares/ares_send.c", - "cares/ares_strcasecmp.c", - "cares/ares_strdup.c", - "cares/ares_strerror.c", - "cares/ares_timeout.c", - "cares/ares_version.c", - "cares/ares_writev.c", - "cares/bitncmp.c", - "cares/inet_net_pton.c", - "cares/inet_ntop.c", - "cares/windows_port.c", + "ares__close_sockets.c", + "ares__get_hostent.c", + "ares__read_line.c", + "ares__timeval.c", + "ares_cancel.c", + "ares_create_query.c", + "ares_data.c", + "ares_destroy.c", + "ares_expand_name.c", + "ares_expand_string.c", + "ares_fds.c", + "ares_free_hostent.c", + "ares_free_string.c", + "ares_getenv.c", + "ares_gethostbyaddr.c", + "ares_gethostbyname.c", + "ares_getnameinfo.c", + "ares_getopt.c", + "ares_getsock.c", + "ares_init.c", + "ares_library_init.c", + "ares_llist.c", + "ares_mkquery.c", + "ares_nowarn.c", + "ares_options.c", + "ares_parse_a_reply.c", + "ares_parse_aaaa_reply.c", + "ares_parse_mx_reply.c", + "ares_parse_naptr_reply.c", + "ares_parse_ns_reply.c", + "ares_parse_ptr_reply.c", + "ares_parse_soa_reply.c", + "ares_parse_srv_reply.c", + "ares_parse_txt_reply.c", + "ares_platform.c", + "ares_process.c", + "ares_query.c", + "ares_search.c", + "ares_send.c", + "ares_strcasecmp.c", + "ares_strdup.c", + "ares_strerror.c", + "ares_timeout.c", + "ares_version.c", + "ares_writev.c", + "bitncmp.c", + "inet_net_pton.c", + "inet_ntop.c", + "windows_port.c", ], hdrs = [ + "ares.h", "ares_build.h", - "cares/ares.h", - "cares/ares_data.h", - "cares/ares_dns.h", - "cares/ares_getenv.h", - "cares/ares_getopt.h", - "cares/ares_inet_net_pton.h", - "cares/ares_iphlpapi.h", - "cares/ares_ipv6.h", - "cares/ares_library_init.h", - "cares/ares_llist.h", - "cares/ares_nowarn.h", - "cares/ares_platform.h", - "cares/ares_private.h", - "cares/ares_rules.h", - "cares/ares_setup.h", - "cares/ares_strcasecmp.h", - "cares/ares_strdup.h", - "cares/ares_version.h", - "cares/bitncmp.h", - "cares/config-win32.h", - "cares/nameser.h", - "cares/setup_once.h", - ] + select({ - ":darwin": ["config_darwin/ares_config.h"], - "//conditions:default": ["config_linux/ares_config.h"], - }), - includes = [ - ".", - "cares" - ] + select({ - ":darwin": ["config_darwin"], - "//conditions:default": ["config_linux"], - }), - linkstatic = 1, - visibility = [ - "//visibility:public", + "ares_config.h", + "ares_data.h", + "ares_dns.h", + "ares_getenv.h", + "ares_getopt.h", + "ares_inet_net_pton.h", + "ares_iphlpapi.h", + "ares_ipv6.h", + "ares_library_init.h", + "ares_llist.h", + "ares_nowarn.h", + "ares_platform.h", + "ares_private.h", + "ares_rules.h", + "ares_setup.h", + "ares_strcasecmp.h", + "ares_strdup.h", + "ares_version.h", + "bitncmp.h", + "config-win32.h", + "nameser.h", + "setup_once.h", ], copts = [ "-D_GNU_SOURCE", @@ -101,4 +116,13 @@ cc_library( "-DNOMINMAX", "-DHAVE_CONFIG_H", ], + data = [ + ":ares_build", + ":ares_config", + ], + includes = ["."], + linkstatic = 1, + visibility = [ + "//visibility:public", + ], ) diff --git a/third_party/cares/cares_local_files.BUILD b/third_party/cares/cares_local_files.BUILD new file mode 100644 index 00000000000..0861b6fa6dc --- /dev/null +++ b/third_party/cares/cares_local_files.BUILD @@ -0,0 +1,21 @@ +package( + default_visibility = ["//visibility:public"], +) + +config_setting( + name = "darwin", + values = {"cpu": "darwin"}, +) + +filegroup( + name = "ares_build_h", + srcs = ["ares_build.h"], +) + +filegroup( + name = "ares_config_h", + srcs = select({ + ":darwin": ["config_darwin/ares_config.h"], + "//conditions:default": ["config_linux/ares_config.h"], + }), +) From 1194782c420bf4c2b4970a460fb2bb5d199441b5 Mon Sep 17 00:00:00 2001 From: kwasimensah Date: Fri, 27 Oct 2017 12:35:55 -0400 Subject: [PATCH 02/70] Add logic to work around buggy Android NDKs Old version of the Android NDK have linker issues with thread local support https://github.com/android-ndk/ndk/issues/8 and isn't actually fixed until r12b https://developer.android.com/ndk/downloads/revision_history.html. ABSL's config.h is being updated to catch this case and having gRPC rely on that will make sure it also gets the fix (rather than repeating a somewhat lengthy macro). Since gRPC already has a dependency on ABSL, I figured this wouldn't be an issue. --- include/grpc/impl/codegen/port_platform.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/include/grpc/impl/codegen/port_platform.h b/include/grpc/impl/codegen/port_platform.h index fb4bfc3162f..324dbe09035 100644 --- a/include/grpc/impl/codegen/port_platform.h +++ b/include/grpc/impl/codegen/port_platform.h @@ -297,6 +297,22 @@ #endif #endif /* GPR_NO_AUTODETECT_PLATFORM */ +// Android specifc logic to detect brokeness around TLS support. +#if defined(__ANDROID__) && defined(__clang__) +#include "third_party/absl/base/config.h" + +#if !defined(ABSL_HAVE_TLS) && defined(GPR_GCC_TLS) +#undef GPR_GCC_TLS +#define GPR_PTHREAD_TLS 1 +#endif //!defined(ABSL_HAVE_TLS) && defined(GPR_GCC_TLS) + +// cpu_posix.cc depends on TLS and cpu_linux.cc doesn't. +#if !defined(GPR_GCC_TLS) && defined(GPR_CPU_POSIX) +#undef GPR_CPU_POSIX +#define GPR_CPU_LINUX 1 +#endif //! defined(GPR_GCC_TLS) && defined(GPR_CPU_POSIX) +#endif // defined(__ANDROID__) && defined(__clang__) + #if defined(__has_include) #if __has_include() #define GRPC_HAS_CXX11_ATOMIC From 51c7bf8425da0761271eab4d330cdbd5fc354d5f Mon Sep 17 00:00:00 2001 From: kwasimensah Date: Fri, 27 Oct 2017 12:39:41 -0400 Subject: [PATCH 03/70] Update port_platform.h --- include/grpc/impl/codegen/port_platform.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/grpc/impl/codegen/port_platform.h b/include/grpc/impl/codegen/port_platform.h index 324dbe09035..5784fcee4c7 100644 --- a/include/grpc/impl/codegen/port_platform.h +++ b/include/grpc/impl/codegen/port_platform.h @@ -299,7 +299,7 @@ // Android specifc logic to detect brokeness around TLS support. #if defined(__ANDROID__) && defined(__clang__) -#include "third_party/absl/base/config.h" +#include "absl/base/config.h" #if !defined(ABSL_HAVE_TLS) && defined(GPR_GCC_TLS) #undef GPR_GCC_TLS From 9757fd25efd2acd718ef741143516b9eb1113003 Mon Sep 17 00:00:00 2001 From: kwasimensah Date: Fri, 27 Oct 2017 12:41:39 -0400 Subject: [PATCH 04/70] Update BUILD --- BUILD | 1 + 1 file changed, 1 insertion(+) diff --git a/BUILD b/BUILD index 99411f955e7..e81b4851430 100644 --- a/BUILD +++ b/BUILD @@ -551,6 +551,7 @@ grpc_cc_library( "include/grpc/impl/codegen/sync_posix.h", "include/grpc/impl/codegen/sync_windows.h", ], + deps = ["@com_google_absl//absl/base:config"], ) grpc_cc_library( From c58952649f27b064ceabedafc84096b5d137d361 Mon Sep 17 00:00:00 2001 From: kwasimensah Date: Mon, 30 Oct 2017 12:26:46 -0400 Subject: [PATCH 05/70] Got cpu_posix.cc to work without tls --- include/grpc/impl/codegen/port_platform.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/include/grpc/impl/codegen/port_platform.h b/include/grpc/impl/codegen/port_platform.h index 5784fcee4c7..dc5a6ea6e56 100644 --- a/include/grpc/impl/codegen/port_platform.h +++ b/include/grpc/impl/codegen/port_platform.h @@ -306,13 +306,6 @@ #define GPR_PTHREAD_TLS 1 #endif //!defined(ABSL_HAVE_TLS) && defined(GPR_GCC_TLS) -// cpu_posix.cc depends on TLS and cpu_linux.cc doesn't. -#if !defined(GPR_GCC_TLS) && defined(GPR_CPU_POSIX) -#undef GPR_CPU_POSIX -#define GPR_CPU_LINUX 1 -#endif //! defined(GPR_GCC_TLS) && defined(GPR_CPU_POSIX) -#endif // defined(__ANDROID__) && defined(__clang__) - #if defined(__has_include) #if __has_include() #define GRPC_HAS_CXX11_ATOMIC From 62e6b1c444916da1c1e725bb5e5952488b1692ca Mon Sep 17 00:00:00 2001 From: kwasimensah Date: Mon, 30 Oct 2017 12:28:58 -0400 Subject: [PATCH 06/70] Use pthreads on posix Use pthreads on posix instead of relying on thread local support --- src/core/lib/support/cpu_posix.cc | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/core/lib/support/cpu_posix.cc b/src/core/lib/support/cpu_posix.cc index 503a96b4c8a..c32ca4659c7 100644 --- a/src/core/lib/support/cpu_posix.cc +++ b/src/core/lib/support/cpu_posix.cc @@ -18,19 +18,20 @@ #include -#ifdef GPR_CPU_POSIX +#if defined(GPR_CPU_POSIX) #include +#include #include #include +#include + #include #include #include #include -static __thread char magic_thread_local; - static long ncpus = 0; static void init_ncpus() { @@ -52,7 +53,27 @@ unsigned gpr_cpu_current_cpu(void) { most code that's using this is using it to shard across work queues though, so here we use thread identity instead to achieve a similar though not identical effect */ - return (unsigned)GPR_HASH_POINTER(&magic_thread_local, gpr_cpu_num_cores()); + static auto DeleteValue = [](void *value_ptr) { + unsigned int *value = static_cast(value_ptr); + if (value) { + delete value; + } + }; + static pthread_key_t thread_id_key; + static int thread_id_key_create_result __attribute__((unused)) = + pthread_key_create(&thread_id_key, DeleteValue); + // pthread_t isn't portably defined to map to an integral type. So keep track + // of thread identity explicitly so hashing works reliably. + static std::atomic thread_counter(0); + + unsigned int *thread_id = + static_cast(pthread_getspecific(thread_id_key)); + if (thread_id == nullptr) { + thread_id = new unsigned int(thread_counter++); + pthread_setspecific(thread_id_key, thread_id); + } + + return (unsigned)GPR_HASH_POINTER(*thread_id, gpr_cpu_num_cores()); } #endif /* GPR_CPU_POSIX */ From 4b02f3b2b6bf0f186a293e44753c30492584aad0 Mon Sep 17 00:00:00 2001 From: kwasimensah Date: Wed, 1 Nov 2017 13:26:00 -0400 Subject: [PATCH 07/70] Updated to use gpr_free and gpr_malloc --- src/core/lib/support/cpu_posix.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/lib/support/cpu_posix.cc b/src/core/lib/support/cpu_posix.cc index c32ca4659c7..2be66ef54ea 100644 --- a/src/core/lib/support/cpu_posix.cc +++ b/src/core/lib/support/cpu_posix.cc @@ -27,6 +27,7 @@ #include +#include #include #include #include @@ -56,7 +57,7 @@ unsigned gpr_cpu_current_cpu(void) { static auto DeleteValue = [](void *value_ptr) { unsigned int *value = static_cast(value_ptr); if (value) { - delete value; + gpr_free(value); } }; static pthread_key_t thread_id_key; @@ -69,7 +70,8 @@ unsigned gpr_cpu_current_cpu(void) { unsigned int *thread_id = static_cast(pthread_getspecific(thread_id_key)); if (thread_id == nullptr) { - thread_id = new unsigned int(thread_counter++); + thread_id = static_cast(gpr_malloc(sizeof(unsigned int))); + *thread_id = thread_counter++; pthread_setspecific(thread_id_key, thread_id); } From 1a671dea71b133848017e4ab001bfeae6b009ab0 Mon Sep 17 00:00:00 2001 From: kwasimensah Date: Wed, 1 Nov 2017 13:28:48 -0400 Subject: [PATCH 08/70] Simplify macro logic here --- include/grpc/impl/codegen/port_platform.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/grpc/impl/codegen/port_platform.h b/include/grpc/impl/codegen/port_platform.h index dc5a6ea6e56..7dde5c61199 100644 --- a/include/grpc/impl/codegen/port_platform.h +++ b/include/grpc/impl/codegen/port_platform.h @@ -297,8 +297,6 @@ #endif #endif /* GPR_NO_AUTODETECT_PLATFORM */ -// Android specifc logic to detect brokeness around TLS support. -#if defined(__ANDROID__) && defined(__clang__) #include "absl/base/config.h" #if !defined(ABSL_HAVE_TLS) && defined(GPR_GCC_TLS) From 685b5d459f3aedce9a1fb15fae6481b8a585d961 Mon Sep 17 00:00:00 2001 From: kwasimensah Date: Wed, 1 Nov 2017 16:38:44 -0400 Subject: [PATCH 09/70] Fixing port_platform Copy over Absl macro to deal with C++/C differences. --- include/grpc/impl/codegen/port_platform.h | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/include/grpc/impl/codegen/port_platform.h b/include/grpc/impl/codegen/port_platform.h index 7dde5c61199..c2ec85d78db 100644 --- a/include/grpc/impl/codegen/port_platform.h +++ b/include/grpc/impl/codegen/port_platform.h @@ -297,12 +297,26 @@ #endif #endif /* GPR_NO_AUTODETECT_PLATFORM */ -#include "absl/base/config.h" - -#if !defined(ABSL_HAVE_TLS) && defined(GPR_GCC_TLS) +/* + * There are platforms for which TLS should not be used even though the compiler + * makes it seem like it's supported (Android NDK < r12b for example). + * This is primarily because of linker problems and toolchain misconfiguration: + * TLS isn't supported until NDK r12b per + * https://developer.android.com/ndk/downloads/revision_history.html + * Since NDK r16, `__NDK_MAJOR__` and `__NDK_MINOR__` are defined in + * . For NDK < r16, users should define these macros, + * e.g. `-D__NDK_MAJOR__=11 -D__NKD_MINOR__=0` for NDK r11. */ +#if defined(__ANDROID__) && defined(__clang__) && defined(GPR_GCC_TLS) +#if __has_include() +#include +#endif /* __has_include() */ +#if defined(__ANDROID__) && defined(__clang__) && defined(__NDK_MAJOR__) && \ + defined(__NDK_MINOR__) && \ + ((__NDK_MAJOR__ < 12) || ((__NDK_MAJOR__ == 12) && (__NDK_MINOR__ < 1))) #undef GPR_GCC_TLS #define GPR_PTHREAD_TLS 1 -#endif //!defined(ABSL_HAVE_TLS) && defined(GPR_GCC_TLS) +#endif +#endif /* defined(__ANDROID__) && defined(__clang__) && defined(GPR_GCC_TLS) */ #if defined(__has_include) #if __has_include() From 221522b455ad02f88e5ca0aa945f4cb7e7388ee8 Mon Sep 17 00:00:00 2001 From: kwasimensah Date: Wed, 1 Nov 2017 16:47:41 -0400 Subject: [PATCH 10/70] Revert BUILD change --- BUILD | 1 - 1 file changed, 1 deletion(-) diff --git a/BUILD b/BUILD index e81b4851430..99411f955e7 100644 --- a/BUILD +++ b/BUILD @@ -551,7 +551,6 @@ grpc_cc_library( "include/grpc/impl/codegen/sync_posix.h", "include/grpc/impl/codegen/sync_windows.h", ], - deps = ["@com_google_absl//absl/base:config"], ) grpc_cc_library( From ac299723bd9c8cee60779cc3513a9acf46e731a6 Mon Sep 17 00:00:00 2001 From: kwasimensah Date: Wed, 1 Nov 2017 19:50:17 -0400 Subject: [PATCH 11/70] Making clang format happy --- include/grpc/impl/codegen/port_platform.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/grpc/impl/codegen/port_platform.h b/include/grpc/impl/codegen/port_platform.h index c2ec85d78db..1906886d5e2 100644 --- a/include/grpc/impl/codegen/port_platform.h +++ b/include/grpc/impl/codegen/port_platform.h @@ -298,8 +298,8 @@ #endif /* GPR_NO_AUTODETECT_PLATFORM */ /* - * There are platforms for which TLS should not be used even though the compiler - * makes it seem like it's supported (Android NDK < r12b for example). + * There are platforms for which TLS should not be used even though the + * compiler makes it seem like it's supported (Android NDK < r12b for example). * This is primarily because of linker problems and toolchain misconfiguration: * TLS isn't supported until NDK r12b per * https://developer.android.com/ndk/downloads/revision_history.html @@ -316,7 +316,7 @@ #undef GPR_GCC_TLS #define GPR_PTHREAD_TLS 1 #endif -#endif /* defined(__ANDROID__) && defined(__clang__) && defined(GPR_GCC_TLS) */ +#endif /*defined(__ANDROID__) && defined(__clang__) && defined(GPR_GCC_TLS) */ #if defined(__has_include) #if __has_include() From bd42ca1110c0aff7a91bdb5e2077e6d69721f4f8 Mon Sep 17 00:00:00 2001 From: josh Date: Wed, 1 Nov 2017 22:37:17 -0600 Subject: [PATCH 12/70] Adding check_bazel_workspace.py to perform a sanity check that the external repo dependencies expressed via git submodules and those expressed as Bazel WORKSPACE rules agree exactly. --- .../run_tests/sanity/check_bazel_workspace.py | 49 +++++++++++++++++++ tools/run_tests/sanity/sanity_tests.yaml | 1 + 2 files changed, 50 insertions(+) create mode 100644 tools/run_tests/sanity/check_bazel_workspace.py diff --git a/tools/run_tests/sanity/check_bazel_workspace.py b/tools/run_tests/sanity/check_bazel_workspace.py new file mode 100644 index 00000000000..776c78b03f2 --- /dev/null +++ b/tools/run_tests/sanity/check_bazel_workspace.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python + +# Copyright 2016 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. + +from __future__ import print_function + +import ast +import os +import re +import subprocess +import sys + +os.chdir(os.path.join(os.path.dirname(sys.argv[0]), '../../..')) + +git_hash_pattern = re.compile('[0-9a-f]{40}') + +# Parse git hashes from submodules +git_submodules = subprocess.check_output('git submodule', shell=True).strip().split('\n') +git_submodule_hashes = {re.search(git_hash_pattern, s).group() for s in git_submodules} + +# Parse git hashes from Bazel WORKSPACE {new_}http_archive rules +with open('WORKSPACE', 'r') as f: + workspace_rules = [expr.value for expr in ast.parse(f.read()).body] + +http_archive_rules = [rule for rule in workspace_rules if rule.func.id.endswith('http_archive')] +archive_urls = [kw.value.s for rule in http_archive_rules for kw in rule.keywords if kw.arg == 'url'] +workspace_git_hashes = {re.search(git_hash_pattern, url).group() for url in archive_urls} + +# Validate the equivalence of the git submodules and Bazel git dependencies. The +# condition we impose is that there is a git submodule for every dependency in +# the workspace, but not necessarily conversely. E.g. Bloaty is a dependency +# not used by any of the targets built by Bazel. +if len(workspace_git_hashes - git_submodule_hashes) > 0: + print("Found discrepancies between git submodules and Bazel WORKSPACE dependencies") + sys.exit(1) + +sys.exit(0) diff --git a/tools/run_tests/sanity/sanity_tests.yaml b/tools/run_tests/sanity/sanity_tests.yaml index 4bc4b7c56f2..57292a4f40b 100644 --- a/tools/run_tests/sanity/sanity_tests.yaml +++ b/tools/run_tests/sanity/sanity_tests.yaml @@ -1,4 +1,5 @@ # a set of tests that are run in parallel for sanity tests +- script: tools/run_tests/sanity/check_bazel_workspace.py - script: tools/run_tests/sanity/check_cache_mk.sh - script: tools/run_tests/sanity/check_owners.sh - script: tools/run_tests/sanity/check_sources_and_headers.py From 60b376849a2c7b9faa8ae5a6ec2e1d6d1aacff43 Mon Sep 17 00:00:00 2001 From: ncteisen Date: Fri, 10 Nov 2017 18:06:09 -0800 Subject: [PATCH 13/70] eagerly free slice buffer after write --- src/core/lib/iomgr/tcp_posix.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/lib/iomgr/tcp_posix.cc b/src/core/lib/iomgr/tcp_posix.cc index fbbb1762b7f..0d78cf41649 100644 --- a/src/core/lib/iomgr/tcp_posix.cc +++ b/src/core/lib/iomgr/tcp_posix.cc @@ -605,9 +605,10 @@ static bool tcp_flush(grpc_exec_ctx* exec_ctx, grpc_tcp* tcp, if (tcp->outgoing_slice_idx == tcp->outgoing_buffer->count) { *error = GRPC_ERROR_NONE; + grpc_slice_buffer_reset_and_unref_internal(exec_ctx, tcp->outgoing_buffer); return true; } - }; + } } static void tcp_handle_write(grpc_exec_ctx* exec_ctx, void* arg /* grpc_tcp */, From ad5bec0ee9436e91684b6eed042837473ccbaea7 Mon Sep 17 00:00:00 2001 From: "Nicolas \"Pixel\" Noble" Date: Mon, 13 Nov 2017 09:40:18 +0100 Subject: [PATCH 14/70] Formatting. --- src/core/lib/support/cpu_posix.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/lib/support/cpu_posix.cc b/src/core/lib/support/cpu_posix.cc index 2be66ef54ea..a9284d80473 100644 --- a/src/core/lib/support/cpu_posix.cc +++ b/src/core/lib/support/cpu_posix.cc @@ -54,8 +54,8 @@ unsigned gpr_cpu_current_cpu(void) { most code that's using this is using it to shard across work queues though, so here we use thread identity instead to achieve a similar though not identical effect */ - static auto DeleteValue = [](void *value_ptr) { - unsigned int *value = static_cast(value_ptr); + static auto DeleteValue = [](void* value_ptr) { + unsigned int* value = static_cast(value_ptr); if (value) { gpr_free(value); } @@ -67,10 +67,10 @@ unsigned gpr_cpu_current_cpu(void) { // of thread identity explicitly so hashing works reliably. static std::atomic thread_counter(0); - unsigned int *thread_id = - static_cast(pthread_getspecific(thread_id_key)); + unsigned int* thread_id = + static_cast(pthread_getspecific(thread_id_key)); if (thread_id == nullptr) { - thread_id = static_cast(gpr_malloc(sizeof(unsigned int))); + thread_id = static_cast(gpr_malloc(sizeof(unsigned int))); *thread_id = thread_counter++; pthread_setspecific(thread_id_key, thread_id); } From 40737d67ee93f3c60be1f0ff6486d6f045646312 Mon Sep 17 00:00:00 2001 From: ncteisen Date: Mon, 13 Nov 2017 08:02:35 -0800 Subject: [PATCH 15/70] Add error string to recv status API --- include/grpc++/impl/codegen/call.h | 2 ++ include/grpc/impl/codegen/grpc_types.h | 1 + .../chttp2/transport/chttp2_transport.cc | 9 ++--- src/core/lib/channel/channel_stack.h | 1 + src/core/lib/surface/call.cc | 34 +++++++++++-------- src/core/lib/transport/error_utils.cc | 8 +++-- src/core/lib/transport/error_utils.h | 8 +++-- test/cpp/microbenchmarks/bm_error.cc | 6 ++-- 8 files changed, 43 insertions(+), 26 deletions(-) diff --git a/include/grpc++/impl/codegen/call.h b/include/grpc++/impl/codegen/call.h index 1a988297dc4..91f45b31472 100644 --- a/include/grpc++/impl/codegen/call.h +++ b/include/grpc++/impl/codegen/call.h @@ -579,6 +579,7 @@ class CallOpClientRecvStatus { op->data.recv_status_on_client.trailing_metadata = metadata_map_->arr(); op->data.recv_status_on_client.status = &status_code_; op->data.recv_status_on_client.status_details = &error_message_; + op->data.recv_status_on_client.error_string = &error_string_; op->flags = 0; op->reserved = NULL; } @@ -605,6 +606,7 @@ class CallOpClientRecvStatus { Status* recv_status_; grpc_status_code status_code_; grpc_slice error_message_; + const char* error_string_; }; /// An abstract collection of call ops, used to generate the diff --git a/include/grpc/impl/codegen/grpc_types.h b/include/grpc/impl/codegen/grpc_types.h index 03be610d255..83b35b1dc47 100644 --- a/include/grpc/impl/codegen/grpc_types.h +++ b/include/grpc/impl/codegen/grpc_types.h @@ -558,6 +558,7 @@ typedef struct grpc_op { grpc_metadata_array* trailing_metadata; grpc_status_code* status; grpc_slice* status_details; + const char** error_string; } recv_status_on_client; struct grpc_op_recv_close_on_server { /** out argument, set to 1 if the call failed in any way (seen as a diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc index 034e6ed8ca9..d5c61d80aec 100644 --- a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc +++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc @@ -1756,7 +1756,7 @@ static void send_goaway(grpc_exec_ctx* exec_ctx, grpc_chttp2_transport* t, grpc_http2_error_code http_error; grpc_slice slice; grpc_error_get_status(exec_ctx, error, GRPC_MILLIS_INF_FUTURE, NULL, &slice, - &http_error); + &http_error, NULL); grpc_chttp2_goaway_append(t->last_new_stream_id, (uint32_t)http_error, grpc_slice_ref_internal(slice), &t->qbuf); grpc_chttp2_initiate_write(exec_ctx, t, @@ -2059,7 +2059,7 @@ void grpc_chttp2_cancel_stream(grpc_exec_ctx* exec_ctx, if (s->id != 0) { grpc_http2_error_code http_error; grpc_error_get_status(exec_ctx, due_to_error, s->deadline, NULL, NULL, - &http_error); + &http_error, NULL); grpc_slice_buffer_add( &t->qbuf, grpc_chttp2_rst_stream_create(s->id, (uint32_t)http_error, &s->stats.outgoing)); @@ -2077,7 +2077,8 @@ void grpc_chttp2_fake_status(grpc_exec_ctx* exec_ctx, grpc_chttp2_transport* t, grpc_chttp2_stream* s, grpc_error* error) { grpc_status_code status; grpc_slice slice; - grpc_error_get_status(exec_ctx, error, s->deadline, &status, &slice, NULL); + grpc_error_get_status(exec_ctx, error, s->deadline, &status, &slice, NULL, + NULL); if (status != GRPC_STATUS_OK) { s->seen_error = true; @@ -2243,7 +2244,7 @@ static void close_from_api(grpc_exec_ctx* exec_ctx, grpc_chttp2_transport* t, grpc_status_code grpc_status; grpc_slice slice; grpc_error_get_status(exec_ctx, error, s->deadline, &grpc_status, &slice, - NULL); + NULL, NULL); GPR_ASSERT(grpc_status >= 0 && (int)grpc_status < 100); diff --git a/src/core/lib/channel/channel_stack.h b/src/core/lib/channel/channel_stack.h index aa993112a02..41851382ae2 100644 --- a/src/core/lib/channel/channel_stack.h +++ b/src/core/lib/channel/channel_stack.h @@ -84,6 +84,7 @@ typedef struct { typedef struct { grpc_call_stats stats; grpc_status_code final_status; + const char* error_string; } grpc_call_final_info; /* Channel filters specify: diff --git a/src/core/lib/surface/call.cc b/src/core/lib/surface/call.cc index 9fd4fdbef9a..4ce494872ae 100644 --- a/src/core/lib/surface/call.cc +++ b/src/core/lib/surface/call.cc @@ -234,6 +234,7 @@ struct grpc_call { struct { grpc_status_code* status; grpc_slice* status_details; + const char** error_string; } client; struct { int* cancelled; @@ -286,7 +287,8 @@ static void receiving_slice_ready(grpc_exec_ctx* exec_ctx, void* bctlp, static void get_final_status(grpc_exec_ctx* exec_ctx, grpc_call* call, void (*set_value)(grpc_status_code code, void* user_data), - void* set_value_user_data, grpc_slice* details); + void* set_value_user_data, grpc_slice* details, + const char** error_string); static void set_status_value_directly(grpc_status_code status, void* dest); static void set_status_from_error(grpc_exec_ctx* exec_ctx, grpc_call* call, status_source source, grpc_error* error); @@ -548,7 +550,8 @@ static void destroy_call(grpc_exec_ctx* exec_ctx, void* call, } get_final_status(exec_ctx, c, set_status_value_directly, - &c->final_info.final_status, NULL); + &c->final_info.final_status, NULL, + &c->final_info.error_string); c->final_info.stats.latency = gpr_time_sub(gpr_now(GPR_CLOCK_MONOTONIC), c->start_time); @@ -734,16 +737,15 @@ static void cancel_with_status(grpc_exec_ctx* exec_ctx, grpc_call* c, * FINAL STATUS CODE MANIPULATION */ -static bool get_final_status_from(grpc_exec_ctx* exec_ctx, grpc_call* call, - grpc_error* error, bool allow_ok_status, - void (*set_value)(grpc_status_code code, - void* user_data), - void* set_value_user_data, - grpc_slice* details) { +static bool get_final_status_from( + grpc_exec_ctx* exec_ctx, grpc_call* call, grpc_error* error, + bool allow_ok_status, + void (*set_value)(grpc_status_code code, void* user_data), + void* set_value_user_data, grpc_slice* details, const char** error_string) { grpc_status_code code; grpc_slice slice = grpc_empty_slice(); grpc_error_get_status(exec_ctx, error, call->send_deadline, &code, &slice, - NULL); + NULL, error_string); if (code == GRPC_STATUS_OK && !allow_ok_status) { return false; } @@ -758,7 +760,8 @@ static bool get_final_status_from(grpc_exec_ctx* exec_ctx, grpc_call* call, static void get_final_status(grpc_exec_ctx* exec_ctx, grpc_call* call, void (*set_value)(grpc_status_code code, void* user_data), - void* set_value_user_data, grpc_slice* details) { + void* set_value_user_data, grpc_slice* details, + const char** error_string) { int i; received_status status[STATUS_SOURCE_COUNT]; for (i = 0; i < STATUS_SOURCE_COUNT; i++) { @@ -782,7 +785,7 @@ static void get_final_status(grpc_exec_ctx* exec_ctx, grpc_call* call, grpc_error_has_clear_grpc_status(status[i].error)) { if (get_final_status_from(exec_ctx, call, status[i].error, allow_ok_status != 0, set_value, - set_value_user_data, details)) { + set_value_user_data, details, error_string)) { return; } } @@ -792,7 +795,7 @@ static void get_final_status(grpc_exec_ctx* exec_ctx, grpc_call* call, if (status[i].is_set) { if (get_final_status_from(exec_ctx, call, status[i].error, allow_ok_status != 0, set_value, - set_value_user_data, details)) { + set_value_user_data, details, error_string)) { return; } } @@ -1333,10 +1336,11 @@ static void post_batch_completion(grpc_exec_ctx* exec_ctx, if (call->is_client) { get_final_status(exec_ctx, call, set_status_value_directly, call->final_op.client.status, - call->final_op.client.status_details); + call->final_op.client.status_details, + call->final_op.client.error_string); } else { get_final_status(exec_ctx, call, set_cancelled_value, - call->final_op.server.cancelled, NULL); + call->final_op.server.cancelled, NULL, NULL); } GRPC_ERROR_UNREF(error); @@ -1993,6 +1997,8 @@ static grpc_call_error call_start_batch(grpc_exec_ctx* exec_ctx, call->final_op.client.status = op->data.recv_status_on_client.status; call->final_op.client.status_details = op->data.recv_status_on_client.status_details; + call->final_op.client.error_string = + op->data.recv_status_on_client.error_string; stream_op->recv_trailing_metadata = true; stream_op->collect_stats = true; stream_op_payload->recv_trailing_metadata.recv_trailing_metadata = diff --git a/src/core/lib/transport/error_utils.cc b/src/core/lib/transport/error_utils.cc index d968b04fd87..15f98752704 100644 --- a/src/core/lib/transport/error_utils.cc +++ b/src/core/lib/transport/error_utils.cc @@ -41,8 +41,12 @@ static grpc_error* recursively_find_error_with_field(grpc_error* error, void grpc_error_get_status(grpc_exec_ctx* exec_ctx, grpc_error* error, grpc_millis deadline, grpc_status_code* code, - grpc_slice* slice, - grpc_http2_error_code* http_error) { + grpc_slice* slice, grpc_http2_error_code* http_error, + const char** full_error_details) { + if (full_error_details != NULL) { + *full_error_details = grpc_error_string(error); + } + // Start with the parent error and recurse through the tree of children // until we find the first one that has a status code. grpc_error* found_error = diff --git a/src/core/lib/transport/error_utils.h b/src/core/lib/transport/error_utils.h index 690e42058af..d47c4f2a3be 100644 --- a/src/core/lib/transport/error_utils.h +++ b/src/core/lib/transport/error_utils.h @@ -30,13 +30,15 @@ extern "C" { /// A utility function to get the status code and message to be returned /// to the application. If not set in the top-level message, looks /// through child errors until it finds the first one with these attributes. -/// All attributes are pulled from the same child error. If any of the -/// attributes (code, msg, http_status) are unneeded, they can be passed as +/// All attributes are pulled from the same child error. full_error_details will +/// be populated with the entire error string. If any of the attributes (code, +/// msg, http_status, full_error_details) are unneeded, they can be passed as /// NULL. void grpc_error_get_status(grpc_exec_ctx* exec_ctx, grpc_error* error, grpc_millis deadline, grpc_status_code* code, grpc_slice* slice, - grpc_http2_error_code* http_status); + grpc_http2_error_code* http_status, + const char** full_error_details); /// A utility function to check whether there is a clear status code that /// doesn't need to be guessed in \a error. This means that \a error or some diff --git a/test/cpp/microbenchmarks/bm_error.cc b/test/cpp/microbenchmarks/bm_error.cc index 56b80dfcf6d..955cdf5a171 100644 --- a/test/cpp/microbenchmarks/bm_error.cc +++ b/test/cpp/microbenchmarks/bm_error.cc @@ -253,7 +253,7 @@ static void BM_ErrorGetStatus(benchmark::State& state) { grpc_status_code status; grpc_slice slice; grpc_error_get_status(&exec_ctx, fixture.error(), fixture.deadline(), - &status, &slice, NULL); + &status, &slice, NULL, NULL); } grpc_exec_ctx_finish(&exec_ctx); track_counters.Finish(state); @@ -267,7 +267,7 @@ static void BM_ErrorGetStatusCode(benchmark::State& state) { while (state.KeepRunning()) { grpc_status_code status; grpc_error_get_status(&exec_ctx, fixture.error(), fixture.deadline(), - &status, NULL, NULL); + &status, NULL, NULL, NULL); } grpc_exec_ctx_finish(&exec_ctx); track_counters.Finish(state); @@ -281,7 +281,7 @@ static void BM_ErrorHttpError(benchmark::State& state) { while (state.KeepRunning()) { grpc_http2_error_code error; grpc_error_get_status(&exec_ctx, fixture.error(), fixture.deadline(), NULL, - NULL, &error); + NULL, &error, NULL); } grpc_exec_ctx_finish(&exec_ctx); track_counters.Finish(state); From 344553c27cd8bbc915f0f3a3831438ac05a07387 Mon Sep 17 00:00:00 2001 From: ncteisen Date: Mon, 6 Nov 2017 19:59:39 -0800 Subject: [PATCH 16/70] Add POC example of inheritance for testing --- .../transport/chttp2/transport/chttp2_transport.cc | 3 +++ src/core/ext/transport/chttp2/transport/internal.h | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc index a955ec2589a..8bf5167286e 100644 --- a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc +++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc @@ -274,6 +274,9 @@ static void init_transport(grpc_exec_ctx* exec_ctx, grpc_chttp2_transport* t, GPR_ASSERT(strlen(GRPC_CHTTP2_CLIENT_CONNECT_STRING) == GRPC_CHTTP2_CLIENT_CONNECT_STRLEN); + t->abstract = new D1; + t->abstract->foo(); + t->base.vtable = get_vtable(); t->ep = ep; /* one ref is for destroy */ diff --git a/src/core/ext/transport/chttp2/transport/internal.h b/src/core/ext/transport/chttp2/transport/internal.h index 60cc280c431..dab0bd830e1 100644 --- a/src/core/ext/transport/chttp2/transport/internal.h +++ b/src/core/ext/transport/chttp2/transport/internal.h @@ -237,12 +237,26 @@ typedef enum { GRPC_CHTTP2_KEEPALIVE_STATE_DISABLED, } grpc_chttp2_keepalive_state; +class AbstractBase { + public: + AbstractBase() {} + virtual void foo() { gpr_log(GPR_ERROR, "base"); } +}; + +class D1 : public AbstractBase { + public: + D1() {} + void foo() override { gpr_log(GPR_ERROR, "derived"); } +}; + struct grpc_chttp2_transport { grpc_transport base; /* must be first */ gpr_refcount refs; grpc_endpoint* ep; char* peer_string; + AbstractBase* abstract; + grpc_combiner* combiner; /** write execution state of the transport */ From 0a69e1ef0932a5148098ca6d688696e9fc2fc998 Mon Sep 17 00:00:00 2001 From: ncteisen Date: Mon, 6 Nov 2017 20:07:39 -0800 Subject: [PATCH 17/70] Change manual ctor to be polymorphic --- .../chttp2/transport/chttp2_transport.cc | 2 +- .../ext/transport/chttp2/transport/internal.h | 6 +- src/core/lib/support/manual_constructor.h | 125 ++++++++++++++++++ 3 files changed, 129 insertions(+), 4 deletions(-) diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc index 8bf5167286e..16500ce05c2 100644 --- a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc +++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc @@ -274,7 +274,7 @@ static void init_transport(grpc_exec_ctx* exec_ctx, grpc_chttp2_transport* t, GPR_ASSERT(strlen(GRPC_CHTTP2_CLIENT_CONNECT_STRING) == GRPC_CHTTP2_CLIENT_CONNECT_STRLEN); - t->abstract = new D1; + t->abstract.Init(); t->abstract->foo(); t->base.vtable = get_vtable(); diff --git a/src/core/ext/transport/chttp2/transport/internal.h b/src/core/ext/transport/chttp2/transport/internal.h index dab0bd830e1..f3b10e56ad0 100644 --- a/src/core/ext/transport/chttp2/transport/internal.h +++ b/src/core/ext/transport/chttp2/transport/internal.h @@ -243,9 +243,9 @@ class AbstractBase { virtual void foo() { gpr_log(GPR_ERROR, "base"); } }; -class D1 : public AbstractBase { +class Derived : public AbstractBase { public: - D1() {} + Derived() {} void foo() override { gpr_log(GPR_ERROR, "derived"); } }; @@ -255,7 +255,7 @@ struct grpc_chttp2_transport { grpc_endpoint* ep; char* peer_string; - AbstractBase* abstract; + grpc_core::PolymorphicManualConstructor abstract; grpc_combiner* combiner; diff --git a/src/core/lib/support/manual_constructor.h b/src/core/lib/support/manual_constructor.h index d753cf98a0c..56e8fd18b9c 100644 --- a/src/core/lib/support/manual_constructor.h +++ b/src/core/lib/support/manual_constructor.h @@ -22,12 +22,137 @@ // manually construct a region of memory with some type #include +#include #include #include #include namespace grpc_core { +#define ASSERT(x) \ + if ((x)) \ + ; \ + else \ + abort() + +template +class is_one_of; + +template +class is_one_of { + public: + static constexpr const bool value = true; +}; + +template +class is_one_of { + public: + static constexpr const bool value = is_one_of::value; +}; + +template +class is_one_of { + public: + static constexpr const bool value = false; +}; + +template +class max_size_of; + +template +class max_size_of { + public: + static constexpr const size_t value = sizeof(A); +}; + +template +class max_size_of { + public: + static constexpr const size_t value = sizeof(A) > max_size_of::value + ? sizeof(A) + : max_size_of::value; +}; + +template +class max_align_of; + +template +class max_align_of { + public: + static constexpr const size_t value = alignof(A); +}; + +template +class max_align_of { + public: + static constexpr const size_t value = alignof(A) > max_align_of::value + ? alignof(A) + : max_align_of::value; +}; + +template +class PolymorphicManualConstructor { + public: + // No constructor or destructor because one of the most useful uses of + // this class is as part of a union, and members of a union could not have + // constructors or destructors till C++11. And, anyway, the whole point of + // this class is to bypass constructor and destructor. + + BaseType* get() { return reinterpret_cast(&space_); } + const BaseType* get() const { + return reinterpret_cast(&space_); + } + + BaseType* operator->() { return get(); } + const BaseType* operator->() const { return get(); } + + BaseType& operator*() { return *get(); } + const BaseType& operator*() const { return *get(); } + + template + void Init() { + FinishInit(new (&space_) DerivedType); + } + + // Init() constructs the Type instance using the given arguments + // (which are forwarded to Type's constructor). + // + // Note that Init() with no arguments performs default-initialization, + // not zero-initialization (i.e it behaves the same as "new Type;", not + // "new Type();"), so it will leave non-class types uninitialized. + template + void Init(Ts&&... args) { + FinishInit(new (&space_) DerivedType(std::forward(args)...)); + } + + // Init() that is equivalent to copy and move construction. + // Enables usage like this: + // ManualConstructor> v; + // v.Init({1, 2, 3}); + template + void Init(const DerivedType& x) { + FinishInit(new (&space_) DerivedType(x)); + } + template + void Init(DerivedType&& x) { + FinishInit(new (&space_) DerivedType(std::move(x))); + } + + void Destroy() { get()->~BaseType(); } + + private: + template + void FinishInit(DerivedType* p) { + static_assert(is_one_of::value, + "DerivedType must be one of the predeclared DerivedTypes"); + ASSERT(reinterpret_cast(static_cast(p)) == p); + } + + typename std::aligned_storage< + grpc_core::max_size_of::value, + grpc_core::max_align_of::value>::type space_; +}; + template class ManualConstructor { public: From e66ad3b3cb63f8762d4f6d97677c2df110d7ec6a Mon Sep 17 00:00:00 2001 From: ncteisen Date: Mon, 6 Nov 2017 20:22:48 -0800 Subject: [PATCH 18/70] Add GRPC_ABSTRACT_BASE_CLASS --- build.yaml | 1 + .../ext/transport/chttp2/transport/internal.h | 4 +++ src/core/lib/support/abstract.h | 28 +++++++++++++++++++ 3 files changed, 33 insertions(+) create mode 100644 src/core/lib/support/abstract.h diff --git a/build.yaml b/build.yaml index cd6486ea290..23ce245efb2 100644 --- a/build.yaml +++ b/build.yaml @@ -104,6 +104,7 @@ filegroups: - include/grpc/support/useful.h headers: - src/core/lib/profiling/timers.h + - src/core/lib/support/abstract.h - src/core/lib/support/arena.h - src/core/lib/support/atomic.h - src/core/lib/support/atomic_with_atm.h diff --git a/src/core/ext/transport/chttp2/transport/internal.h b/src/core/ext/transport/chttp2/transport/internal.h index f3b10e56ad0..d883170a231 100644 --- a/src/core/ext/transport/chttp2/transport/internal.h +++ b/src/core/ext/transport/chttp2/transport/internal.h @@ -38,6 +38,7 @@ #include "src/core/lib/iomgr/combiner.h" #include "src/core/lib/iomgr/endpoint.h" #include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/support/abstract.h" #include "src/core/lib/support/manual_constructor.h" #include "src/core/lib/transport/connectivity_state.h" #include "src/core/lib/transport/transport_impl.h" @@ -240,12 +241,15 @@ typedef enum { class AbstractBase { public: AbstractBase() {} + virtual ~AbstractBase() { gpr_log(GPR_ERROR, "base dtor"); } virtual void foo() { gpr_log(GPR_ERROR, "base"); } + GRPC_ABSTRACT_BASE_CLASS }; class Derived : public AbstractBase { public: Derived() {} + virtual ~Derived() { gpr_log(GPR_ERROR, "derived dtor"); } void foo() override { gpr_log(GPR_ERROR, "derived"); } }; diff --git a/src/core/lib/support/abstract.h b/src/core/lib/support/abstract.h new file mode 100644 index 00000000000..e8b8c094588 --- /dev/null +++ b/src/core/lib/support/abstract.h @@ -0,0 +1,28 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SUPPORT_ABSTRACT_H +#define GRPC_CORE_LIB_SUPPORT_ABSTRACT_H + +// This is needed to support abstract base classes in the c core. Since gRPC +// doesn't have a c++ runtime, it will hit a linker error on delete unless +// a we define a virtual operator delete. See this blog for more info: +// https://eli.thegreenplace.net/2015/c-deleting-destructors-and-virtual-operator-delete/ +#define GRPC_ABSTRACT_BASE_CLASS static void operator delete(void *p) { abort(); } + +#endif /* GRPC_CORE_LIB_SUPPORT_ABSTRACT_H */ From 0b37f03991b85a363fe4a47665affa4aa3132b9a Mon Sep 17 00:00:00 2001 From: ncteisen Date: Mon, 6 Nov 2017 20:24:21 -0800 Subject: [PATCH 19/70] Regenerate projects --- gRPC-Core.podspec | 2 ++ grpc.gemspec | 1 + package.xml | 1 + tools/doxygen/Doxyfile.c++.internal | 1 + tools/doxygen/Doxyfile.core.internal | 1 + tools/run_tests/generated/sources_and_headers.json | 2 ++ 6 files changed, 8 insertions(+) diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec index 01b8d659772..3df6d766a20 100644 --- a/gRPC-Core.podspec +++ b/gRPC-Core.podspec @@ -187,6 +187,7 @@ Pod::Spec.new do |s| # To save you from scrolling, this is the last part of the podspec. ss.source_files = 'src/core/lib/profiling/timers.h', + 'src/core/lib/support/abstract.h', 'src/core/lib/support/arena.h', 'src/core/lib/support/atomic.h', 'src/core/lib/support/atomic_with_atm.h', @@ -707,6 +708,7 @@ Pod::Spec.new do |s| 'src/core/plugin_registry/grpc_plugin_registry.cc' ss.private_header_files = 'src/core/lib/profiling/timers.h', + 'src/core/lib/support/abstract.h', 'src/core/lib/support/arena.h', 'src/core/lib/support/atomic.h', 'src/core/lib/support/atomic_with_atm.h', diff --git a/grpc.gemspec b/grpc.gemspec index 2fe2536cc76..4d594cfa0d3 100644 --- a/grpc.gemspec +++ b/grpc.gemspec @@ -84,6 +84,7 @@ Gem::Specification.new do |s| s.files += %w( include/grpc/impl/codegen/sync_posix.h ) s.files += %w( include/grpc/impl/codegen/sync_windows.h ) s.files += %w( src/core/lib/profiling/timers.h ) + s.files += %w( src/core/lib/support/abstract.h ) s.files += %w( src/core/lib/support/arena.h ) s.files += %w( src/core/lib/support/atomic.h ) s.files += %w( src/core/lib/support/atomic_with_atm.h ) diff --git a/package.xml b/package.xml index 9dee62f871c..4bf496f698c 100644 --- a/package.xml +++ b/package.xml @@ -96,6 +96,7 @@ + diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal index c9c64399f75..3c564e203af 100644 --- a/tools/doxygen/Doxyfile.c++.internal +++ b/tools/doxygen/Doxyfile.c++.internal @@ -1026,6 +1026,7 @@ src/core/lib/slice/percent_encoding.h \ src/core/lib/slice/slice_hash_table.h \ src/core/lib/slice/slice_internal.h \ src/core/lib/slice/slice_string_helpers.h \ +src/core/lib/support/abstract.h \ src/core/lib/support/arena.h \ src/core/lib/support/atomic.h \ src/core/lib/support/atomic_with_atm.h \ diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal index b9844f8b89a..29efb3d0634 100644 --- a/tools/doxygen/Doxyfile.core.internal +++ b/tools/doxygen/Doxyfile.core.internal @@ -1268,6 +1268,7 @@ src/core/lib/slice/slice_intern.cc \ src/core/lib/slice/slice_internal.h \ src/core/lib/slice/slice_string_helpers.cc \ src/core/lib/slice/slice_string_helpers.h \ +src/core/lib/support/abstract.h \ src/core/lib/support/alloc.cc \ src/core/lib/support/arena.cc \ src/core/lib/support/arena.h \ diff --git a/tools/run_tests/generated/sources_and_headers.json b/tools/run_tests/generated/sources_and_headers.json index 6cd9a040562..70818558a78 100644 --- a/tools/run_tests/generated/sources_and_headers.json +++ b/tools/run_tests/generated/sources_and_headers.json @@ -7766,6 +7766,7 @@ "include/grpc/support/tls_pthread.h", "include/grpc/support/useful.h", "src/core/lib/profiling/timers.h", + "src/core/lib/support/abstract.h", "src/core/lib/support/arena.h", "src/core/lib/support/atomic.h", "src/core/lib/support/atomic_with_atm.h", @@ -7814,6 +7815,7 @@ "include/grpc/support/tls_pthread.h", "include/grpc/support/useful.h", "src/core/lib/profiling/timers.h", + "src/core/lib/support/abstract.h", "src/core/lib/support/arena.h", "src/core/lib/support/atomic.h", "src/core/lib/support/atomic_with_atm.h", From f9d7c278be36c16fd13c12c6665c90793451e24f Mon Sep 17 00:00:00 2001 From: ncteisen Date: Mon, 6 Nov 2017 20:32:57 -0800 Subject: [PATCH 20/70] Add no-rtti to all of core --- Makefile | 11 +++++++++++ build.yaml | 1 + templates/Makefile.template | 12 +++++++++++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 1e3418e4109..664b5973fb6 100644 --- a/Makefile +++ b/Makefile @@ -328,6 +328,7 @@ ifeq ($(SYSTEM),Darwin) CXXFLAGS += -stdlib=libc++ endif CPPFLAGS += -g -Wall -Wextra -Werror -Wno-long-long -Wno-unused-parameter -DOSATOMIC_USE_INLINED=1 -Ithird_party/abseil-cpp +COREFLAGS += -fno-rtti -fno-exceptions LDFLAGS += -g CPPFLAGS += $(CPPFLAGS_$(CONFIG)) @@ -2570,6 +2571,16 @@ $(OBJDIR)/$(CONFIG)/src/compiler/%.o : src/compiler/%.cc $(Q) mkdir -p `dirname $@` $(Q) $(HOST_CXX) $(HOST_CXXFLAGS) $(HOST_CPPFLAGS) -MMD -MF $(addsuffix .dep, $(basename $@)) -c -o $@ $< +$(OBJDIR)/$(CONFIG)/src/core/%.o : src/core/%.cc + $(E) "[CXX] Compiling $<" + $(Q) mkdir -p `dirname $@` + $(Q) $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(COREFLAGS) -MMD -MF $(addsuffix .dep, $(basename $@)) -c -o $@ $< + +$(OBJDIR)/$(CONFIG)/test/core/%.o : test/core/%.cc + $(E) "[CXX] Compiling $<" + $(Q) mkdir -p `dirname $@` + $(Q) $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(COREFLAGS) -MMD -MF $(addsuffix .dep, $(basename $@)) -c -o $@ $< + $(OBJDIR)/$(CONFIG)/%.o : %.cc $(E) "[CXX] Compiling $<" $(Q) mkdir -p `dirname $@` diff --git a/build.yaml b/build.yaml index 23ce245efb2..483b425e8eb 100644 --- a/build.yaml +++ b/build.yaml @@ -4930,6 +4930,7 @@ defaults: CPPFLAGS: -Ithird_party/boringssl/include -fvisibility=hidden -DOPENSSL_NO_ASM -D_GNU_SOURCE -DWIN32_LEAN_AND_MEAN -D_HAS_EXCEPTIONS=0 -DNOMINMAX global: + COREFLAGS: -fno-rtti -fno-exceptions CPPFLAGS: -g -Wall -Wextra -Werror -Wno-long-long -Wno-unused-parameter -DOSATOMIC_USE_INLINED=1 -Ithird_party/abseil-cpp LDFLAGS: -g diff --git a/templates/Makefile.template b/templates/Makefile.template index 05887879100..3eb838857a9 100644 --- a/templates/Makefile.template +++ b/templates/Makefile.template @@ -215,7 +215,7 @@ ifeq ($(SYSTEM),Darwin) CXXFLAGS += -stdlib=libc++ endif - % for arg in ['CFLAGS', 'CXXFLAGS', 'CPPFLAGS', 'LDFLAGS', 'DEFINES']: + % for arg in ['CFLAGS', 'CXXFLAGS', 'CPPFLAGS', 'COREFLAGS', 'LDFLAGS', 'DEFINES']: % if defaults.get('global', []).get(arg, None) is not None: ${arg} += ${defaults.get('global').get(arg)} % endif @@ -1268,6 +1268,16 @@ $(Q) mkdir -p `dirname $@` $(Q) $(HOST_CXX) $(HOST_CXXFLAGS) $(HOST_CPPFLAGS) -MMD -MF $(addsuffix .dep, $(basename $@)) -c -o $@ $< + $(OBJDIR)/$(CONFIG)/src/core/%.o : src/core/%.cc + $(E) "[CXX] Compiling $<" + $(Q) mkdir -p `dirname $@` + $(Q) $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(COREFLAGS) -MMD -MF $(addsuffix .dep, $(basename $@)) -c -o $@ $< + + $(OBJDIR)/$(CONFIG)/test/core/%.o : test/core/%.cc + $(E) "[CXX] Compiling $<" + $(Q) mkdir -p `dirname $@` + $(Q) $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(COREFLAGS) -MMD -MF $(addsuffix .dep, $(basename $@)) -c -o $@ $< + $(OBJDIR)/$(CONFIG)/%.o : %.cc $(E) "[CXX] Compiling $<" $(Q) mkdir -p `dirname $@` From f2132f1c5def84f3bea405c6f5337d656e6b558d Mon Sep 17 00:00:00 2001 From: ncteisen Date: Tue, 7 Nov 2017 11:14:33 -0800 Subject: [PATCH 21/70] Fix no logging tests --- src/core/ext/transport/chttp2/transport/chttp2_transport.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc index 16500ce05c2..a6a9fea9967 100644 --- a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc +++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc @@ -275,7 +275,6 @@ static void init_transport(grpc_exec_ctx* exec_ctx, grpc_chttp2_transport* t, GRPC_CHTTP2_CLIENT_CONNECT_STRLEN); t->abstract.Init(); - t->abstract->foo(); t->base.vtable = get_vtable(); t->ep = ep; From 215209087eb6f1f432da2015688484532f3f2e7a Mon Sep 17 00:00:00 2001 From: ncteisen Date: Tue, 7 Nov 2017 11:17:41 -0800 Subject: [PATCH 22/70] clang-fmt --- src/core/lib/support/abstract.h | 3 ++- src/core/lib/support/manual_constructor.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/lib/support/abstract.h b/src/core/lib/support/abstract.h index e8b8c094588..ddf472bc37a 100644 --- a/src/core/lib/support/abstract.h +++ b/src/core/lib/support/abstract.h @@ -23,6 +23,7 @@ // doesn't have a c++ runtime, it will hit a linker error on delete unless // a we define a virtual operator delete. See this blog for more info: // https://eli.thegreenplace.net/2015/c-deleting-destructors-and-virtual-operator-delete/ -#define GRPC_ABSTRACT_BASE_CLASS static void operator delete(void *p) { abort(); } +#define GRPC_ABSTRACT_BASE_CLASS \ + static void operator delete(void* p) { abort(); } #endif /* GRPC_CORE_LIB_SUPPORT_ABSTRACT_H */ diff --git a/src/core/lib/support/manual_constructor.h b/src/core/lib/support/manual_constructor.h index 56e8fd18b9c..8160ad56d5d 100644 --- a/src/core/lib/support/manual_constructor.h +++ b/src/core/lib/support/manual_constructor.h @@ -33,7 +33,7 @@ namespace grpc_core { if ((x)) \ ; \ else \ - abort() + abort() template class is_one_of; From 8d224c1c28abfd553b707d77740faec95b2c77ec Mon Sep 17 00:00:00 2001 From: ncteisen Date: Thu, 9 Nov 2017 11:51:58 -0800 Subject: [PATCH 23/70] Address github comments --- src/core/lib/support/manual_constructor.h | 24 ++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/core/lib/support/manual_constructor.h b/src/core/lib/support/manual_constructor.h index 8160ad56d5d..6f7ec83c1e3 100644 --- a/src/core/lib/support/manual_constructor.h +++ b/src/core/lib/support/manual_constructor.h @@ -29,12 +29,12 @@ namespace grpc_core { -#define ASSERT(x) \ - if ((x)) \ - ; \ - else \ - abort() +// this contains templated helpers needed to implement the ManualConstructors +// in this file. +namespace manual_ctor_impl { +// is_one_of returns true it a class, Needle, is present in a variadic list of +// classes, Haystack. template class is_one_of; @@ -56,6 +56,8 @@ class is_one_of { static constexpr const bool value = false; }; +// max_size_of returns sizeof(Type) for the largest type in the variadic list +// of classes, Types. template class max_size_of; @@ -73,6 +75,8 @@ class max_size_of { : max_size_of::value; }; +// max_size_of returns alignof(Type) for the largest type in the variadic list +// of classes, Types. template class max_align_of; @@ -90,6 +94,8 @@ class max_align_of { : max_align_of::value; }; +} // namespace manual_ctor_impl + template class PolymorphicManualConstructor { public: @@ -143,14 +149,14 @@ class PolymorphicManualConstructor { private: template void FinishInit(DerivedType* p) { - static_assert(is_one_of::value, + static_assert(manual_ctor_impl::is_one_of::value, "DerivedType must be one of the predeclared DerivedTypes"); - ASSERT(reinterpret_cast(static_cast(p)) == p); + GPR_ASSERT(reinterpret_cast(static_cast(p)) == p); } typename std::aligned_storage< - grpc_core::max_size_of::value, - grpc_core::max_align_of::value>::type space_; + grpc_core::manual_ctor_impl::max_size_of::value, + grpc_core::manual_ctor_impl::max_align_of::value>::type space_; }; template From 4d9c98b14690c508f0e877439c0a53e61f596733 Mon Sep 17 00:00:00 2001 From: ncteisen Date: Thu, 9 Nov 2017 12:43:15 -0800 Subject: [PATCH 24/70] split POC into real test --- CMakeLists.txt | 29 ++++++ Makefile | 36 +++++++ build.yaml | 10 ++ .../chttp2/transport/chttp2_transport.cc | 2 - .../ext/transport/chttp2/transport/internal.h | 17 ---- src/core/lib/support/manual_constructor.h | 2 + test/core/support/manual_constructor_test.cc | 97 +++++++++++++++++++ .../generated/sources_and_headers.json | 15 +++ tools/run_tests/generated/tests.json | 24 +++++ 9 files changed, 213 insertions(+), 19 deletions(-) create mode 100644 test/core/support/manual_constructor_test.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 4daff95efde..4c632c14ce2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -430,6 +430,7 @@ add_dependencies(buildtests_c gpr_env_test) add_dependencies(buildtests_c gpr_histogram_test) add_dependencies(buildtests_c gpr_host_port_test) add_dependencies(buildtests_c gpr_log_test) +add_dependencies(buildtests_c gpr_manual_constructor_test) add_dependencies(buildtests_c gpr_mpscq_test) add_dependencies(buildtests_c gpr_spinlock_test) add_dependencies(buildtests_c gpr_stack_lockfree_test) @@ -6390,6 +6391,34 @@ target_link_libraries(gpr_log_test endif (gRPC_BUILD_TESTS) if (gRPC_BUILD_TESTS) +add_executable(gpr_manual_constructor_test + test/core/support/manual_constructor_test.cc +) + + +target_include_directories(gpr_manual_constructor_test + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include + PRIVATE ${BORINGSSL_ROOT_DIR}/include + PRIVATE ${PROTOBUF_ROOT_DIR}/src + PRIVATE ${BENCHMARK_ROOT_DIR}/include + PRIVATE ${ZLIB_ROOT_DIR} + PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib + PRIVATE ${CARES_INCLUDE_DIR} + PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/cares/cares + PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags/include + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/third_party/abseil-cpp +) + +target_link_libraries(gpr_manual_constructor_test + ${_gRPC_ALLTARGETS_LIBRARIES} + gpr_test_util + gpr +) + +endif (gRPC_BUILD_TESTS) +if (gRPC_BUILD_TESTS) + add_executable(gpr_mpscq_test test/core/support/mpscq_test.cc ) diff --git a/Makefile b/Makefile index 664b5973fb6..d17afad155c 100644 --- a/Makefile +++ b/Makefile @@ -991,6 +991,7 @@ gpr_env_test: $(BINDIR)/$(CONFIG)/gpr_env_test gpr_histogram_test: $(BINDIR)/$(CONFIG)/gpr_histogram_test gpr_host_port_test: $(BINDIR)/$(CONFIG)/gpr_host_port_test gpr_log_test: $(BINDIR)/$(CONFIG)/gpr_log_test +gpr_manual_constructor_test: $(BINDIR)/$(CONFIG)/gpr_manual_constructor_test gpr_mpscq_test: $(BINDIR)/$(CONFIG)/gpr_mpscq_test gpr_spinlock_test: $(BINDIR)/$(CONFIG)/gpr_spinlock_test gpr_stack_lockfree_test: $(BINDIR)/$(CONFIG)/gpr_stack_lockfree_test @@ -1383,6 +1384,7 @@ buildtests_c: privatelibs_c \ $(BINDIR)/$(CONFIG)/gpr_histogram_test \ $(BINDIR)/$(CONFIG)/gpr_host_port_test \ $(BINDIR)/$(CONFIG)/gpr_log_test \ + $(BINDIR)/$(CONFIG)/gpr_manual_constructor_test \ $(BINDIR)/$(CONFIG)/gpr_mpscq_test \ $(BINDIR)/$(CONFIG)/gpr_spinlock_test \ $(BINDIR)/$(CONFIG)/gpr_stack_lockfree_test \ @@ -1827,6 +1829,8 @@ test_c: buildtests_c $(Q) $(BINDIR)/$(CONFIG)/gpr_host_port_test || ( echo test gpr_host_port_test failed ; exit 1 ) $(E) "[RUN] Testing gpr_log_test" $(Q) $(BINDIR)/$(CONFIG)/gpr_log_test || ( echo test gpr_log_test failed ; exit 1 ) + $(E) "[RUN] Testing gpr_manual_constructor_test" + $(Q) $(BINDIR)/$(CONFIG)/gpr_manual_constructor_test || ( echo test gpr_manual_constructor_test failed ; exit 1 ) $(E) "[RUN] Testing gpr_mpscq_test" $(Q) $(BINDIR)/$(CONFIG)/gpr_mpscq_test || ( echo test gpr_mpscq_test failed ; exit 1 ) $(E) "[RUN] Testing gpr_spinlock_test" @@ -10153,6 +10157,38 @@ endif endif +GPR_MANUAL_CONSTRUCTOR_TEST_SRC = \ + test/core/support/manual_constructor_test.cc \ + +GPR_MANUAL_CONSTRUCTOR_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GPR_MANUAL_CONSTRUCTOR_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/gpr_manual_constructor_test: openssl_dep_error + +else + + + +$(BINDIR)/$(CONFIG)/gpr_manual_constructor_test: $(GPR_MANUAL_CONSTRUCTOR_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GPR_MANUAL_CONSTRUCTOR_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/gpr_manual_constructor_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/support/manual_constructor_test.o: $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + +deps_gpr_manual_constructor_test: $(GPR_MANUAL_CONSTRUCTOR_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GPR_MANUAL_CONSTRUCTOR_TEST_OBJS:.o=.dep) +endif +endif + + GPR_MPSCQ_TEST_SRC = \ test/core/support/mpscq_test.cc \ diff --git a/build.yaml b/build.yaml index 483b425e8eb..d0c2bec1063 100644 --- a/build.yaml +++ b/build.yaml @@ -2215,6 +2215,16 @@ targets: - gpr_test_util - gpr uses_polling: false +- name: gpr_manual_constructor_test + cpu_cost: 3 + build: test + language: c + src: + - test/core/support/manual_constructor_test.cc + deps: + - gpr_test_util + - gpr + uses_polling: false - name: gpr_mpscq_test cpu_cost: 30 build: test diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc index a6a9fea9967..a955ec2589a 100644 --- a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc +++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc @@ -274,8 +274,6 @@ static void init_transport(grpc_exec_ctx* exec_ctx, grpc_chttp2_transport* t, GPR_ASSERT(strlen(GRPC_CHTTP2_CLIENT_CONNECT_STRING) == GRPC_CHTTP2_CLIENT_CONNECT_STRLEN); - t->abstract.Init(); - t->base.vtable = get_vtable(); t->ep = ep; /* one ref is for destroy */ diff --git a/src/core/ext/transport/chttp2/transport/internal.h b/src/core/ext/transport/chttp2/transport/internal.h index d883170a231..5bca566dc2a 100644 --- a/src/core/ext/transport/chttp2/transport/internal.h +++ b/src/core/ext/transport/chttp2/transport/internal.h @@ -38,7 +38,6 @@ #include "src/core/lib/iomgr/combiner.h" #include "src/core/lib/iomgr/endpoint.h" #include "src/core/lib/iomgr/timer.h" -#include "src/core/lib/support/abstract.h" #include "src/core/lib/support/manual_constructor.h" #include "src/core/lib/transport/connectivity_state.h" #include "src/core/lib/transport/transport_impl.h" @@ -238,20 +237,6 @@ typedef enum { GRPC_CHTTP2_KEEPALIVE_STATE_DISABLED, } grpc_chttp2_keepalive_state; -class AbstractBase { - public: - AbstractBase() {} - virtual ~AbstractBase() { gpr_log(GPR_ERROR, "base dtor"); } - virtual void foo() { gpr_log(GPR_ERROR, "base"); } - GRPC_ABSTRACT_BASE_CLASS -}; - -class Derived : public AbstractBase { - public: - Derived() {} - virtual ~Derived() { gpr_log(GPR_ERROR, "derived dtor"); } - void foo() override { gpr_log(GPR_ERROR, "derived"); } -}; struct grpc_chttp2_transport { grpc_transport base; /* must be first */ @@ -259,8 +244,6 @@ struct grpc_chttp2_transport { grpc_endpoint* ep; char* peer_string; - grpc_core::PolymorphicManualConstructor abstract; - grpc_combiner* combiner; /** write execution state of the transport */ diff --git a/src/core/lib/support/manual_constructor.h b/src/core/lib/support/manual_constructor.h index 6f7ec83c1e3..5ad07a6709f 100644 --- a/src/core/lib/support/manual_constructor.h +++ b/src/core/lib/support/manual_constructor.h @@ -27,6 +27,8 @@ #include #include +#include + namespace grpc_core { // this contains templated helpers needed to implement the ManualConstructors diff --git a/test/core/support/manual_constructor_test.cc b/test/core/support/manual_constructor_test.cc new file mode 100644 index 00000000000..13368ee62b3 --- /dev/null +++ b/test/core/support/manual_constructor_test.cc @@ -0,0 +1,97 @@ +/* + * + * Copyright 2017 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. + * + */ + +/* Test of gpr synchronization support. */ + +#include "src/core/lib/support/abstract.h" +#include "src/core/lib/support/manual_constructor.h" +#include +#include +#include +#include +#include +#include +#include +#include "test/core/util/test_config.h" + +class A { + public: + A() {} + virtual ~A() {} + virtual const char* foo() { return "A_foo"; } + virtual const char* bar() { return "A_bar"; } + GRPC_ABSTRACT_BASE_CLASS +}; + +class B : public A { + public: + B() {} + ~B() {} + const char* foo() override { return "B_foo"; } + char get_junk() { return junk[0]; } + private: + char junk[1000]; +}; + +class C : public B { + public: + C() {} + ~C() {} + virtual const char* bar() { return "C_bar"; } + char get_more_junk() { return more_junk[0]; } + private: + char more_junk[1000]; +}; + +class D : public A { + public: + virtual const char* bar() { return "D_bar"; } +}; + +static void basic_test() { + grpc_core::PolymorphicManualConstructor poly; + poly.Init(); + GPR_ASSERT(!strcmp(poly->foo(), "B_foo")); + GPR_ASSERT(!strcmp(poly->bar(), "A_bar")); +} + +static void complex_test() { + grpc_core::PolymorphicManualConstructor polyB; + polyB.Init(); + GPR_ASSERT(!strcmp(polyB->foo(), "B_foo")); + GPR_ASSERT(!strcmp(polyB->bar(), "A_bar")); + + grpc_core::PolymorphicManualConstructor polyC; + polyC.Init(); + GPR_ASSERT(!strcmp(polyC->foo(), "B_foo")); + GPR_ASSERT(!strcmp(polyC->bar(), "C_bar")); + + grpc_core::PolymorphicManualConstructor polyD; + polyD.Init(); + GPR_ASSERT(!strcmp(polyD->foo(), "A_foo")); + GPR_ASSERT(!strcmp(polyD->bar(), "D_bar")); +} + +/* ------------------------------------------------- */ + +int main(int argc, char* argv[]) { + grpc_test_init(argc, argv); + basic_test(); + complex_test(); + return 0; +} diff --git a/tools/run_tests/generated/sources_and_headers.json b/tools/run_tests/generated/sources_and_headers.json index 70818558a78..a1a5020b422 100644 --- a/tools/run_tests/generated/sources_and_headers.json +++ b/tools/run_tests/generated/sources_and_headers.json @@ -748,6 +748,21 @@ "third_party": false, "type": "target" }, + { + "deps": [ + "gpr", + "gpr_test_util" + ], + "headers": [], + "is_filegroup": false, + "language": "c", + "name": "gpr_manual_constructor_test", + "src": [ + "test/core/support/manual_constructor_test.cc" + ], + "third_party": false, + "type": "target" + }, { "deps": [ "gpr", diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json index 5df5a7477b1..70b452453dd 100644 --- a/tools/run_tests/generated/tests.json +++ b/tools/run_tests/generated/tests.json @@ -911,6 +911,30 @@ ], "uses_polling": false }, + { + "args": [], + "benchmark": false, + "ci_platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "cpu_cost": 3, + "exclude_configs": [], + "exclude_iomgrs": [], + "flaky": false, + "gtest": false, + "language": "c", + "name": "gpr_manual_constructor_test", + "platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "uses_polling": false + }, { "args": [], "benchmark": false, From f0798b4873ab72a374c141d84a4c4d5ceec2b638 Mon Sep 17 00:00:00 2001 From: ncteisen Date: Thu, 9 Nov 2017 12:55:34 -0800 Subject: [PATCH 25/70] Fix Bazel build --- BUILD | 1 + test/core/support/BUILD | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/BUILD b/BUILD index 6a514496f62..ba927582c18 100644 --- a/BUILD +++ b/BUILD @@ -480,6 +480,7 @@ grpc_cc_library( ], hdrs = [ "src/core/lib/profiling/timers.h", + "src/core/lib/support/abstract.h", "src/core/lib/support/arena.h", "src/core/lib/support/atomic.h", "src/core/lib/support/atomic_with_atm.h", diff --git a/test/core/support/BUILD b/test/core/support/BUILD index 9d042fdc9f4..69512cd9a92 100644 --- a/test/core/support/BUILD +++ b/test/core/support/BUILD @@ -138,6 +138,16 @@ grpc_cc_test( ], ) +grpc_cc_test( + name = "manual_constructor_test", + srcs = ["manual_constructor_test.cc"], + language = "C++", + deps = [ + "//:gpr", + "//test/core/util:gpr_test_util", + ], +) + grpc_cc_test( name = "spinlock_test", srcs = ["spinlock_test.cc"], From 684f15b210fe4442b714dc93053f05a75431629e Mon Sep 17 00:00:00 2001 From: ncteisen Date: Fri, 10 Nov 2017 16:23:44 -0800 Subject: [PATCH 26/70] clang fmt --- src/core/ext/transport/chttp2/transport/internal.h | 1 - src/core/lib/support/manual_constructor.h | 8 +++++--- test/core/support/manual_constructor_test.cc | 4 +++- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/core/ext/transport/chttp2/transport/internal.h b/src/core/ext/transport/chttp2/transport/internal.h index 5bca566dc2a..60cc280c431 100644 --- a/src/core/ext/transport/chttp2/transport/internal.h +++ b/src/core/ext/transport/chttp2/transport/internal.h @@ -237,7 +237,6 @@ typedef enum { GRPC_CHTTP2_KEEPALIVE_STATE_DISABLED, } grpc_chttp2_keepalive_state; - struct grpc_chttp2_transport { grpc_transport base; /* must be first */ gpr_refcount refs; diff --git a/src/core/lib/support/manual_constructor.h b/src/core/lib/support/manual_constructor.h index 5ad07a6709f..25731936b1c 100644 --- a/src/core/lib/support/manual_constructor.h +++ b/src/core/lib/support/manual_constructor.h @@ -151,14 +151,16 @@ class PolymorphicManualConstructor { private: template void FinishInit(DerivedType* p) { - static_assert(manual_ctor_impl::is_one_of::value, - "DerivedType must be one of the predeclared DerivedTypes"); + static_assert( + manual_ctor_impl::is_one_of::value, + "DerivedType must be one of the predeclared DerivedTypes"); GPR_ASSERT(reinterpret_cast(static_cast(p)) == p); } typename std::aligned_storage< grpc_core::manual_ctor_impl::max_size_of::value, - grpc_core::manual_ctor_impl::max_align_of::value>::type space_; + grpc_core::manual_ctor_impl::max_align_of::value>::type + space_; }; template diff --git a/test/core/support/manual_constructor_test.cc b/test/core/support/manual_constructor_test.cc index 13368ee62b3..714f8b21a01 100644 --- a/test/core/support/manual_constructor_test.cc +++ b/test/core/support/manual_constructor_test.cc @@ -18,7 +18,6 @@ /* Test of gpr synchronization support. */ -#include "src/core/lib/support/abstract.h" #include "src/core/lib/support/manual_constructor.h" #include #include @@ -27,6 +26,7 @@ #include #include #include +#include "src/core/lib/support/abstract.h" #include "test/core/util/test_config.h" class A { @@ -44,6 +44,7 @@ class B : public A { ~B() {} const char* foo() override { return "B_foo"; } char get_junk() { return junk[0]; } + private: char junk[1000]; }; @@ -54,6 +55,7 @@ class C : public B { ~C() {} virtual const char* bar() { return "C_bar"; } char get_more_junk() { return more_junk[0]; } + private: char more_junk[1000]; }; From 30321c96d3deb9f0736648e85b8f6774082badaa Mon Sep 17 00:00:00 2001 From: josh Date: Mon, 13 Nov 2017 22:02:38 -0700 Subject: [PATCH 27/70] Update the git hash of the Google Benchmark dependency in the Bazel WORKSPACE --- WORKSPACE | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WORKSPACE b/WORKSPACE index 6ce7b9de140..6df24e9c2ff 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -93,8 +93,8 @@ http_archive( new_http_archive( name = "com_github_google_benchmark", build_file = "third_party/benchmark.BUILD", - strip_prefix = "benchmark-44c25c892a6229b20db7cd9dc05584ea865896de", - url = "https://github.com/google/benchmark/archive/44c25c892a6229b20db7cd9dc05584ea865896de.tar.gz", + strip_prefix = "benchmark-5b7683f49e1e9223cf9927b24f6fd3d6bd82e3f8", + url = "https://github.com/google/benchmark/archive/5b7683f49e1e9223cf9927b24f6fd3d6bd82e3f8.tar.gz", ) new_local_repository( From 9b3b8e32ec5d6f69aa8c4faeb8f58e5450580289 Mon Sep 17 00:00:00 2001 From: ncteisen Date: Tue, 14 Nov 2017 11:31:19 -0800 Subject: [PATCH 28/70] clang fmt --- src/core/lib/iomgr/tcp_posix.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/lib/iomgr/tcp_posix.cc b/src/core/lib/iomgr/tcp_posix.cc index 6a0e1fc5feb..6b61ed9ec30 100644 --- a/src/core/lib/iomgr/tcp_posix.cc +++ b/src/core/lib/iomgr/tcp_posix.cc @@ -606,7 +606,8 @@ static bool tcp_flush(grpc_exec_ctx* exec_ctx, grpc_tcp* tcp, if (tcp->outgoing_slice_idx == tcp->outgoing_buffer->count) { *error = GRPC_ERROR_NONE; - grpc_slice_buffer_reset_and_unref_internal(exec_ctx, tcp->outgoing_buffer); + grpc_slice_buffer_reset_and_unref_internal(exec_ctx, + tcp->outgoing_buffer); return true; } } From 751c3245f8807c8f2468b4d8ca4d7da8d7607221 Mon Sep 17 00:00:00 2001 From: ncteisen Date: Tue, 14 Nov 2017 12:31:24 -0800 Subject: [PATCH 29/70] Add partial unref --- include/grpc/impl/codegen/slice.h | 4 ++++ src/core/lib/iomgr/tcp_posix.cc | 2 ++ src/core/lib/slice/slice_buffer.cc | 17 ++++++++++++++++- src/core/lib/slice/slice_internal.h | 3 +++ 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/include/grpc/impl/codegen/slice.h b/include/grpc/impl/codegen/slice.h index 11997fcb563..369cee91a8e 100644 --- a/include/grpc/impl/codegen/slice.h +++ b/include/grpc/impl/codegen/slice.h @@ -108,6 +108,10 @@ typedef struct { /** the number of slices allocated in the array. External users (i.e any code * outside grpc core) MUST NOT use this field */ size_t capacity; + /** the index of the first slice who's memory is still owned by this buffer. + * This is only to be used when partially unreffing this slice buffer in + * grpc_slice_buffer_partial_reset_and_unref_internal. */ + size_t idx_of_first_valid_slice; /** the combined length of all slices in the array */ size_t length; /** inlined elements to avoid allocations */ diff --git a/src/core/lib/iomgr/tcp_posix.cc b/src/core/lib/iomgr/tcp_posix.cc index 6b61ed9ec30..016ca87219b 100644 --- a/src/core/lib/iomgr/tcp_posix.cc +++ b/src/core/lib/iomgr/tcp_posix.cc @@ -576,6 +576,8 @@ static bool tcp_flush(grpc_exec_ctx* exec_ctx, grpc_tcp* tcp, if (errno == EAGAIN) { tcp->outgoing_slice_idx = unwind_slice_idx; tcp->outgoing_byte_idx = unwind_byte_idx; + grpc_slice_buffer_partial_reset_and_unref_internal( + exec_ctx, tcp->outgoing_buffer, unwind_slice_idx); return false; } else if (errno == EPIPE) { *error = grpc_error_set_int(GRPC_OS_ERROR(errno, "sendmsg"), diff --git a/src/core/lib/slice/slice_buffer.cc b/src/core/lib/slice/slice_buffer.cc index 5db54dad91e..cd5040fbcb5 100644 --- a/src/core/lib/slice/slice_buffer.cc +++ b/src/core/lib/slice/slice_buffer.cc @@ -62,6 +62,7 @@ void grpc_slice_buffer_init(grpc_slice_buffer* sb) { sb->count = 0; sb->length = 0; sb->capacity = GRPC_SLICE_BUFFER_INLINE_ELEMENTS; + sb->idx_of_first_valid_slice = 0; sb->base_slices = sb->slices = sb->inlined; } @@ -166,12 +167,26 @@ void grpc_slice_buffer_pop(grpc_slice_buffer* sb) { void grpc_slice_buffer_reset_and_unref_internal(grpc_exec_ctx* exec_ctx, grpc_slice_buffer* sb) { size_t i; - for (i = 0; i < sb->count; i++) { + for (i = sb->idx_of_first_valid_slice; i < sb->count; i++) { grpc_slice_unref_internal(exec_ctx, sb->slices[i]); } sb->count = 0; sb->length = 0; + sb->idx_of_first_valid_slice = 0; +} + +void grpc_slice_buffer_partial_reset_and_unref_internal(grpc_exec_ctx* exec_ctx, + grpc_slice_buffer* sb, + size_t idx) { + GPR_ASSERT(idx <= sb->count); + + size_t i; + for (i = sb->idx_of_first_valid_slice; i < idx; i++) { + grpc_slice_unref_internal(exec_ctx, sb->slices[i]); + } + + sb->idx_of_first_valid_slice = idx; } void grpc_slice_buffer_reset_and_unref(grpc_slice_buffer* sb) { diff --git a/src/core/lib/slice/slice_internal.h b/src/core/lib/slice/slice_internal.h index 2439fc08267..2c616950d7f 100644 --- a/src/core/lib/slice/slice_internal.h +++ b/src/core/lib/slice/slice_internal.h @@ -32,6 +32,9 @@ grpc_slice grpc_slice_ref_internal(grpc_slice slice); void grpc_slice_unref_internal(grpc_exec_ctx* exec_ctx, grpc_slice slice); void grpc_slice_buffer_reset_and_unref_internal(grpc_exec_ctx* exec_ctx, grpc_slice_buffer* sb); +void grpc_slice_buffer_partial_reset_and_unref_internal(grpc_exec_ctx* exec_ctx, + grpc_slice_buffer* sb, + size_t idx); void grpc_slice_buffer_destroy_internal(grpc_exec_ctx* exec_ctx, grpc_slice_buffer* sb); From 0d12c0ceabe7e91538451beb5a0363655148b404 Mon Sep 17 00:00:00 2001 From: kwasimensah Date: Tue, 14 Nov 2017 15:53:45 -0500 Subject: [PATCH 30/70] Update to not depend on c++ runtime --- src/core/lib/support/cpu_posix.cc | 40 +++++++++++++++---------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/core/lib/support/cpu_posix.cc b/src/core/lib/support/cpu_posix.cc index 0d57d46131d..832818aac56 100644 --- a/src/core/lib/support/cpu_posix.cc +++ b/src/core/lib/support/cpu_posix.cc @@ -25,8 +25,6 @@ #include #include -#include - #include #include #include @@ -35,6 +33,8 @@ static long ncpus = 0; +static pthread_key_t thread_id_key; + static void init_ncpus() { ncpus = sysconf(_SC_NPROCESSORS_ONLN); if (ncpus < 1 || ncpus > INT32_MAX) { @@ -49,33 +49,33 @@ unsigned gpr_cpu_num_cores(void) { return (unsigned)ncpus; } +static void delete_thread_id_key(void *value) { + if (value) { + gpr_free(value); + } + pthread_key_delete(thread_id_key); +} + +static void init_thread_id_key(void) { + pthread_key_create(&thread_id_key, delete_thread_id_key); +} + unsigned gpr_cpu_current_cpu(void) { /* NOTE: there's no way I know to return the actual cpu index portably... most code that's using this is using it to shard across work queues though, so here we use thread identity instead to achieve a similar though not identical effect */ - static auto DeleteValue = [](void* value_ptr) { - unsigned int* value = static_cast(value_ptr); - if (value) { - gpr_free(value); - } - }; - static pthread_key_t thread_id_key; - static int thread_id_key_create_result __attribute__((unused)) = - pthread_key_create(&thread_id_key, DeleteValue); - // pthread_t isn't portably defined to map to an integral type. So keep track - // of thread identity explicitly so hashing works reliably. - static std::atomic thread_counter(0); - - unsigned int* thread_id = - static_cast(pthread_getspecific(thread_id_key)); + static gpr_once once = GPR_ONCE_INIT; + gpr_once_init(&once, init_thread_id_key); + + unsigned int *thread_id = + static_cast(pthread_getspecific(thread_id_key)); if (thread_id == nullptr) { - thread_id = static_cast(gpr_malloc(sizeof(unsigned int))); - *thread_id = thread_counter++; + thread_id = static_cast(gpr_malloc(sizeof(unsigned int))); pthread_setspecific(thread_id_key, thread_id); } - return (unsigned)GPR_HASH_POINTER(*thread_id, gpr_cpu_num_cores()); + return (unsigned)GPR_HASH_POINTER(thread_id, gpr_cpu_num_cores()); } #endif /* GPR_CPU_POSIX */ From 589c940195f82738a95ff1f78a4f4d6a43566a41 Mon Sep 17 00:00:00 2001 From: ncteisen Date: Tue, 14 Nov 2017 18:22:54 -0800 Subject: [PATCH 31/70] add test --- src/core/lib/iomgr/tcp_posix.cc | 4 +-- src/core/lib/slice/slice_buffer.cc | 9 ++++--- src/core/lib/slice/slice_internal.h | 6 ++--- test/core/slice/slice_buffer_test.cc | 39 ++++++++++++++++++++++++++++ 4 files changed, 49 insertions(+), 9 deletions(-) diff --git a/src/core/lib/iomgr/tcp_posix.cc b/src/core/lib/iomgr/tcp_posix.cc index 016ca87219b..0864cd594d2 100644 --- a/src/core/lib/iomgr/tcp_posix.cc +++ b/src/core/lib/iomgr/tcp_posix.cc @@ -576,8 +576,8 @@ static bool tcp_flush(grpc_exec_ctx* exec_ctx, grpc_tcp* tcp, if (errno == EAGAIN) { tcp->outgoing_slice_idx = unwind_slice_idx; tcp->outgoing_byte_idx = unwind_byte_idx; - grpc_slice_buffer_partial_reset_and_unref_internal( - exec_ctx, tcp->outgoing_buffer, unwind_slice_idx); + grpc_slice_buffer_partial_unref_internal(exec_ctx, tcp->outgoing_buffer, + unwind_slice_idx); return false; } else if (errno == EPIPE) { *error = grpc_error_set_int(GRPC_OS_ERROR(errno, "sendmsg"), diff --git a/src/core/lib/slice/slice_buffer.cc b/src/core/lib/slice/slice_buffer.cc index cd5040fbcb5..8fdd8cf2257 100644 --- a/src/core/lib/slice/slice_buffer.cc +++ b/src/core/lib/slice/slice_buffer.cc @@ -176,10 +176,11 @@ void grpc_slice_buffer_reset_and_unref_internal(grpc_exec_ctx* exec_ctx, sb->idx_of_first_valid_slice = 0; } -void grpc_slice_buffer_partial_reset_and_unref_internal(grpc_exec_ctx* exec_ctx, - grpc_slice_buffer* sb, - size_t idx) { - GPR_ASSERT(idx <= sb->count); +void grpc_slice_buffer_partial_unref_internal(grpc_exec_ctx* exec_ctx, + grpc_slice_buffer* sb, + size_t idx) { + GPR_ASSERT(idx < sb->count); // if idx == count, then partial is not needed + GPR_ASSERT(sb->idx_of_first_valid_slice <= idx); size_t i; for (i = sb->idx_of_first_valid_slice; i < idx; i++) { diff --git a/src/core/lib/slice/slice_internal.h b/src/core/lib/slice/slice_internal.h index 2c616950d7f..10527dcdeb5 100644 --- a/src/core/lib/slice/slice_internal.h +++ b/src/core/lib/slice/slice_internal.h @@ -32,9 +32,9 @@ grpc_slice grpc_slice_ref_internal(grpc_slice slice); void grpc_slice_unref_internal(grpc_exec_ctx* exec_ctx, grpc_slice slice); void grpc_slice_buffer_reset_and_unref_internal(grpc_exec_ctx* exec_ctx, grpc_slice_buffer* sb); -void grpc_slice_buffer_partial_reset_and_unref_internal(grpc_exec_ctx* exec_ctx, - grpc_slice_buffer* sb, - size_t idx); +void grpc_slice_buffer_partial_unref_internal(grpc_exec_ctx* exec_ctx, + grpc_slice_buffer* sb, + size_t idx); void grpc_slice_buffer_destroy_internal(grpc_exec_ctx* exec_ctx, grpc_slice_buffer* sb); diff --git a/test/core/slice/slice_buffer_test.cc b/test/core/slice/slice_buffer_test.cc index 338e8079dc8..696251536f5 100644 --- a/test/core/slice/slice_buffer_test.cc +++ b/test/core/slice/slice_buffer_test.cc @@ -18,6 +18,7 @@ #include #include +#include "src/core/lib/slice/slice_internal.h" #include "test/core/util/test_config.h" void test_slice_buffer_add() { @@ -104,11 +105,49 @@ void test_slice_buffer_move_first() { GPR_ASSERT(dst.length == dst_len); } +static void populate_slice_buffer(grpc_slice_buffer* sb) { + grpc_slice slices[4]; + int idx = 0; + + slices[0] = grpc_slice_from_copied_string("aaa"); + slices[1] = grpc_slice_from_copied_string("bbbb"); + slices[2] = grpc_slice_from_copied_string("ccc"); + slices[3] = grpc_slice_from_copied_string("ddddd"); + + for (idx = 0; idx < 4; idx++) { + grpc_slice_ref(slices[idx]); + grpc_slice_buffer_add_indexed(sb, slices[idx]); + } +} + +void test_slice_buffer_unref() { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_slice_buffer sb; + + grpc_slice_buffer_init(&sb); + + // regular init and unref + populate_slice_buffer(&sb); + grpc_slice_buffer_reset_and_unref_internal(&exec_ctx, &sb); + + // inits, then unrefs partial, then unrefs the rest + populate_slice_buffer(&sb); + grpc_slice_buffer_partial_unref_internal(&exec_ctx, &sb, 1); + grpc_slice_buffer_reset_and_unref_internal(&exec_ctx, &sb); + + // two partial unrefs + populate_slice_buffer(&sb); + grpc_slice_buffer_partial_unref_internal(&exec_ctx, &sb, 1); + grpc_slice_buffer_partial_unref_internal(&exec_ctx, &sb, 3); + grpc_slice_buffer_reset_and_unref_internal(&exec_ctx, &sb); +} + int main(int argc, char** argv) { grpc_test_init(argc, argv); test_slice_buffer_add(); test_slice_buffer_move_first(); + test_slice_buffer_unref(); return 0; } From 254cb369a6b65023ccb9cdb16200a3043dab0259 Mon Sep 17 00:00:00 2001 From: Nathaniel Manista Date: Wed, 15 Nov 2017 20:32:00 +0000 Subject: [PATCH 32/70] Require port and server_port interop flags Also sort arguments to ArgumentParser.add_argument in idiomatic order. --- .../grpcio_tests/tests/interop/client.py | 31 ++++++++++--------- .../grpcio_tests/tests/interop/server.py | 7 +++-- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/python/grpcio_tests/tests/interop/client.py b/src/python/grpcio_tests/tests/interop/client.py index e520c082903..383b5f033d7 100644 --- a/src/python/grpcio_tests/tests/interop/client.py +++ b/src/python/grpcio_tests/tests/interop/client.py @@ -29,37 +29,40 @@ def _args(): parser = argparse.ArgumentParser() parser.add_argument( '--server_host', - help='the host to which to connect', + default="localhost", type=str, - default="localhost") + help='the host to which to connect') parser.add_argument( - '--server_port', help='the port to which to connect', type=int) + '--server_port', + type=int, + required=True, + help='the port to which to connect') parser.add_argument( '--test_case', - help='the test case to execute', + default='large_unary', type=str, - default="large_unary") + help='the test case to execute') parser.add_argument( '--use_tls', - help='require a secure connection', default=False, - type=resources.parse_bool) + type=resources.parse_bool, + help='require a secure connection') parser.add_argument( '--use_test_ca', - help='replace platform root CAs with ca.pem', default=False, - type=resources.parse_bool) + type=resources.parse_bool, + help='replace platform root CAs with ca.pem') parser.add_argument( '--server_host_override', default="foo.test.google.fr", - help='the server host to which to claim to connect', - type=str) + type=str, + help='the server host to which to claim to connect') parser.add_argument( - '--oauth_scope', help='scope for OAuth tokens', type=str) + '--oauth_scope', type=str, help='scope for OAuth tokens') parser.add_argument( '--default_service_account', - help='email address of the default service account', - type=str) + type=str, + help='email address of the default service account') return parser.parse_args() diff --git a/src/python/grpcio_tests/tests/interop/server.py b/src/python/grpcio_tests/tests/interop/server.py index 8ad1f5f7cd5..eeb41a21d23 100644 --- a/src/python/grpcio_tests/tests/interop/server.py +++ b/src/python/grpcio_tests/tests/interop/server.py @@ -29,12 +29,13 @@ _ONE_DAY_IN_SECONDS = 60 * 60 * 24 def serve(): parser = argparse.ArgumentParser() - parser.add_argument('--port', help='the port on which to serve', type=int) + parser.add_argument( + '--port', type=int, required=True, help='the port on which to serve') parser.add_argument( '--use_tls', - help='require a secure connection', default=False, - type=resources.parse_bool) + type=resources.parse_bool, + help='require a secure connection') args = parser.parse_args() server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) From 901c47c96bb53dc1cad2d2572d3c17a300f9769d Mon Sep 17 00:00:00 2001 From: ncteisen Date: Wed, 15 Nov 2017 15:21:23 -0800 Subject: [PATCH 33/70] Fix artifact builds --- src/core/ext/transport/chttp2/transport/flow_control.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/ext/transport/chttp2/transport/flow_control.h b/src/core/ext/transport/chttp2/transport/flow_control.h index 7dd348ed5f1..c0dd76bdf73 100644 --- a/src/core/ext/transport/chttp2/transport/flow_control.h +++ b/src/core/ext/transport/chttp2/transport/flow_control.h @@ -19,6 +19,7 @@ #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FLOW_CONTROL_H #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FLOW_CONTROL_H +#include #include #include From 8c1f338b4ab55455d064920526127495bab96e6c Mon Sep 17 00:00:00 2001 From: ncteisen Date: Wed, 15 Nov 2017 17:00:24 -0800 Subject: [PATCH 34/70] Add comment --- include/grpc/impl/codegen/grpc_types.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/grpc/impl/codegen/grpc_types.h b/include/grpc/impl/codegen/grpc_types.h index 83b35b1dc47..c8c1437c915 100644 --- a/include/grpc/impl/codegen/grpc_types.h +++ b/include/grpc/impl/codegen/grpc_types.h @@ -558,6 +558,9 @@ typedef struct grpc_op { grpc_metadata_array* trailing_metadata; grpc_status_code* status; grpc_slice* status_details; + /** If this is not nullptr, it will be populated with the full fidelity + * error string for debugging purposes. The application is responsible + * for freeing the data. */ const char** error_string; } recv_status_on_client; struct grpc_op_recv_close_on_server { From d21b96cbc75e7f9d6684907585a490b61325b63f Mon Sep 17 00:00:00 2001 From: ncteisen Date: Wed, 15 Nov 2017 17:05:47 -0800 Subject: [PATCH 35/70] Dup the memory --- src/core/lib/transport/error_utils.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/lib/transport/error_utils.cc b/src/core/lib/transport/error_utils.cc index 061445d44c3..bcaedeeac96 100644 --- a/src/core/lib/transport/error_utils.cc +++ b/src/core/lib/transport/error_utils.cc @@ -18,6 +18,7 @@ #include "src/core/lib/transport/error_utils.h" +#include #include "src/core/lib/iomgr/error_internal.h" #include "src/core/lib/transport/status_conversion.h" @@ -44,7 +45,7 @@ void grpc_error_get_status(grpc_exec_ctx* exec_ctx, grpc_error* error, grpc_slice* slice, grpc_http2_error_code* http_error, const char** full_error_details) { if (full_error_details != NULL) { - *full_error_details = grpc_error_string(error); + *full_error_details = gpr_strdup(grpc_error_string(error)); } // Start with the parent error and recurse through the tree of children From 0354c22d402647b5ba532038511a4849c53b2b0b Mon Sep 17 00:00:00 2001 From: ncteisen Date: Wed, 15 Nov 2017 18:22:11 -0800 Subject: [PATCH 36/70] Save C++ for follow up PR --- include/grpc++/impl/codegen/call.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/grpc++/impl/codegen/call.h b/include/grpc++/impl/codegen/call.h index 91f45b31472..1a988297dc4 100644 --- a/include/grpc++/impl/codegen/call.h +++ b/include/grpc++/impl/codegen/call.h @@ -579,7 +579,6 @@ class CallOpClientRecvStatus { op->data.recv_status_on_client.trailing_metadata = metadata_map_->arr(); op->data.recv_status_on_client.status = &status_code_; op->data.recv_status_on_client.status_details = &error_message_; - op->data.recv_status_on_client.error_string = &error_string_; op->flags = 0; op->reserved = NULL; } @@ -606,7 +605,6 @@ class CallOpClientRecvStatus { Status* recv_status_; grpc_status_code status_code_; grpc_slice error_message_; - const char* error_string_; }; /// An abstract collection of call ops, used to generate the From 3039b37a0ecf68d7b99fa84df21bb6691a174289 Mon Sep 17 00:00:00 2001 From: ncteisen Date: Wed, 15 Nov 2017 18:24:05 -0800 Subject: [PATCH 37/70] Fix bad merge --- test/cpp/microbenchmarks/bm_error.cc | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/test/cpp/microbenchmarks/bm_error.cc b/test/cpp/microbenchmarks/bm_error.cc index 66f9fe37047..bbd8b3c339f 100644 --- a/test/cpp/microbenchmarks/bm_error.cc +++ b/test/cpp/microbenchmarks/bm_error.cc @@ -251,11 +251,7 @@ static void BM_ErrorGetStatus(benchmark::State& state) { grpc_status_code status; grpc_slice slice; grpc_error_get_status(&exec_ctx, fixture.error(), fixture.deadline(), -<<<<<<< HEAD - &status, &slice, NULL, NULL); -======= - &status, &slice, nullptr); ->>>>>>> ff2edb3eba20b30f232ded7769cab8cd20ff5009 + &status, &slice, nullptr, nullptr); } grpc_exec_ctx_finish(&exec_ctx); track_counters.Finish(state); @@ -269,11 +265,7 @@ static void BM_ErrorGetStatusCode(benchmark::State& state) { while (state.KeepRunning()) { grpc_status_code status; grpc_error_get_status(&exec_ctx, fixture.error(), fixture.deadline(), -<<<<<<< HEAD - &status, NULL, NULL, NULL); -======= - &status, nullptr, nullptr); ->>>>>>> ff2edb3eba20b30f232ded7769cab8cd20ff5009 + &status, nullptr, nullptr, nullptr); } grpc_exec_ctx_finish(&exec_ctx); track_counters.Finish(state); @@ -286,13 +278,8 @@ static void BM_ErrorHttpError(benchmark::State& state) { grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; while (state.KeepRunning()) { grpc_http2_error_code error; -<<<<<<< HEAD - grpc_error_get_status(&exec_ctx, fixture.error(), fixture.deadline(), NULL, - NULL, &error, NULL); -======= grpc_error_get_status(&exec_ctx, fixture.error(), fixture.deadline(), - nullptr, nullptr, &error); ->>>>>>> ff2edb3eba20b30f232ded7769cab8cd20ff5009 + nullptr, nullptr, &error, nullptr); } grpc_exec_ctx_finish(&exec_ctx); track_counters.Finish(state); From 14c60f1d12dd9b4ac67ab8addae0c27f5971e55a Mon Sep 17 00:00:00 2001 From: ncteisen Date: Wed, 15 Nov 2017 20:26:50 -0800 Subject: [PATCH 38/70] Fix segfault --- include/grpc++/impl/codegen/call.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/grpc++/impl/codegen/call.h b/include/grpc++/impl/codegen/call.h index 1a988297dc4..41e95866cf7 100644 --- a/include/grpc++/impl/codegen/call.h +++ b/include/grpc++/impl/codegen/call.h @@ -579,6 +579,7 @@ class CallOpClientRecvStatus { op->data.recv_status_on_client.trailing_metadata = metadata_map_->arr(); op->data.recv_status_on_client.status = &status_code_; op->data.recv_status_on_client.status_details = &error_message_; + op->data.recv_status_on_client.error_string = nullptr; op->flags = 0; op->reserved = NULL; } From 9e2be4998e453ef66a595b230519a006ca257529 Mon Sep 17 00:00:00 2001 From: ncteisen Date: Wed, 15 Nov 2017 23:52:55 -0800 Subject: [PATCH 39/70] Only alloc if status <> ok --- src/core/lib/transport/error_utils.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/lib/transport/error_utils.cc b/src/core/lib/transport/error_utils.cc index bcaedeeac96..723663a6d78 100644 --- a/src/core/lib/transport/error_utils.cc +++ b/src/core/lib/transport/error_utils.cc @@ -44,10 +44,6 @@ void grpc_error_get_status(grpc_exec_ctx* exec_ctx, grpc_error* error, grpc_millis deadline, grpc_status_code* code, grpc_slice* slice, grpc_http2_error_code* http_error, const char** full_error_details) { - if (full_error_details != NULL) { - *full_error_details = gpr_strdup(grpc_error_string(error)); - } - // Start with the parent error and recurse through the tree of children // until we find the first one that has a status code. grpc_error* found_error = @@ -74,6 +70,10 @@ void grpc_error_get_status(grpc_exec_ctx* exec_ctx, grpc_error* error, } if (code != nullptr) *code = status; + if (full_error_details != NULL && status != GRPC_STATUS_OK) { + *full_error_details = gpr_strdup(grpc_error_string(error)); + } + if (http_error != nullptr) { if (grpc_error_get_int(found_error, GRPC_ERROR_INT_HTTP2_ERROR, &integer)) { *http_error = (grpc_http2_error_code)integer; From 0bf12cfd32f72be119a4c64433f8de13b9354847 Mon Sep 17 00:00:00 2001 From: kwasimensah Date: Thu, 16 Nov 2017 15:07:00 -0500 Subject: [PATCH 40/70] Fix clang-format issues --- src/core/lib/support/cpu_posix.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/lib/support/cpu_posix.cc b/src/core/lib/support/cpu_posix.cc index 832818aac56..c9dbc0d5d90 100644 --- a/src/core/lib/support/cpu_posix.cc +++ b/src/core/lib/support/cpu_posix.cc @@ -49,7 +49,7 @@ unsigned gpr_cpu_num_cores(void) { return (unsigned)ncpus; } -static void delete_thread_id_key(void *value) { +static void delete_thread_id_key(void* value) { if (value) { gpr_free(value); } @@ -68,10 +68,10 @@ unsigned gpr_cpu_current_cpu(void) { static gpr_once once = GPR_ONCE_INIT; gpr_once_init(&once, init_thread_id_key); - unsigned int *thread_id = - static_cast(pthread_getspecific(thread_id_key)); + unsigned int* thread_id = + static_cast(pthread_getspecific(thread_id_key)); if (thread_id == nullptr) { - thread_id = static_cast(gpr_malloc(sizeof(unsigned int))); + thread_id = static_cast(gpr_malloc(sizeof(unsigned int))); pthread_setspecific(thread_id_key, thread_id); } From 311fa5f8188c4a1116a38991a377030708833336 Mon Sep 17 00:00:00 2001 From: ncteisen Date: Thu, 16 Nov 2017 14:25:50 -0800 Subject: [PATCH 41/70] Reviewer feedback: no API change --- include/grpc/impl/codegen/slice.h | 4 --- src/core/lib/iomgr/tcp_posix.cc | 39 +++++++++++++++++------------- src/core/lib/slice/slice_buffer.cc | 18 +------------- 3 files changed, 23 insertions(+), 38 deletions(-) diff --git a/include/grpc/impl/codegen/slice.h b/include/grpc/impl/codegen/slice.h index 369cee91a8e..11997fcb563 100644 --- a/include/grpc/impl/codegen/slice.h +++ b/include/grpc/impl/codegen/slice.h @@ -108,10 +108,6 @@ typedef struct { /** the number of slices allocated in the array. External users (i.e any code * outside grpc core) MUST NOT use this field */ size_t capacity; - /** the index of the first slice who's memory is still owned by this buffer. - * This is only to be used when partially unreffing this slice buffer in - * grpc_slice_buffer_partial_reset_and_unref_internal. */ - size_t idx_of_first_valid_slice; /** the combined length of all slices in the array */ size_t length; /** inlined elements to avoid allocations */ diff --git a/src/core/lib/iomgr/tcp_posix.cc b/src/core/lib/iomgr/tcp_posix.cc index 0864cd594d2..3523c689730 100644 --- a/src/core/lib/iomgr/tcp_posix.cc +++ b/src/core/lib/iomgr/tcp_posix.cc @@ -81,9 +81,7 @@ typedef struct { grpc_slice_buffer* incoming_buffer; grpc_slice_buffer* outgoing_buffer; - /** slice within outgoing_buffer to write next */ - size_t outgoing_slice_idx; - /** byte within outgoing_buffer->slices[outgoing_slice_idx] to write next */ + /** byte within outgoing_buffer->slices[0] to write next */ size_t outgoing_byte_idx; grpc_closure* read_cb; @@ -532,23 +530,26 @@ static bool tcp_flush(grpc_exec_ctx* exec_ctx, grpc_tcp* tcp, size_t unwind_slice_idx; size_t unwind_byte_idx; + // We always start at zero, because we eagerly unref and trim the slice + // buffer as we write + size_t outgoing_slice_idx = 0; + for (;;) { sending_length = 0; - unwind_slice_idx = tcp->outgoing_slice_idx; + unwind_slice_idx = outgoing_slice_idx; unwind_byte_idx = tcp->outgoing_byte_idx; - for (iov_size = 0; tcp->outgoing_slice_idx != tcp->outgoing_buffer->count && + for (iov_size = 0; outgoing_slice_idx != tcp->outgoing_buffer->count && iov_size != MAX_WRITE_IOVEC; iov_size++) { iov[iov_size].iov_base = GRPC_SLICE_START_PTR( - tcp->outgoing_buffer->slices[tcp->outgoing_slice_idx]) + + tcp->outgoing_buffer->slices[outgoing_slice_idx]) + tcp->outgoing_byte_idx; iov[iov_size].iov_len = - GRPC_SLICE_LENGTH( - tcp->outgoing_buffer->slices[tcp->outgoing_slice_idx]) - + GRPC_SLICE_LENGTH(tcp->outgoing_buffer->slices[outgoing_slice_idx]) - tcp->outgoing_byte_idx; sending_length += iov[iov_size].iov_len; - tcp->outgoing_slice_idx++; + outgoing_slice_idx++; tcp->outgoing_byte_idx = 0; } GPR_ASSERT(iov_size > 0); @@ -574,10 +575,15 @@ static bool tcp_flush(grpc_exec_ctx* exec_ctx, grpc_tcp* tcp, if (sent_length < 0) { if (errno == EAGAIN) { - tcp->outgoing_slice_idx = unwind_slice_idx; tcp->outgoing_byte_idx = unwind_byte_idx; - grpc_slice_buffer_partial_unref_internal(exec_ctx, tcp->outgoing_buffer, - unwind_slice_idx); + // unref all and forget about all slices that have been written to this + // point + for (size_t idx = 0; idx < unwind_slice_idx; ++idx) { + grpc_slice_unref_internal(exec_ctx, + tcp->outgoing_buffer->slices[idx]); + tcp->outgoing_buffer->count--; + } + tcp->outgoing_buffer->slices += unwind_slice_idx; return false; } else if (errno == EPIPE) { *error = grpc_error_set_int(GRPC_OS_ERROR(errno, "sendmsg"), @@ -595,9 +601,9 @@ static bool tcp_flush(grpc_exec_ctx* exec_ctx, grpc_tcp* tcp, while (trailing > 0) { size_t slice_length; - tcp->outgoing_slice_idx--; - slice_length = GRPC_SLICE_LENGTH( - tcp->outgoing_buffer->slices[tcp->outgoing_slice_idx]); + outgoing_slice_idx--; + slice_length = + GRPC_SLICE_LENGTH(tcp->outgoing_buffer->slices[outgoing_slice_idx]); if (slice_length > trailing) { tcp->outgoing_byte_idx = slice_length - trailing; break; @@ -606,7 +612,7 @@ static bool tcp_flush(grpc_exec_ctx* exec_ctx, grpc_tcp* tcp, } } - if (tcp->outgoing_slice_idx == tcp->outgoing_buffer->count) { + if (outgoing_slice_idx == tcp->outgoing_buffer->count) { *error = GRPC_ERROR_NONE; grpc_slice_buffer_reset_and_unref_internal(exec_ctx, tcp->outgoing_buffer); @@ -676,7 +682,6 @@ static void tcp_write(grpc_exec_ctx* exec_ctx, grpc_endpoint* ep, return; } tcp->outgoing_buffer = buf; - tcp->outgoing_slice_idx = 0; tcp->outgoing_byte_idx = 0; if (!tcp_flush(exec_ctx, tcp, &error)) { diff --git a/src/core/lib/slice/slice_buffer.cc b/src/core/lib/slice/slice_buffer.cc index 8fdd8cf2257..5db54dad91e 100644 --- a/src/core/lib/slice/slice_buffer.cc +++ b/src/core/lib/slice/slice_buffer.cc @@ -62,7 +62,6 @@ void grpc_slice_buffer_init(grpc_slice_buffer* sb) { sb->count = 0; sb->length = 0; sb->capacity = GRPC_SLICE_BUFFER_INLINE_ELEMENTS; - sb->idx_of_first_valid_slice = 0; sb->base_slices = sb->slices = sb->inlined; } @@ -167,27 +166,12 @@ void grpc_slice_buffer_pop(grpc_slice_buffer* sb) { void grpc_slice_buffer_reset_and_unref_internal(grpc_exec_ctx* exec_ctx, grpc_slice_buffer* sb) { size_t i; - for (i = sb->idx_of_first_valid_slice; i < sb->count; i++) { + for (i = 0; i < sb->count; i++) { grpc_slice_unref_internal(exec_ctx, sb->slices[i]); } sb->count = 0; sb->length = 0; - sb->idx_of_first_valid_slice = 0; -} - -void grpc_slice_buffer_partial_unref_internal(grpc_exec_ctx* exec_ctx, - grpc_slice_buffer* sb, - size_t idx) { - GPR_ASSERT(idx < sb->count); // if idx == count, then partial is not needed - GPR_ASSERT(sb->idx_of_first_valid_slice <= idx); - - size_t i; - for (i = sb->idx_of_first_valid_slice; i < idx; i++) { - grpc_slice_unref_internal(exec_ctx, sb->slices[i]); - } - - sb->idx_of_first_valid_slice = idx; } void grpc_slice_buffer_reset_and_unref(grpc_slice_buffer* sb) { From 588d6d9294fb328d22d2edbd39693408cf6b2eff Mon Sep 17 00:00:00 2001 From: ncteisen Date: Thu, 16 Nov 2017 14:28:30 -0800 Subject: [PATCH 42/70] unref before write failures for completness --- src/core/lib/iomgr/tcp_posix.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/lib/iomgr/tcp_posix.cc b/src/core/lib/iomgr/tcp_posix.cc index 3523c689730..026c1c09425 100644 --- a/src/core/lib/iomgr/tcp_posix.cc +++ b/src/core/lib/iomgr/tcp_posix.cc @@ -589,9 +589,13 @@ static bool tcp_flush(grpc_exec_ctx* exec_ctx, grpc_tcp* tcp, *error = grpc_error_set_int(GRPC_OS_ERROR(errno, "sendmsg"), GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE); + grpc_slice_buffer_reset_and_unref_internal(exec_ctx, + tcp->outgoing_buffer); return true; } else { *error = tcp_annotate_error(GRPC_OS_ERROR(errno, "sendmsg"), tcp); + grpc_slice_buffer_reset_and_unref_internal(exec_ctx, + tcp->outgoing_buffer); return true; } } From 34fec74986ec693643b083920496d7c91624a5c8 Mon Sep 17 00:00:00 2001 From: ncteisen Date: Thu, 16 Nov 2017 14:33:00 -0800 Subject: [PATCH 43/70] No more API change, no test change --- test/core/slice/slice_buffer_test.cc | 39 ---------------------------- 1 file changed, 39 deletions(-) diff --git a/test/core/slice/slice_buffer_test.cc b/test/core/slice/slice_buffer_test.cc index 696251536f5..338e8079dc8 100644 --- a/test/core/slice/slice_buffer_test.cc +++ b/test/core/slice/slice_buffer_test.cc @@ -18,7 +18,6 @@ #include #include -#include "src/core/lib/slice/slice_internal.h" #include "test/core/util/test_config.h" void test_slice_buffer_add() { @@ -105,49 +104,11 @@ void test_slice_buffer_move_first() { GPR_ASSERT(dst.length == dst_len); } -static void populate_slice_buffer(grpc_slice_buffer* sb) { - grpc_slice slices[4]; - int idx = 0; - - slices[0] = grpc_slice_from_copied_string("aaa"); - slices[1] = grpc_slice_from_copied_string("bbbb"); - slices[2] = grpc_slice_from_copied_string("ccc"); - slices[3] = grpc_slice_from_copied_string("ddddd"); - - for (idx = 0; idx < 4; idx++) { - grpc_slice_ref(slices[idx]); - grpc_slice_buffer_add_indexed(sb, slices[idx]); - } -} - -void test_slice_buffer_unref() { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_slice_buffer sb; - - grpc_slice_buffer_init(&sb); - - // regular init and unref - populate_slice_buffer(&sb); - grpc_slice_buffer_reset_and_unref_internal(&exec_ctx, &sb); - - // inits, then unrefs partial, then unrefs the rest - populate_slice_buffer(&sb); - grpc_slice_buffer_partial_unref_internal(&exec_ctx, &sb, 1); - grpc_slice_buffer_reset_and_unref_internal(&exec_ctx, &sb); - - // two partial unrefs - populate_slice_buffer(&sb); - grpc_slice_buffer_partial_unref_internal(&exec_ctx, &sb, 1); - grpc_slice_buffer_partial_unref_internal(&exec_ctx, &sb, 3); - grpc_slice_buffer_reset_and_unref_internal(&exec_ctx, &sb); -} - int main(int argc, char** argv) { grpc_test_init(argc, argv); test_slice_buffer_add(); test_slice_buffer_move_first(); - test_slice_buffer_unref(); return 0; } From 1de0d69fd92918a20a60c8d7d75e5d1effe42966 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Fri, 17 Nov 2017 15:55:47 +0100 Subject: [PATCH 44/70] add GCStats to microbenchmarks --- src/csharp/Grpc.Microbenchmarks/GCStats.cs | 69 +++++++++++++++++++ .../Grpc.Microbenchmarks/ThreadedBenchmark.cs | 3 +- 2 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 src/csharp/Grpc.Microbenchmarks/GCStats.cs diff --git a/src/csharp/Grpc.Microbenchmarks/GCStats.cs b/src/csharp/Grpc.Microbenchmarks/GCStats.cs new file mode 100644 index 00000000000..ca7051ec4e5 --- /dev/null +++ b/src/csharp/Grpc.Microbenchmarks/GCStats.cs @@ -0,0 +1,69 @@ +#region Copyright notice and license + +// Copyright 2015 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#endregion + +using System; +using Grpc.Core; +using Grpc.Core.Internal; + +namespace Grpc.Microbenchmarks +{ + internal class GCStats + { + readonly object myLock = new object(); + GCStatsSnapshot lastSnapshot; + + public GCStats() + { + lastSnapshot = new GCStatsSnapshot(GC.CollectionCount(0), GC.CollectionCount(1), GC.CollectionCount(2)); + } + + public GCStatsSnapshot GetSnapshot(bool reset = false) + { + lock (myLock) + { + var newSnapshot = new GCStatsSnapshot(GC.CollectionCount(0) - lastSnapshot.Gen0, + GC.CollectionCount(1) - lastSnapshot.Gen1, + GC.CollectionCount(2) - lastSnapshot.Gen2); + if (reset) + { + lastSnapshot = newSnapshot; + } + return newSnapshot; + } + } + } + + public class GCStatsSnapshot + { + public GCStatsSnapshot(int gen0, int gen1, int gen2) + { + this.Gen0 = gen0; + this.Gen1 = gen1; + this.Gen2 = gen2; + } + + public int Gen0 { get; } + public int Gen1 { get; } + public int Gen2 { get; } + + public override string ToString() + { + return string.Format("[GCCollectionCount: gen0 {0}, gen1 {1}, gen2 {2}]", Gen0, Gen1, Gen2); + } + } +} diff --git a/src/csharp/Grpc.Microbenchmarks/ThreadedBenchmark.cs b/src/csharp/Grpc.Microbenchmarks/ThreadedBenchmark.cs index feac8d16908..95b9aaaf3f9 100644 --- a/src/csharp/Grpc.Microbenchmarks/ThreadedBenchmark.cs +++ b/src/csharp/Grpc.Microbenchmarks/ThreadedBenchmark.cs @@ -46,6 +46,7 @@ namespace Grpc.Microbenchmarks public void Run() { Console.WriteLine("Running threads."); + var gcStats = new GCStats(); var threads = new List(); for (int i = 0; i < runners.Count; i++) { @@ -58,7 +59,7 @@ namespace Grpc.Microbenchmarks { thread.Join(); } - Console.WriteLine("All threads finished."); + Console.WriteLine("All threads finished (GC Stats Delta: " + gcStats.GetSnapshot() + ")"); } } } From ef69907cff00c8fabde2c112925196778c39fbef Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Fri, 17 Nov 2017 17:38:18 +0100 Subject: [PATCH 45/70] add CompletionRegistry microbenchmark --- .../CompletionRegistryBenchmark.cs | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 src/csharp/Grpc.Microbenchmarks/CompletionRegistryBenchmark.cs diff --git a/src/csharp/Grpc.Microbenchmarks/CompletionRegistryBenchmark.cs b/src/csharp/Grpc.Microbenchmarks/CompletionRegistryBenchmark.cs new file mode 100644 index 00000000000..810de119a8b --- /dev/null +++ b/src/csharp/Grpc.Microbenchmarks/CompletionRegistryBenchmark.cs @@ -0,0 +1,69 @@ +#region Copyright notice and license + +// Copyright 2015 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#endregion + +using System; +using System.Runtime.InteropServices; +using System.Threading; +using Grpc.Core; +using Grpc.Core.Internal; +using System.Collections.Generic; +using System.Diagnostics; + +namespace Grpc.Microbenchmarks +{ + public class CompletionRegistryBenchmark + { + GrpcEnvironment environment; + + public void Init() + { + environment = GrpcEnvironment.AddRef(); + } + + public void Cleanup() + { + GrpcEnvironment.ReleaseAsync().Wait(); + } + + public void Run(int threadCount, int iterations) + { + Console.WriteLine(string.Format("CompletionRegistryBenchmark: threads={0}, iterations={1}", threadCount, iterations)); + var threadedBenchmark = new ThreadedBenchmark(threadCount, () => ThreadBody(iterations)); + threadedBenchmark.Run(); + // TODO: parametrize by number of pending completions + } + + private void ThreadBody(int iterations) + { + var completionRegistry = new CompletionRegistry(environment); + var ctx = BatchContextSafeHandle.Create(); + var completionDelegate = new OpCompletionDelegate((success) => {}); + + var stopwatch = Stopwatch.StartNew(); + for (int i = 0; i < iterations; i++) + { + completionRegistry.Register(ctx.DangerousGetHandle(), completionDelegate); + var callback = completionRegistry.Extract(completionRegistry.LastRegisteredKey); + } + stopwatch.Stop(); + Console.WriteLine("Elapsed millis: " + stopwatch.ElapsedMilliseconds); + + ctx.Dispose(); + } + } +} From 74a723f20da288fc4087f75a7a42cdc4b33adbe4 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Fri, 17 Nov 2017 17:39:01 +0100 Subject: [PATCH 46/70] add PInvokeByteArray microbenchmark --- .../PInvokeByteArrayBenchmark.cs | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 src/csharp/Grpc.Microbenchmarks/PInvokeByteArrayBenchmark.cs diff --git a/src/csharp/Grpc.Microbenchmarks/PInvokeByteArrayBenchmark.cs b/src/csharp/Grpc.Microbenchmarks/PInvokeByteArrayBenchmark.cs new file mode 100644 index 00000000000..787b5508fba --- /dev/null +++ b/src/csharp/Grpc.Microbenchmarks/PInvokeByteArrayBenchmark.cs @@ -0,0 +1,64 @@ +#region Copyright notice and license + +// Copyright 2015 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#endregion + +using System; +using System.Runtime.InteropServices; +using System.Threading; +using Grpc.Core; +using Grpc.Core.Internal; +using System.Collections.Generic; +using System.Diagnostics; + +namespace Grpc.Microbenchmarks +{ + public class PInvokeByteArrayBenchmark + { + static readonly NativeMethods Native = NativeMethods.Get(); + + public void Init() + { + } + + public void Cleanup() + { + } + + public void Run(int threadCount, int iterations, int payloadSize) + { + Console.WriteLine(string.Format("PInvokeByteArrayBenchmark: threads={0}, iterations={1}, payloadSize={2}", threadCount, iterations, payloadSize)); + var threadedBenchmark = new ThreadedBenchmark(threadCount, () => ThreadBody(iterations, payloadSize)); + threadedBenchmark.Run(); + } + + private void ThreadBody(int iterations, int payloadSize) + { + var payload = new byte[payloadSize]; + + var stopwatch = Stopwatch.StartNew(); + for (int i = 0; i < iterations; i++) + { + var gcHandle = GCHandle.Alloc(payload, GCHandleType.Pinned); + var payloadPtr = gcHandle.AddrOfPinnedObject(); + Native.grpcsharp_test_nop(payloadPtr); + gcHandle.Free(); + } + stopwatch.Stop(); + Console.WriteLine("Elapsed millis: " + stopwatch.ElapsedMilliseconds); + } + } +} From 8aeaacc220cb5238c9cab91c91fc5878ab3b4775 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Fri, 17 Nov 2017 17:39:29 +0100 Subject: [PATCH 47/70] generalize invocation of C# microbenchmarks --- .../Grpc.Microbenchmarks.csproj | 4 ++ src/csharp/Grpc.Microbenchmarks/Program.cs | 67 +++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/src/csharp/Grpc.Microbenchmarks/Grpc.Microbenchmarks.csproj b/src/csharp/Grpc.Microbenchmarks/Grpc.Microbenchmarks.csproj index 108357e4eb4..8a629f9748b 100644 --- a/src/csharp/Grpc.Microbenchmarks/Grpc.Microbenchmarks.csproj +++ b/src/csharp/Grpc.Microbenchmarks/Grpc.Microbenchmarks.csproj @@ -15,6 +15,10 @@ + + + + diff --git a/src/csharp/Grpc.Microbenchmarks/Program.cs b/src/csharp/Grpc.Microbenchmarks/Program.cs index d07d4187c45..79ec308501d 100644 --- a/src/csharp/Grpc.Microbenchmarks/Program.cs +++ b/src/csharp/Grpc.Microbenchmarks/Program.cs @@ -20,14 +20,81 @@ using System; using Grpc.Core; using Grpc.Core.Internal; using Grpc.Core.Logging; +using CommandLine; +using CommandLine.Text; namespace Grpc.Microbenchmarks { class Program { + public enum MicrobenchmarkType + { + CompletionRegistry, + PInvokeByteArray, + SendMessage + } + + private class BenchmarkOptions + { + [Option("benchmark", Required = true, HelpText = "Benchmark to run")] + public MicrobenchmarkType Benchmark { get; set; } + } + public static void Main(string[] args) { GrpcEnvironment.SetLogger(new ConsoleLogger()); + var parserResult = Parser.Default.ParseArguments(args) + .WithNotParsed(errors => { + Console.WriteLine("Supported benchmarks:"); + foreach (var enumValue in Enum.GetValues(typeof(MicrobenchmarkType))) + { + Console.WriteLine(" " + enumValue); + } + Environment.Exit(1); + }) + .WithParsed(options => + { + switch (options.Benchmark) + { + case MicrobenchmarkType.CompletionRegistry: + RunCompletionRegistryBenchmark(); + break; + case MicrobenchmarkType.PInvokeByteArray: + RunPInvokeByteArrayBenchmark(); + break; + case MicrobenchmarkType.SendMessage: + RunSendMessageBenchmark(); + break; + default: + throw new ArgumentException("Unsupported benchmark."); + } + }); + } + + static void RunCompletionRegistryBenchmark() + { + var benchmark = new CompletionRegistryBenchmark(); + benchmark.Init(); + foreach (int threadCount in new int[] {1, 1, 2, 4, 8, 12}) + { + benchmark.Run(threadCount, 4 * 1000 * 1000); + } + benchmark.Cleanup(); + } + + static void RunPInvokeByteArrayBenchmark() + { + var benchmark = new PInvokeByteArrayBenchmark(); + benchmark.Init(); + foreach (int threadCount in new int[] {1, 1, 2, 4, 8, 12}) + { + benchmark.Run(threadCount, 4 * 1000 * 1000, 0); + } + benchmark.Cleanup(); + } + + static void RunSendMessageBenchmark() + { var benchmark = new SendMessageBenchmark(); benchmark.Init(); foreach (int threadCount in new int[] {1, 1, 2, 4, 8, 12}) From 692915ee0784909bceabcd67a7350b9f2fc2f501 Mon Sep 17 00:00:00 2001 From: ncteisen Date: Fri, 17 Nov 2017 11:45:49 -0800 Subject: [PATCH 48/70] Swtich to using grpc_bb_take_first --- src/core/lib/iomgr/tcp_posix.cc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/core/lib/iomgr/tcp_posix.cc b/src/core/lib/iomgr/tcp_posix.cc index 026c1c09425..212ecd7fc65 100644 --- a/src/core/lib/iomgr/tcp_posix.cc +++ b/src/core/lib/iomgr/tcp_posix.cc @@ -579,11 +579,9 @@ static bool tcp_flush(grpc_exec_ctx* exec_ctx, grpc_tcp* tcp, // unref all and forget about all slices that have been written to this // point for (size_t idx = 0; idx < unwind_slice_idx; ++idx) { - grpc_slice_unref_internal(exec_ctx, - tcp->outgoing_buffer->slices[idx]); - tcp->outgoing_buffer->count--; + grpc_slice_unref_internal( + exec_ctx, grpc_slice_buffer_take_first(tcp->outgoing_buffer)); } - tcp->outgoing_buffer->slices += unwind_slice_idx; return false; } else if (errno == EPIPE) { *error = grpc_error_set_int(GRPC_OS_ERROR(errno, "sendmsg"), From f852c060384027ae1c82b1ea5d8dae62c9c5ea3c Mon Sep 17 00:00:00 2001 From: ncteisen Date: Fri, 17 Nov 2017 16:50:12 -0800 Subject: [PATCH 49/70] Fix TSAN attempt --- src/core/lib/transport/error_utils.cc | 9 ++++++--- src/core/lib/transport/error_utils.h | 6 +++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/core/lib/transport/error_utils.cc b/src/core/lib/transport/error_utils.cc index 723663a6d78..acac1330b56 100644 --- a/src/core/lib/transport/error_utils.cc +++ b/src/core/lib/transport/error_utils.cc @@ -43,7 +43,7 @@ static grpc_error* recursively_find_error_with_field(grpc_error* error, void grpc_error_get_status(grpc_exec_ctx* exec_ctx, grpc_error* error, grpc_millis deadline, grpc_status_code* code, grpc_slice* slice, grpc_http2_error_code* http_error, - const char** full_error_details) { + const char** error_string) { // Start with the parent error and recurse through the tree of children // until we find the first one that has a status code. grpc_error* found_error = @@ -70,8 +70,11 @@ void grpc_error_get_status(grpc_exec_ctx* exec_ctx, grpc_error* error, } if (code != nullptr) *code = status; - if (full_error_details != NULL && status != GRPC_STATUS_OK) { - *full_error_details = gpr_strdup(grpc_error_string(error)); + if (error_string != NULL && status != GRPC_STATUS_OK) { + const char* str = grpc_error_string(error); + if (str != nullptr) { + *error_string = gpr_strdup(str); + } } if (http_error != nullptr) { diff --git a/src/core/lib/transport/error_utils.h b/src/core/lib/transport/error_utils.h index d47c4f2a3be..6f21f484e5c 100644 --- a/src/core/lib/transport/error_utils.h +++ b/src/core/lib/transport/error_utils.h @@ -30,15 +30,15 @@ extern "C" { /// A utility function to get the status code and message to be returned /// to the application. If not set in the top-level message, looks /// through child errors until it finds the first one with these attributes. -/// All attributes are pulled from the same child error. full_error_details will +/// All attributes are pulled from the same child error. error_string will /// be populated with the entire error string. If any of the attributes (code, -/// msg, http_status, full_error_details) are unneeded, they can be passed as +/// msg, http_status, error_string) are unneeded, they can be passed as /// NULL. void grpc_error_get_status(grpc_exec_ctx* exec_ctx, grpc_error* error, grpc_millis deadline, grpc_status_code* code, grpc_slice* slice, grpc_http2_error_code* http_status, - const char** full_error_details); + const char** error_string); /// A utility function to check whether there is a clear status code that /// doesn't need to be guessed in \a error. This means that \a error or some From 26521b38d235c9587ed391e285a3f5d34979df75 Mon Sep 17 00:00:00 2001 From: yang-g Date: Fri, 17 Nov 2017 17:15:37 -0800 Subject: [PATCH 50/70] Avoid calling dtor on grpc_fd before putting it in freelist --- src/core/lib/iomgr/ev_epoll1_linux.cc | 10 ++++++---- src/core/lib/iomgr/ev_epollex_linux.cc | 10 ++++++---- src/core/lib/iomgr/ev_epollsig_linux.cc | 10 ++++++---- src/core/lib/iomgr/lockfree_event.cc | 6 ++++-- src/core/lib/iomgr/lockfree_event.h | 4 +++- 5 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/core/lib/iomgr/ev_epoll1_linux.cc b/src/core/lib/iomgr/ev_epoll1_linux.cc index 918bc6f9333..dddd52c49f9 100644 --- a/src/core/lib/iomgr/ev_epoll1_linux.cc +++ b/src/core/lib/iomgr/ev_epoll1_linux.cc @@ -263,11 +263,13 @@ static grpc_fd* fd_create(int fd, const char* name) { if (new_fd == nullptr) { new_fd = (grpc_fd*)gpr_malloc(sizeof(grpc_fd)); + new_fd->read_closure.Init(); + new_fd->write_closure.Init(); } new_fd->fd = fd; - new_fd->read_closure.Init(); - new_fd->write_closure.Init(); + new_fd->read_closure->Init(); + new_fd->write_closure->Init(); gpr_atm_no_barrier_store(&new_fd->read_notifier_pollset, (gpr_atm)NULL); new_fd->freelist_next = nullptr; @@ -336,8 +338,8 @@ static void fd_orphan(grpc_exec_ctx* exec_ctx, grpc_fd* fd, GRPC_CLOSURE_SCHED(exec_ctx, on_done, GRPC_ERROR_REF(error)); grpc_iomgr_unregister_object(&fd->iomgr_object); - fd->read_closure.Destroy(); - fd->write_closure.Destroy(); + fd->read_closure->Destroy(); + fd->write_closure->Destroy(); gpr_mu_lock(&fd_freelist_mu); fd->freelist_next = fd_freelist; diff --git a/src/core/lib/iomgr/ev_epollex_linux.cc b/src/core/lib/iomgr/ev_epollex_linux.cc index bfd2ac43263..d2192731bfc 100644 --- a/src/core/lib/iomgr/ev_epollex_linux.cc +++ b/src/core/lib/iomgr/ev_epollex_linux.cc @@ -288,8 +288,8 @@ static void fd_destroy(grpc_exec_ctx* exec_ctx, void* arg, grpc_error* error) { fd->freelist_next = fd_freelist; fd_freelist = fd; - fd->read_closure.Destroy(); - fd->write_closure.Destroy(); + fd->read_closure->Destroy(); + fd->write_closure->Destroy(); gpr_mu_unlock(&fd_freelist_mu); } @@ -342,6 +342,8 @@ static grpc_fd* fd_create(int fd, const char* name) { if (new_fd == nullptr) { new_fd = (grpc_fd*)gpr_malloc(sizeof(grpc_fd)); + new_fd->read_closure.Init(); + new_fd->write_closure.Init(); } gpr_mu_init(&new_fd->pollable_mu); @@ -349,8 +351,8 @@ static grpc_fd* fd_create(int fd, const char* name) { new_fd->pollable_obj = nullptr; gpr_atm_rel_store(&new_fd->refst, (gpr_atm)1); new_fd->fd = fd; - new_fd->read_closure.Init(); - new_fd->write_closure.Init(); + new_fd->read_closure->Init(); + new_fd->write_closure->Init(); gpr_atm_no_barrier_store(&new_fd->read_notifier_pollset, (gpr_atm)NULL); new_fd->freelist_next = nullptr; diff --git a/src/core/lib/iomgr/ev_epollsig_linux.cc b/src/core/lib/iomgr/ev_epollsig_linux.cc index 5b4a7ba19c6..25c0a938971 100644 --- a/src/core/lib/iomgr/ev_epollsig_linux.cc +++ b/src/core/lib/iomgr/ev_epollsig_linux.cc @@ -767,8 +767,8 @@ static void unref_by(grpc_fd* fd, int n) { fd_freelist = fd; grpc_iomgr_unregister_object(&fd->iomgr_object); - fd->read_closure.Destroy(); - fd->write_closure.Destroy(); + fd->read_closure->Destroy(); + fd->write_closure->Destroy(); gpr_mu_unlock(&fd_freelist_mu); } else { @@ -819,6 +819,8 @@ static grpc_fd* fd_create(int fd, const char* name) { if (new_fd == nullptr) { new_fd = (grpc_fd*)gpr_malloc(sizeof(grpc_fd)); gpr_mu_init(&new_fd->po.mu); + new_fd->read_closure.Init(); + new_fd->write_closure.Init(); } /* Note: It is not really needed to get the new_fd->po.mu lock here. If this @@ -833,8 +835,8 @@ static grpc_fd* fd_create(int fd, const char* name) { gpr_atm_rel_store(&new_fd->refst, (gpr_atm)1); new_fd->fd = fd; new_fd->orphaned = false; - new_fd->read_closure.Init(); - new_fd->write_closure.Init(); + new_fd->read_closure->Init(); + new_fd->write_closure->Init(); gpr_atm_no_barrier_store(&new_fd->read_notifier_pollset, (gpr_atm)NULL); new_fd->freelist_next = nullptr; diff --git a/src/core/lib/iomgr/lockfree_event.cc b/src/core/lib/iomgr/lockfree_event.cc index 40e2ed62193..57a81fb5b26 100644 --- a/src/core/lib/iomgr/lockfree_event.cc +++ b/src/core/lib/iomgr/lockfree_event.cc @@ -57,7 +57,9 @@ extern grpc_tracer_flag grpc_polling_trace; namespace grpc_core { -LockfreeEvent::LockfreeEvent() { +LockfreeEvent::LockfreeEvent() { Init(); } + +void LockfreeEvent::Init() { /* Perform an atomic store to start the state machine. Note carefully that LockfreeEvent *MAY* be used whilst in a destroyed @@ -67,7 +69,7 @@ LockfreeEvent::LockfreeEvent() { gpr_atm_no_barrier_store(&state_, kClosureNotReady); } -LockfreeEvent::~LockfreeEvent() { +void LockfreeEvent::Destroy() { gpr_atm curr; do { curr = gpr_atm_no_barrier_load(&state_); diff --git a/src/core/lib/iomgr/lockfree_event.h b/src/core/lib/iomgr/lockfree_event.h index c667dcd3bc9..b9490f5458b 100644 --- a/src/core/lib/iomgr/lockfree_event.h +++ b/src/core/lib/iomgr/lockfree_event.h @@ -30,11 +30,13 @@ namespace grpc_core { class LockfreeEvent { public: LockfreeEvent(); - ~LockfreeEvent(); LockfreeEvent(const LockfreeEvent&) = delete; LockfreeEvent& operator=(const LockfreeEvent&) = delete; + void Init(); + void Destroy(); + bool IsShutdown() const { return (gpr_atm_no_barrier_load(&state_) & kShutdownBit) != 0; } From abbdbf93742ed46c7f7481816f0915c069357040 Mon Sep 17 00:00:00 2001 From: ncteisen Date: Fri, 17 Nov 2017 19:02:04 -0800 Subject: [PATCH 51/70] Attempt to fix TSAN --- test/core/end2end/fixtures/http_proxy_fixture.cc | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/test/core/end2end/fixtures/http_proxy_fixture.cc b/test/core/end2end/fixtures/http_proxy_fixture.cc index 0bdd15c57d5..ac0c953a79f 100644 --- a/test/core/end2end/fixtures/http_proxy_fixture.cc +++ b/test/core/end2end/fixtures/http_proxy_fixture.cc @@ -88,9 +88,11 @@ typedef struct proxy_connection { grpc_slice_buffer client_read_buffer; grpc_slice_buffer client_deferred_write_buffer; + bool client_is_writing; grpc_slice_buffer client_write_buffer; grpc_slice_buffer server_read_buffer; grpc_slice_buffer server_deferred_write_buffer; + bool server_is_writing; grpc_slice_buffer server_write_buffer; grpc_http_parser http_parser; @@ -148,6 +150,7 @@ static void proxy_connection_failed(grpc_exec_ctx* exec_ctx, static void on_client_write_done(grpc_exec_ctx* exec_ctx, void* arg, grpc_error* error) { proxy_connection* conn = (proxy_connection*)arg; + conn->client_is_writing = false; if (error != GRPC_ERROR_NONE) { proxy_connection_failed(exec_ctx, conn, true /* is_client */, "HTTP proxy client write", error); @@ -160,6 +163,7 @@ static void on_client_write_done(grpc_exec_ctx* exec_ctx, void* arg, if (conn->client_deferred_write_buffer.length > 0) { grpc_slice_buffer_move_into(&conn->client_deferred_write_buffer, &conn->client_write_buffer); + conn->client_is_writing = true; grpc_endpoint_write(exec_ctx, conn->client_endpoint, &conn->client_write_buffer, &conn->on_client_write_done); @@ -173,6 +177,7 @@ static void on_client_write_done(grpc_exec_ctx* exec_ctx, void* arg, static void on_server_write_done(grpc_exec_ctx* exec_ctx, void* arg, grpc_error* error) { proxy_connection* conn = (proxy_connection*)arg; + conn->server_is_writing = false; if (error != GRPC_ERROR_NONE) { proxy_connection_failed(exec_ctx, conn, false /* is_client */, "HTTP proxy server write", error); @@ -185,6 +190,7 @@ static void on_server_write_done(grpc_exec_ctx* exec_ctx, void* arg, if (conn->server_deferred_write_buffer.length > 0) { grpc_slice_buffer_move_into(&conn->server_deferred_write_buffer, &conn->server_write_buffer); + conn->server_is_writing = true; grpc_endpoint_write(exec_ctx, conn->server_endpoint, &conn->server_write_buffer, &conn->on_server_write_done); @@ -210,13 +216,14 @@ static void on_client_read_done(grpc_exec_ctx* exec_ctx, void* arg, // the current write is finished. // // Otherwise, move the read data into the write buffer and write it. - if (conn->server_write_buffer.length > 0) { + if (conn->server_is_writing) { grpc_slice_buffer_move_into(&conn->client_read_buffer, &conn->server_deferred_write_buffer); } else { grpc_slice_buffer_move_into(&conn->client_read_buffer, &conn->server_write_buffer); proxy_connection_ref(conn, "client_read"); + conn->server_is_writing = true; grpc_endpoint_write(exec_ctx, conn->server_endpoint, &conn->server_write_buffer, &conn->on_server_write_done); @@ -242,13 +249,14 @@ static void on_server_read_done(grpc_exec_ctx* exec_ctx, void* arg, // the current write is finished. // // Otherwise, move the read data into the write buffer and write it. - if (conn->client_write_buffer.length > 0) { + if (conn->client_is_writing) { grpc_slice_buffer_move_into(&conn->server_read_buffer, &conn->client_deferred_write_buffer); } else { grpc_slice_buffer_move_into(&conn->server_read_buffer, &conn->client_write_buffer); proxy_connection_ref(conn, "server_read"); + conn->client_is_writing = true; grpc_endpoint_write(exec_ctx, conn->client_endpoint, &conn->client_write_buffer, &conn->on_client_write_done); @@ -262,6 +270,7 @@ static void on_server_read_done(grpc_exec_ctx* exec_ctx, void* arg, static void on_write_response_done(grpc_exec_ctx* exec_ctx, void* arg, grpc_error* error) { proxy_connection* conn = (proxy_connection*)arg; + conn->client_is_writing = false; if (error != GRPC_ERROR_NONE) { proxy_connection_failed(exec_ctx, conn, true /* is_client */, "HTTP proxy write response", error); @@ -302,6 +311,7 @@ static void on_server_connect_done(grpc_exec_ctx* exec_ctx, void* arg, grpc_slice slice = grpc_slice_from_copied_string("HTTP/1.0 200 connected\r\n\r\n"); grpc_slice_buffer_add(&conn->client_write_buffer, slice); + conn->client_is_writing = true; grpc_endpoint_write(exec_ctx, conn->client_endpoint, &conn->client_write_buffer, &conn->on_write_response_done); @@ -450,9 +460,11 @@ static void on_accept(grpc_exec_ctx* exec_ctx, void* arg, grpc_combiner_scheduler(conn->proxy->combiner)); grpc_slice_buffer_init(&conn->client_read_buffer); grpc_slice_buffer_init(&conn->client_deferred_write_buffer); + conn->client_is_writing = false; grpc_slice_buffer_init(&conn->client_write_buffer); grpc_slice_buffer_init(&conn->server_read_buffer); grpc_slice_buffer_init(&conn->server_deferred_write_buffer); + conn->server_is_writing = false; grpc_slice_buffer_init(&conn->server_write_buffer); grpc_http_parser_init(&conn->http_parser, GRPC_HTTP_REQUEST, &conn->http_request); From 84a066bf29180bb2d48d118e8bd17362a649dffa Mon Sep 17 00:00:00 2001 From: josh Date: Fri, 17 Nov 2017 20:11:56 -0700 Subject: [PATCH 52/70] Turning on executable bit for tools/run_tests/sanity/check_bazel_workspace.py --- tools/run_tests/sanity/check_bazel_workspace.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 tools/run_tests/sanity/check_bazel_workspace.py diff --git a/tools/run_tests/sanity/check_bazel_workspace.py b/tools/run_tests/sanity/check_bazel_workspace.py old mode 100644 new mode 100755 From 8abe26237e0060d2601b789ffcbf52437bd67a7d Mon Sep 17 00:00:00 2001 From: kwasimensah Date: Sun, 19 Nov 2017 16:01:50 -0500 Subject: [PATCH 53/70] Fix pthread cleanup logic Don't delete the key in the pthread_key_create destructor. The key isn't specific to instances of values. --- src/core/lib/support/cpu_posix.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/core/lib/support/cpu_posix.cc b/src/core/lib/support/cpu_posix.cc index c9dbc0d5d90..bca14a0c121 100644 --- a/src/core/lib/support/cpu_posix.cc +++ b/src/core/lib/support/cpu_posix.cc @@ -49,15 +49,14 @@ unsigned gpr_cpu_num_cores(void) { return (unsigned)ncpus; } -static void delete_thread_id_key(void* value) { +static void delete_thread_id(void* value) { if (value) { gpr_free(value); } - pthread_key_delete(thread_id_key); } static void init_thread_id_key(void) { - pthread_key_create(&thread_id_key, delete_thread_id_key); + pthread_key_create(&thread_id_key, delete_thread_id); } unsigned gpr_cpu_current_cpu(void) { From 9b4315682bcc5ab581e3deeab41c76e336010a19 Mon Sep 17 00:00:00 2001 From: Mehrdad Afshari Date: Mon, 20 Nov 2017 10:46:28 -0800 Subject: [PATCH 54/70] Do not require bazel for check_grpcio_tools.py The `make_grpcio_tools.py` rube-goldberg machinery relies on `bazel` to extract the list of files required to compile the `grpcio-tools` package that are provided by the `protobuf` submodule. In order to ensure that list is up to date, `check_grpcio_tools.py` sanity check does the same `bazel` query, and checks the full contents against the already existing list in the repository. This has the downside of requiring `bazel` to run that particular check at sanity test time, and flakiness has been seen there. This commit changes the code generation process to include the git hash of the submodule at the time `make_grpcio_tools.py` is invoked and stores it in the generated file and instead of doing the whole process at test time, the `check_grpcio_tools.py` sanity test simply checks the submodule version at test time and verifies it against the version included in the file by `make_grpcio_tools.py` thus removing the `bazel` dependency at test time and increasing test robustness and speed. --- tools/distrib/python/check_grpcio_tools.py | 20 +++++++++++-------- .../python/grpcio_tools/protoc_lib_deps.py | 4 +++- tools/distrib/python/make_grpcio_tools.py | 20 +++++++++++++++++-- 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/tools/distrib/python/check_grpcio_tools.py b/tools/distrib/python/check_grpcio_tools.py index c5afa713e66..b56ccaea7af 100755 --- a/tools/distrib/python/check_grpcio_tools.py +++ b/tools/distrib/python/check_grpcio_tools.py @@ -14,17 +14,21 @@ # See the License for the specific language governing permissions and # limitations under the License. -import cStringIO - -import make_grpcio_tools as make +import make_grpcio_tools as _make OUT_OF_DATE_MESSAGE = """file {} is out of date Have you called tools/distrib/python/make_grpcio_tools.py since upgrading protobuf?""" -check_protoc_lib_deps_content = make.get_deps() +submodule_commit_hash = _make.protobuf_submodule_commit_hash() + +with open(_make.GRPC_PYTHON_PROTOC_LIB_DEPS, 'r') as _protoc_lib_deps_file: + content = _protoc_lib_deps_file.read().splitlines() + +testString = (_make.COMMIT_HASH_PREFIX + + submodule_commit_hash + + _make.COMMIT_HASH_SUFFIX) -with open(make.GRPC_PYTHON_PROTOC_LIB_DEPS, 'r') as protoc_lib_deps_file: - if protoc_lib_deps_file.read() != check_protoc_lib_deps_content: - print(OUT_OF_DATE_MESSAGE.format(make.GRPC_PYTHON_PROTOC_LIB_DEPS)) - raise SystemExit(1) +if testString not in content: + print(OUT_OF_DATE_MESSAGE.format(_make.GRPC_PYTHON_PROTOC_LIB_DEPS)) + raise SystemExit(1) diff --git a/tools/distrib/python/grpcio_tools/protoc_lib_deps.py b/tools/distrib/python/grpcio_tools/protoc_lib_deps.py index 18470d59d59..2a42a518fcd 100644 --- a/tools/distrib/python/grpcio_tools/protoc_lib_deps.py +++ b/tools/distrib/python/grpcio_tools/protoc_lib_deps.py @@ -1,5 +1,5 @@ -# Copyright 2016 gRPC authors. +# Copyright 2017 gRPC authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,3 +19,5 @@ PROTO_FILES=['google/protobuf/wrappers.proto', 'google/protobuf/type.proto', 'go CC_INCLUDE='third_party/protobuf/src' PROTO_INCLUDE='third_party/protobuf/src' + +PROTOBUF_SUBMODULE_VERSION="80a37e0782d2d702d52234b62dd4b9ec74fd2c95" diff --git a/tools/distrib/python/make_grpcio_tools.py b/tools/distrib/python/make_grpcio_tools.py index 5bc07ff360d..c865f0bcc03 100755 --- a/tools/distrib/python/make_grpcio_tools.py +++ b/tools/distrib/python/make_grpcio_tools.py @@ -28,7 +28,7 @@ import traceback import uuid DEPS_FILE_CONTENT=""" -# Copyright 2016 gRPC authors. +# Copyright 2017 gRPC authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -48,8 +48,13 @@ PROTO_FILES={proto_files} CC_INCLUDE={cc_include} PROTO_INCLUDE={proto_include} + +{commit_hash} """ +COMMIT_HASH_PREFIX = 'PROTOBUF_SUBMODULE_VERSION="' +COMMIT_HASH_SUFFIX = '"' + # Bazel query result prefix for expected source files in protobuf. PROTOBUF_CC_PREFIX = '//:src/' PROTOBUF_PROTO_PREFIX = '//:src/' @@ -63,6 +68,7 @@ GRPC_PYTHON_ROOT = os.path.join(GRPC_ROOT, 'tools', 'distrib', GRPC_PYTHON_PROTOBUF_RELATIVE_ROOT = os.path.join('third_party', 'protobuf', 'src') GRPC_PROTOBUF = os.path.join(GRPC_ROOT, GRPC_PYTHON_PROTOBUF_RELATIVE_ROOT) +GRPC_PROTOBUF_SUBMODULE_ROOT = os.path.join(GRPC_ROOT, 'third_party', 'protobuf') GRPC_PROTOC_PLUGINS = os.path.join(GRPC_ROOT, 'src', 'compiler') GRPC_PYTHON_PROTOBUF = os.path.join(GRPC_PYTHON_ROOT, 'third_party', 'protobuf', 'src') @@ -78,6 +84,14 @@ BAZEL_DEPS = os.path.join(GRPC_ROOT, 'tools', 'distrib', 'python', 'bazel_deps.s BAZEL_DEPS_PROTOC_LIB_QUERY = '//:protoc_lib' BAZEL_DEPS_COMMON_PROTOS_QUERY = '//:well_known_protos' +def protobuf_submodule_commit_hash(): + """Gets the commit hash for the HEAD of the protobuf submodule currently + checked out.""" + cwd = os.getcwd() + os.chdir(GRPC_PROTOBUF_SUBMODULE_ROOT) + output = subprocess.check_output(['git', 'rev-parse', 'HEAD']) + os.chdir(cwd) + return output.splitlines()[0].strip() def bazel_query(query): output = subprocess.check_output([BAZEL_DEPS, query]) @@ -94,11 +108,13 @@ def get_deps(): proto_files = [ name[len(PROTOBUF_PROTO_PREFIX):] for name in proto_files_output if name.endswith('.proto') and name.startswith(PROTOBUF_PROTO_PREFIX)] + commit_hash = protobuf_submodule_commit_hash() deps_file_content = DEPS_FILE_CONTENT.format( cc_files=cc_files, proto_files=proto_files, cc_include=repr(GRPC_PYTHON_PROTOBUF_RELATIVE_ROOT), - proto_include=repr(GRPC_PYTHON_PROTOBUF_RELATIVE_ROOT)) + proto_include=repr(GRPC_PYTHON_PROTOBUF_RELATIVE_ROOT), + commit_hash=COMMIT_HASH_PREFIX + commit_hash + COMMIT_HASH_SUFFIX) return deps_file_content def long_path(path): From 8d1a3ca5fc909899b4895733d496fb563c02dae9 Mon Sep 17 00:00:00 2001 From: ncteisen Date: Mon, 20 Nov 2017 15:32:51 -0500 Subject: [PATCH 55/70] use aquire release --- src/core/lib/iomgr/error.cc | 2 +- src/core/lib/transport/error_utils.cc | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/core/lib/iomgr/error.cc b/src/core/lib/iomgr/error.cc index 581b903f1a0..5fe6514802b 100644 --- a/src/core/lib/iomgr/error.cc +++ b/src/core/lib/iomgr/error.cc @@ -754,7 +754,7 @@ const char* grpc_error_string(grpc_error* err) { if (!gpr_atm_rel_cas(&err->atomics.error_string, 0, (gpr_atm)out)) { gpr_free(out); - out = (char*)gpr_atm_no_barrier_load(&err->atomics.error_string); + out = (char*)gpr_atm_acq_load(&err->atomics.error_string); } GPR_TIMER_END("grpc_error_string", 0); diff --git a/src/core/lib/transport/error_utils.cc b/src/core/lib/transport/error_utils.cc index acac1330b56..69c8ae6de36 100644 --- a/src/core/lib/transport/error_utils.cc +++ b/src/core/lib/transport/error_utils.cc @@ -71,10 +71,7 @@ void grpc_error_get_status(grpc_exec_ctx* exec_ctx, grpc_error* error, if (code != nullptr) *code = status; if (error_string != NULL && status != GRPC_STATUS_OK) { - const char* str = grpc_error_string(error); - if (str != nullptr) { - *error_string = gpr_strdup(str); - } + *error_string = gpr_strdup(grpc_error_string(error)); } if (http_error != nullptr) { From ed49fe5510780a96bf128bc6397f0d7d5394f669 Mon Sep 17 00:00:00 2001 From: yang-g Date: Mon, 20 Nov 2017 13:49:54 -0800 Subject: [PATCH 56/70] Resolve comments --- src/core/lib/iomgr/ev_epoll1_linux.cc | 8 ++++---- src/core/lib/iomgr/ev_epollex_linux.cc | 8 ++++---- src/core/lib/iomgr/ev_epollsig_linux.cc | 8 ++++---- src/core/lib/iomgr/lockfree_event.cc | 6 +++--- src/core/lib/iomgr/lockfree_event.h | 7 +++++-- 5 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/core/lib/iomgr/ev_epoll1_linux.cc b/src/core/lib/iomgr/ev_epoll1_linux.cc index dddd52c49f9..8aada4e9535 100644 --- a/src/core/lib/iomgr/ev_epoll1_linux.cc +++ b/src/core/lib/iomgr/ev_epoll1_linux.cc @@ -268,8 +268,8 @@ static grpc_fd* fd_create(int fd, const char* name) { } new_fd->fd = fd; - new_fd->read_closure->Init(); - new_fd->write_closure->Init(); + new_fd->read_closure->InitEvent(); + new_fd->write_closure->InitEvent(); gpr_atm_no_barrier_store(&new_fd->read_notifier_pollset, (gpr_atm)NULL); new_fd->freelist_next = nullptr; @@ -338,8 +338,8 @@ static void fd_orphan(grpc_exec_ctx* exec_ctx, grpc_fd* fd, GRPC_CLOSURE_SCHED(exec_ctx, on_done, GRPC_ERROR_REF(error)); grpc_iomgr_unregister_object(&fd->iomgr_object); - fd->read_closure->Destroy(); - fd->write_closure->Destroy(); + fd->read_closure->DestroyEvent(); + fd->write_closure->DestroyEvent(); gpr_mu_lock(&fd_freelist_mu); fd->freelist_next = fd_freelist; diff --git a/src/core/lib/iomgr/ev_epollex_linux.cc b/src/core/lib/iomgr/ev_epollex_linux.cc index d2192731bfc..a6f069e2943 100644 --- a/src/core/lib/iomgr/ev_epollex_linux.cc +++ b/src/core/lib/iomgr/ev_epollex_linux.cc @@ -288,8 +288,8 @@ static void fd_destroy(grpc_exec_ctx* exec_ctx, void* arg, grpc_error* error) { fd->freelist_next = fd_freelist; fd_freelist = fd; - fd->read_closure->Destroy(); - fd->write_closure->Destroy(); + fd->read_closure->DestroyEvent(); + fd->write_closure->DestroyEvent(); gpr_mu_unlock(&fd_freelist_mu); } @@ -351,8 +351,8 @@ static grpc_fd* fd_create(int fd, const char* name) { new_fd->pollable_obj = nullptr; gpr_atm_rel_store(&new_fd->refst, (gpr_atm)1); new_fd->fd = fd; - new_fd->read_closure->Init(); - new_fd->write_closure->Init(); + new_fd->read_closure->InitEvent(); + new_fd->write_closure->InitEvent(); gpr_atm_no_barrier_store(&new_fd->read_notifier_pollset, (gpr_atm)NULL); new_fd->freelist_next = nullptr; diff --git a/src/core/lib/iomgr/ev_epollsig_linux.cc b/src/core/lib/iomgr/ev_epollsig_linux.cc index 25c0a938971..d51c665e8c0 100644 --- a/src/core/lib/iomgr/ev_epollsig_linux.cc +++ b/src/core/lib/iomgr/ev_epollsig_linux.cc @@ -767,8 +767,8 @@ static void unref_by(grpc_fd* fd, int n) { fd_freelist = fd; grpc_iomgr_unregister_object(&fd->iomgr_object); - fd->read_closure->Destroy(); - fd->write_closure->Destroy(); + fd->read_closure->DestroyEvent(); + fd->write_closure->DestroyEvent(); gpr_mu_unlock(&fd_freelist_mu); } else { @@ -835,8 +835,8 @@ static grpc_fd* fd_create(int fd, const char* name) { gpr_atm_rel_store(&new_fd->refst, (gpr_atm)1); new_fd->fd = fd; new_fd->orphaned = false; - new_fd->read_closure->Init(); - new_fd->write_closure->Init(); + new_fd->read_closure->InitEvent(); + new_fd->write_closure->InitEvent(); gpr_atm_no_barrier_store(&new_fd->read_notifier_pollset, (gpr_atm)NULL); new_fd->freelist_next = nullptr; diff --git a/src/core/lib/iomgr/lockfree_event.cc b/src/core/lib/iomgr/lockfree_event.cc index 57a81fb5b26..edd997ff301 100644 --- a/src/core/lib/iomgr/lockfree_event.cc +++ b/src/core/lib/iomgr/lockfree_event.cc @@ -57,9 +57,9 @@ extern grpc_tracer_flag grpc_polling_trace; namespace grpc_core { -LockfreeEvent::LockfreeEvent() { Init(); } +LockfreeEvent::LockfreeEvent() { InitEvent(); } -void LockfreeEvent::Init() { +void LockfreeEvent::InitEvent() { /* Perform an atomic store to start the state machine. Note carefully that LockfreeEvent *MAY* be used whilst in a destroyed @@ -69,7 +69,7 @@ void LockfreeEvent::Init() { gpr_atm_no_barrier_store(&state_, kClosureNotReady); } -void LockfreeEvent::Destroy() { +void LockfreeEvent::DestroyEvent() { gpr_atm curr; do { curr = gpr_atm_no_barrier_load(&state_); diff --git a/src/core/lib/iomgr/lockfree_event.h b/src/core/lib/iomgr/lockfree_event.h index b9490f5458b..aec67a3399a 100644 --- a/src/core/lib/iomgr/lockfree_event.h +++ b/src/core/lib/iomgr/lockfree_event.h @@ -34,8 +34,11 @@ class LockfreeEvent { LockfreeEvent(const LockfreeEvent&) = delete; LockfreeEvent& operator=(const LockfreeEvent&) = delete; - void Init(); - void Destroy(); + // These methods are used to initialize and destroy the internal state. These + // cannot be done in constructor and destructor because SetReady may be called + // when the event is destroyed and put in a freelist. + void InitEvent(); + void DestroyEvent(); bool IsShutdown() const { return (gpr_atm_no_barrier_load(&state_) & kShutdownBit) != 0; From bcf083fa9099e5c919f1bdbc7694705665468d3c Mon Sep 17 00:00:00 2001 From: Giang Nguyen Date: Mon, 30 Oct 2017 10:19:17 -0500 Subject: [PATCH 57/70] Add Python support for server SSL cert reloading Previously, a secure server is configured with SSL credentials during initialization, and those credentials will be used for the lifetime of the server. If the user wants the server to use new credentials, the user has to restart the server, resulting in server downtime. This change enables the user to optionally configure the server with a "certificiate config fetcher," such that on every new client connection, the server will call the config fetcher before performing the handshake, allowing the user application to optionally specify new certificate configuration for the server to use (the fetcher can return a "no change" and the server continues to use its current certificate configuration). --- src/python/grpcio/grpc/__init__.py | 85 ++- .../grpc/_cython/_cygrpc/credentials.pxd.pxi | 15 + .../grpc/_cython/_cygrpc/credentials.pyx.pxi | 110 +++- .../grpcio/grpc/_cython/_cygrpc/grpc.pxi | 40 +- .../grpc/_cython/_cygrpc/server.pyx.pxi | 36 ++ src/python/grpcio_tests/tests/tests.json | 4 + .../grpcio_tests/tests/unit/_api_test.py | 19 +- .../unit/_server_ssl_cert_config_test.py | 520 ++++++++++++++++++ .../tests/unit/credentials/README | 1 - .../tests/unit/credentials/README.md | 15 + .../certificate_hierarchy_1/certs/ca.cert.pem | 31 ++ .../intermediate/certs/client.cert.pem | 28 + .../intermediate/certs/intermediate.cert.pem | 31 ++ .../intermediate/certs/localhost-1.cert.pem | 30 + .../intermediate/private/client.key.pem | 27 + .../intermediate/private/localhost-1.key.pem | 27 + .../certificate_hierarchy_2/certs/ca.cert.pem | 31 ++ .../intermediate/certs/client.cert.pem | 28 + .../intermediate/certs/intermediate.cert.pem | 31 ++ .../intermediate/certs/localhost-1.cert.pem | 30 + .../intermediate/private/client.key.pem | 27 + .../intermediate/private/localhost-1.key.pem | 27 + .../grpcio_tests/tests/unit/resources.py | 80 ++- 23 files changed, 1231 insertions(+), 42 deletions(-) create mode 100644 src/python/grpcio_tests/tests/unit/_server_ssl_cert_config_test.py delete mode 100644 src/python/grpcio_tests/tests/unit/credentials/README create mode 100644 src/python/grpcio_tests/tests/unit/credentials/README.md create mode 100644 src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/certs/ca.cert.pem create mode 100644 src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/intermediate/certs/client.cert.pem create mode 100644 src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/intermediate/certs/intermediate.cert.pem create mode 100644 src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/intermediate/certs/localhost-1.cert.pem create mode 100644 src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/intermediate/private/client.key.pem create mode 100644 src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/intermediate/private/localhost-1.key.pem create mode 100644 src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/certs/ca.cert.pem create mode 100644 src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/intermediate/certs/client.cert.pem create mode 100644 src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/intermediate/certs/intermediate.cert.pem create mode 100644 src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/intermediate/certs/localhost-1.cert.pem create mode 100644 src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/intermediate/private/client.key.pem create mode 100644 src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/intermediate/private/localhost-1.key.pem diff --git a/src/python/grpcio/grpc/__init__.py b/src/python/grpcio/grpc/__init__.py index c9f9ac27d91..70558a699a2 100644 --- a/src/python/grpcio/grpc/__init__.py +++ b/src/python/grpcio/grpc/__init__.py @@ -424,6 +424,19 @@ class ServerCredentials(object): self._credentials = credentials +class ServerCertificateConfig(object): + """A certificate config for use with an SSL-enabled Server, e.g., can + be returned in the certificate config fetching callback. + + This class has no supported interface -- it exists to define the + type of its instances and its instances exist to be passed to + other functions. + """ + + def __init__(self, cert_config): + self._cert_config = cert_config + + ######################## Multi-Callable Interfaces ########################### @@ -1252,6 +1265,60 @@ def ssl_server_credentials(private_key_certificate_chain_pairs, ], require_client_auth)) +def ssl_server_certificate_config(private_key_certificate_chain_pairs, + root_certificates=None): + """Creates a ServerCertificateConfig for use with an SSL-enabled Server. + + Args: + private_key_certificate_chain_pairs: A collection of pairs of + the form [PEM-encoded private key, PEM-encoded certificate + chain]. + root_certificates: An optional byte string of PEM-encoded client root + certificates that the server will use to verify client authentication. + + Returns: + A ServerCertificateConfig that can be returned in the certificate config + fetching callback. + """ + if len(private_key_certificate_chain_pairs) == 0: + raise ValueError( + 'At least one private key-certificate chain pair is required!') + else: + return ServerCertificateConfig( + _cygrpc.server_certificate_config_ssl(root_certificates, [ + _cygrpc.SslPemKeyCertPair(key, pem) + for key, pem in private_key_certificate_chain_pairs + ])) + + +def ssl_server_credentials_dynamic_cert_config(initial_cert_config, + cert_config_fetcher, + require_client_auth=False): + """Creates a ServerCredentials for use with an SSL-enabled Server. + + Args: + initial_cert_config (ServerCertificateConfig): the certificate + config with which the server will be initialized. + cert_config_fetcher (callable): a callable that takes no + arguments and should return a ServerCertificateConfig to + replace the server's current cert, or None for no change + (i.e., the server will continue its current certificate + config). The library will call this callback on *every* new + client connection before starting the TLS handshake with the + client, thus allowing the user application to optionally + return a new ServerCertificateConfig that the server will then + use for the handshake. + require_client_auth: A boolean indicating whether or not to + require clients to be authenticated. + + Returns: + A ServerCredentials. + """ + return ServerCredentials( + _cygrpc.server_credentials_ssl_dynamic_cert_config( + initial_cert_config, cert_config_fetcher, require_client_auth)) + + def channel_ready_future(channel): """Creates a Future that tracks when a Channel is ready. @@ -1334,18 +1401,20 @@ __all__ = ('FutureTimeoutError', 'FutureCancelledError', 'Future', 'ChannelConnectivity', 'StatusCode', 'RpcError', 'RpcContext', 'Call', 'ChannelCredentials', 'CallCredentials', 'AuthMetadataContext', 'AuthMetadataPluginCallback', - 'AuthMetadataPlugin', 'ServerCredentials', 'UnaryUnaryMultiCallable', - 'UnaryStreamMultiCallable', 'StreamUnaryMultiCallable', - 'StreamStreamMultiCallable', 'Channel', 'ServicerContext', - 'RpcMethodHandler', 'HandlerCallDetails', 'GenericRpcHandler', - 'ServiceRpcHandler', 'Server', 'unary_unary_rpc_method_handler', - 'unary_stream_rpc_method_handler', 'stream_unary_rpc_method_handler', + 'AuthMetadataPlugin', 'ServerCertificateConfig', 'ServerCredentials', + 'UnaryUnaryMultiCallable', 'UnaryStreamMultiCallable', + 'StreamUnaryMultiCallable', 'StreamStreamMultiCallable', 'Channel', + 'ServicerContext', 'RpcMethodHandler', 'HandlerCallDetails', + 'GenericRpcHandler', 'ServiceRpcHandler', 'Server', + 'unary_unary_rpc_method_handler', 'unary_stream_rpc_method_handler', + 'stream_unary_rpc_method_handler', 'stream_stream_rpc_method_handler', 'method_handlers_generic_handler', 'ssl_channel_credentials', 'metadata_call_credentials', 'access_token_call_credentials', 'composite_call_credentials', 'composite_channel_credentials', - 'ssl_server_credentials', 'channel_ready_future', 'insecure_channel', - 'secure_channel', 'server',) + 'ssl_server_credentials', 'ssl_server_certificate_config', + 'ssl_server_credentials_dynamic_cert_config', 'channel_ready_future', + 'insecure_channel', 'secure_channel', 'server',) ############################### Extension Shims ################################ diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pxd.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pxd.pxi index 41975cbe9e9..bc0f185c77d 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pxd.pxi +++ b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pxd.pxi @@ -28,12 +28,27 @@ cdef class CallCredentials: cdef list references +cdef class ServerCertificateConfig: + + cdef grpc_ssl_server_certificate_config *c_cert_config + cdef const char *c_pem_root_certs + cdef grpc_ssl_pem_key_cert_pair *c_ssl_pem_key_cert_pairs + cdef size_t c_ssl_pem_key_cert_pairs_count + cdef list references + + cdef class ServerCredentials: cdef grpc_server_credentials *c_credentials cdef grpc_ssl_pem_key_cert_pair *c_ssl_pem_key_cert_pairs cdef size_t c_ssl_pem_key_cert_pairs_count cdef list references + # the cert config related state is used only if this credentials is + # created with cert config/fetcher + cdef object initial_cert_config + cdef object cert_config_fetcher + # whether C-core has asked for the initial_cert_config + cdef bint initial_cert_config_fetched cdef class CredentialsMetadataPlugin: diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi index 0fabda19ce6..db813b7243f 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi +++ b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi @@ -14,6 +14,7 @@ cimport cpython +import grpc import threading import traceback @@ -58,12 +59,30 @@ cdef class CallCredentials: grpc_shutdown() +cdef class ServerCertificateConfig: + + def __cinit__(self): + grpc_init() + self.c_cert_config = NULL + self.c_pem_root_certs = NULL + self.c_ssl_pem_key_cert_pairs = NULL + self.references = [] + + def __dealloc__(self): + grpc_ssl_server_certificate_config_destroy(self.c_cert_config) + gpr_free(self.c_ssl_pem_key_cert_pairs) + grpc_shutdown() + + cdef class ServerCredentials: def __cinit__(self): grpc_init() self.c_credentials = NULL self.references = [] + self.initial_cert_config = None + self.cert_config_fetcher = None + self.initial_cert_config_fetched = False def __dealloc__(self): if self.c_credentials != NULL: @@ -254,34 +273,85 @@ def call_credentials_metadata_plugin(CredentialsMetadataPlugin plugin): credentials.references.append(plugin) return credentials -def server_credentials_ssl(pem_root_certs, pem_key_cert_pairs, - bint force_client_auth): - pem_root_certs = str_to_bytes(pem_root_certs) +cdef const char* _get_c_pem_root_certs(pem_root_certs): cdef char *c_pem_root_certs = NULL - if pem_root_certs is not None: + if pem_root_certs is not None: c_pem_root_certs = pem_root_certs - pem_key_cert_pairs = list(pem_key_cert_pairs) + return c_pem_root_certs + +cdef grpc_ssl_pem_key_cert_pair* _create_c_ssl_pem_key_cert_pairs(pem_key_cert_pairs): + # return a malloc'ed grpc_ssl_pem_key_cert_pair from a _list_ of SslPemKeyCertPair for pair in pem_key_cert_pairs: if not isinstance(pair, SslPemKeyCertPair): raise TypeError("expected pem_key_cert_pairs to be sequence of " "SslPemKeyCertPair") + cdef size_t c_ssl_pem_key_cert_pairs_count = len(pem_key_cert_pairs) + cdef grpc_ssl_pem_key_cert_pair* c_ssl_pem_key_cert_pairs = NULL + with nogil: + c_ssl_pem_key_cert_pairs = ( + gpr_malloc( + sizeof(grpc_ssl_pem_key_cert_pair) * c_ssl_pem_key_cert_pairs_count)) + for i in range(c_ssl_pem_key_cert_pairs_count): + c_ssl_pem_key_cert_pairs[i] = ( + (pem_key_cert_pairs[i]).c_pair) + return c_ssl_pem_key_cert_pairs + +def server_credentials_ssl(pem_root_certs, pem_key_cert_pairs, + bint force_client_auth): + pem_root_certs = str_to_bytes(pem_root_certs) + pem_key_cert_pairs = list(pem_key_cert_pairs) cdef ServerCredentials credentials = ServerCredentials() - credentials.references.append(pem_key_cert_pairs) credentials.references.append(pem_root_certs) + credentials.references.append(pem_key_cert_pairs) + cdef char * c_pem_root_certs = _get_c_pem_root_certs(pem_root_certs) credentials.c_ssl_pem_key_cert_pairs_count = len(pem_key_cert_pairs) - with nogil: - credentials.c_ssl_pem_key_cert_pairs = ( - gpr_malloc( - sizeof(grpc_ssl_pem_key_cert_pair) * - credentials.c_ssl_pem_key_cert_pairs_count - )) - for i in range(credentials.c_ssl_pem_key_cert_pairs_count): - credentials.c_ssl_pem_key_cert_pairs[i] = ( - (pem_key_cert_pairs[i]).c_pair) - credentials.c_credentials = grpc_ssl_server_credentials_create( - c_pem_root_certs, credentials.c_ssl_pem_key_cert_pairs, - credentials.c_ssl_pem_key_cert_pairs_count, - GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY if force_client_auth else GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE, - NULL) + credentials.c_ssl_pem_key_cert_pairs = _create_c_ssl_pem_key_cert_pairs(pem_key_cert_pairs) + cdef grpc_ssl_server_certificate_config *c_cert_config = NULL + c_cert_config = grpc_ssl_server_certificate_config_create( + c_pem_root_certs, credentials.c_ssl_pem_key_cert_pairs, + credentials.c_ssl_pem_key_cert_pairs_count) + cdef grpc_ssl_server_credentials_options* c_options = NULL + # C-core assumes ownership of c_cert_config + c_options = grpc_ssl_server_credentials_create_options_using_config( + GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY + if force_client_auth else + GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE, + c_cert_config) + # C-core assumes ownership of c_options + credentials.c_credentials = grpc_ssl_server_credentials_create_with_options(c_options) return credentials +def server_certificate_config_ssl(pem_root_certs, pem_key_cert_pairs): + pem_root_certs = str_to_bytes(pem_root_certs) + pem_key_cert_pairs = list(pem_key_cert_pairs) + cdef ServerCertificateConfig cert_config = ServerCertificateConfig() + cert_config.references.append(pem_root_certs) + cert_config.references.append(pem_key_cert_pairs) + cert_config.c_pem_root_certs = _get_c_pem_root_certs(pem_root_certs) + cert_config.c_ssl_pem_key_cert_pairs_count = len(pem_key_cert_pairs) + cert_config.c_ssl_pem_key_cert_pairs = _create_c_ssl_pem_key_cert_pairs(pem_key_cert_pairs) + cert_config.c_cert_config = grpc_ssl_server_certificate_config_create( + cert_config.c_pem_root_certs, cert_config.c_ssl_pem_key_cert_pairs, + cert_config.c_ssl_pem_key_cert_pairs_count) + return cert_config + +def server_credentials_ssl_dynamic_cert_config(initial_cert_config, + cert_config_fetcher, + bint force_client_auth): + if not isinstance(initial_cert_config, grpc.ServerCertificateConfig): + raise TypeError('initial_cert_config must be a grpc.ServerCertificateConfig') + if not callable(cert_config_fetcher): + raise TypeError('cert_config_fetcher must be callable') + cdef ServerCredentials credentials = ServerCredentials() + credentials.initial_cert_config = initial_cert_config + credentials.cert_config_fetcher = cert_config_fetcher + cdef grpc_ssl_server_credentials_options* c_options = NULL + c_options = grpc_ssl_server_credentials_create_options_using_config_fetcher( + GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY + if force_client_auth else + GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE, + _server_cert_config_fetcher_wrapper, + credentials) + # C-core assumes ownership of c_options + credentials.c_credentials = grpc_ssl_server_credentials_create_with_options(c_options) + return credentials diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi index f115106e605..660263fc09d 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi +++ b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi @@ -391,6 +391,42 @@ cdef extern from "grpc/grpc_security.h": GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY + ctypedef enum grpc_ssl_certificate_config_reload_status: + GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_UNCHANGED + GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_NEW + GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_FAIL + + ctypedef struct grpc_ssl_server_certificate_config: + # We don't care about the internals + pass + + ctypedef struct grpc_ssl_server_credentials_options: + # We don't care about the internals + pass + + grpc_ssl_server_certificate_config * grpc_ssl_server_certificate_config_create( + const char *pem_root_certs, + const grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs, + size_t num_key_cert_pairs) + + void grpc_ssl_server_certificate_config_destroy(grpc_ssl_server_certificate_config *config) + + ctypedef grpc_ssl_certificate_config_reload_status (*grpc_ssl_server_certificate_config_callback)( + void *user_data, + grpc_ssl_server_certificate_config **config) + + grpc_ssl_server_credentials_options *grpc_ssl_server_credentials_create_options_using_config( + grpc_ssl_client_certificate_request_type client_certificate_request, + grpc_ssl_server_certificate_config *certificate_config) + + grpc_ssl_server_credentials_options* grpc_ssl_server_credentials_create_options_using_config_fetcher( + grpc_ssl_client_certificate_request_type client_certificate_request, + grpc_ssl_server_certificate_config_callback cb, + void *user_data) + + grpc_server_credentials *grpc_ssl_server_credentials_create_with_options( + grpc_ssl_server_credentials_options *options) + ctypedef struct grpc_ssl_pem_key_cert_pair: const char *private_key const char *certificate_chain "cert_chain" @@ -440,10 +476,6 @@ cdef extern from "grpc/grpc_security.h": # We don't care about the internals (and in fact don't know them) pass - grpc_server_credentials *grpc_ssl_server_credentials_create( - const char *pem_root_certs, - grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs, - size_t num_key_cert_pairs, int force_client_auth, void *reserved) void grpc_server_credentials_release(grpc_server_credentials *creds) nogil int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr, diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi index b8db27469f9..66565d084b1 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi +++ b/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi @@ -14,8 +14,44 @@ cimport cpython +import logging import time +import grpc +cdef grpc_ssl_certificate_config_reload_status _server_cert_config_fetcher_wrapper( + void* user_data, grpc_ssl_server_certificate_config **config) with gil: + # This is a credentials.ServerCertificateConfig + cdef ServerCertificateConfig cert_config = None + if not user_data: + raise ValueError('internal error: user_data must be specified') + credentials = user_data + if not credentials.initial_cert_config_fetched: + # C-core is asking for the initial cert config + credentials.initial_cert_config_fetched = True + cert_config = credentials.initial_cert_config._cert_config + else: + user_cb = credentials.cert_config_fetcher + try: + cert_config_wrapper = user_cb() + except Exception: + logging.exception('Error fetching certificate config') + return GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_FAIL + if cert_config_wrapper is None: + return GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_UNCHANGED + elif not isinstance(cert_config_wrapper, grpc.ServerCertificateConfig): + logging.error('Error fetching certificate config: certificate ' + 'config must be of type grpc.ServerCertificateConfig, ' + 'not %s' % type(cert_config_wrapper).__name__) + return GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_FAIL + else: + cert_config = cert_config_wrapper._cert_config + config[0] = cert_config.c_cert_config + # our caller will assume ownership of memory, so we have to recreate + # a copy of c_cert_config here + cert_config.c_cert_config = grpc_ssl_server_certificate_config_create( + cert_config.c_pem_root_certs, cert_config.c_ssl_pem_key_cert_pairs, + cert_config.c_ssl_pem_key_cert_pairs_count) + return GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_NEW cdef class Server: diff --git a/src/python/grpcio_tests/tests/tests.json b/src/python/grpcio_tests/tests/tests.json index 8512d5b96fa..e277a3ea1d1 100644 --- a/src/python/grpcio_tests/tests/tests.json +++ b/src/python/grpcio_tests/tests/tests.json @@ -46,6 +46,10 @@ "unit._reconnect_test.ReconnectTest", "unit._resource_exhausted_test.ResourceExhaustedTest", "unit._rpc_test.RPCTest", + "unit._server_ssl_cert_config_test.ServerSSLCertConfigFetcherParamsChecks", + "unit._server_ssl_cert_config_test.ServerSSLCertReloadTestCertConfigReuse", + "unit._server_ssl_cert_config_test.ServerSSLCertReloadTestWithClientAuth", + "unit._server_ssl_cert_config_test.ServerSSLCertReloadTestWithoutClientAuth", "unit._thread_cleanup_test.CleanupThreadTest", "unit.beta._beta_features_test.BetaFeaturesTest", "unit.beta._beta_features_test.ContextManagementAndLifecycleTest", diff --git a/src/python/grpcio_tests/tests/unit/_api_test.py b/src/python/grpcio_tests/tests/unit/_api_test.py index a3351aab504..d877e0f993f 100644 --- a/src/python/grpcio_tests/tests/unit/_api_test.py +++ b/src/python/grpcio_tests/tests/unit/_api_test.py @@ -30,19 +30,22 @@ class AllTest(unittest.TestCase): 'ChannelConnectivity', 'StatusCode', 'RpcError', 'RpcContext', 'Call', 'ChannelCredentials', 'CallCredentials', 'AuthMetadataContext', 'AuthMetadataPluginCallback', - 'AuthMetadataPlugin', 'ServerCredentials', - 'UnaryUnaryMultiCallable', 'UnaryStreamMultiCallable', - 'StreamUnaryMultiCallable', 'StreamStreamMultiCallable', 'Channel', - 'ServicerContext', 'RpcMethodHandler', 'HandlerCallDetails', - 'GenericRpcHandler', 'ServiceRpcHandler', 'Server', - 'unary_unary_rpc_method_handler', 'unary_stream_rpc_method_handler', + 'AuthMetadataPlugin', 'ServerCertificateConfig', + 'ServerCredentials', 'UnaryUnaryMultiCallable', + 'UnaryStreamMultiCallable', 'StreamUnaryMultiCallable', + 'StreamStreamMultiCallable', 'Channel', 'ServicerContext', + 'RpcMethodHandler', 'HandlerCallDetails', 'GenericRpcHandler', + 'ServiceRpcHandler', 'Server', 'unary_unary_rpc_method_handler', + 'unary_stream_rpc_method_handler', 'stream_unary_rpc_method_handler', 'stream_stream_rpc_method_handler', 'method_handlers_generic_handler', 'ssl_channel_credentials', 'metadata_call_credentials', 'access_token_call_credentials', 'composite_call_credentials', 'composite_channel_credentials', - 'ssl_server_credentials', 'channel_ready_future', - 'insecure_channel', 'secure_channel', 'server',) + 'ssl_server_credentials', 'ssl_server_certificate_config', + 'ssl_server_credentials_dynamic_cert_config', + 'channel_ready_future', 'insecure_channel', 'secure_channel', + 'server',) six.assertCountEqual(self, expected_grpc_code_elements, _from_grpc_import_star.GRPC_ELEMENTS) diff --git a/src/python/grpcio_tests/tests/unit/_server_ssl_cert_config_test.py b/src/python/grpcio_tests/tests/unit/_server_ssl_cert_config_test.py new file mode 100644 index 00000000000..d2f9f114a54 --- /dev/null +++ b/src/python/grpcio_tests/tests/unit/_server_ssl_cert_config_test.py @@ -0,0 +1,520 @@ +# Copyright 2017 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. +""" +This tests server certificate rotation support. + +Here we test various aspects of gRPC Python, and in some cases C-core +by extension, support for server certificate rotation. + +* ServerSSLCertReloadTestWithClientAuth: test ability to rotate + server's SSL cert for use in future channels with clients while not + affecting any existing channel. The server requires client + authentication. + +* ServerSSLCertReloadTestWithoutClientAuth: like + ServerSSLCertReloadTestWithClientAuth except that the server does + not authenticate the client. + +* ServerSSLCertReloadTestCertConfigReuse: tests gRPC Python's ability + to deal with user's reuse of ServerCertificateConfig instances. +""" + +import abc +import collections +import os +import six +import threading +import unittest + +from concurrent import futures + +import grpc +from tests.unit import resources +from tests.testing import _application_common +from tests.testing import _server_application +from tests.testing.proto import services_pb2_grpc + +CA_1_PEM = resources.cert_hier_1_root_ca_cert() +CA_2_PEM = resources.cert_hier_2_root_ca_cert() + +CLIENT_KEY_1_PEM = resources.cert_hier_1_client_1_key() +CLIENT_CERT_CHAIN_1_PEM = (resources.cert_hier_1_client_1_cert() + + resources.cert_hier_1_intermediate_ca_cert()) + +CLIENT_KEY_2_PEM = resources.cert_hier_2_client_1_key() +CLIENT_CERT_CHAIN_2_PEM = (resources.cert_hier_2_client_1_cert() + + resources.cert_hier_2_intermediate_ca_cert()) + +SERVER_KEY_1_PEM = resources.cert_hier_1_server_1_key() +SERVER_CERT_CHAIN_1_PEM = (resources.cert_hier_1_server_1_cert() + + resources.cert_hier_1_intermediate_ca_cert()) + +SERVER_KEY_2_PEM = resources.cert_hier_2_server_1_key() +SERVER_CERT_CHAIN_2_PEM = (resources.cert_hier_2_server_1_cert() + + resources.cert_hier_2_intermediate_ca_cert()) + +# for use with the CertConfigFetcher. Roughly a simple custom mock +# implementation +Call = collections.namedtuple('Call', ['did_raise', 'returned_cert_config']) + + +def _create_client_stub( + port, + expect_success, + root_certificates=None, + private_key=None, + certificate_chain=None,): + channel = grpc.secure_channel('localhost:{}'.format(port), + grpc.ssl_channel_credentials( + root_certificates=root_certificates, + private_key=private_key, + certificate_chain=certificate_chain)) + if expect_success: + # per Nathaniel: there's some robustness issue if we start + # using a channel without waiting for it to be actually ready + grpc.channel_ready_future(channel).result(timeout=10) + return services_pb2_grpc.FirstServiceStub(channel) + + +class CertConfigFetcher(object): + + def __init__(self): + self._lock = threading.Lock() + self._calls = [] + self._should_raise = False + self._cert_config = None + + def reset(self): + with self._lock: + self._calls = [] + self._should_raise = False + self._cert_config = None + + def configure(self, should_raise, cert_config): + assert not (should_raise and cert_config), ( + "should not specify both should_raise and a cert_config at the same time" + ) + with self._lock: + self._should_raise = should_raise + self._cert_config = cert_config + + def getCalls(self): + with self._lock: + return self._calls + + def __call__(self): + with self._lock: + if self._should_raise: + self._calls.append(Call(True, None)) + raise ValueError('just for fun, should not affect the test') + else: + self._calls.append(Call(False, self._cert_config)) + return self._cert_config + + +class _ServerSSLCertReloadTest( + six.with_metaclass(abc.ABCMeta, unittest.TestCase)): + + def __init__(self, *args, **kwargs): + super(_ServerSSLCertReloadTest, self).__init__(*args, **kwargs) + self.server = None + self.port = None + + @abc.abstractmethod + def require_client_auth(self): + raise NotImplementedError() + + def setUp(self): + self.server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) + services_pb2_grpc.add_FirstServiceServicer_to_server( + _server_application.FirstServiceServicer(), self.server) + switch_cert_on_client_num = 10 + initial_cert_config = grpc.ssl_server_certificate_config( + [(SERVER_KEY_1_PEM, SERVER_CERT_CHAIN_1_PEM)], + root_certificates=CA_2_PEM) + self.cert_config_fetcher = CertConfigFetcher() + server_credentials = grpc.ssl_server_credentials_dynamic_cert_config( + initial_cert_config, + self.cert_config_fetcher, + require_client_auth=self.require_client_auth()) + self.port = self.server.add_secure_port('[::]:0', server_credentials) + self.server.start() + + def tearDown(self): + if self.server: + self.server.stop(None) + + def _perform_rpc(self, client_stub, expect_success): + # we don't care about the actual response of the rpc; only + # whether we can perform it or not, and if not, the status + # code must be UNAVAILABLE + request = _application_common.UNARY_UNARY_REQUEST + if expect_success: + response = client_stub.UnUn(request) + self.assertEqual(response, _application_common.UNARY_UNARY_RESPONSE) + else: + with self.assertRaises(grpc.RpcError) as exception_context: + client_stub.UnUn(request) + self.assertEqual(exception_context.exception.code(), + grpc.StatusCode.UNAVAILABLE) + + def _do_one_shot_client_rpc(self, + expect_success, + root_certificates=None, + private_key=None, + certificate_chain=None): + client_stub = _create_client_stub( + self.port, + expect_success, + root_certificates=root_certificates, + private_key=private_key, + certificate_chain=certificate_chain) + self._perform_rpc(client_stub, expect_success) + del client_stub + + def _test(self): + # things should work... + self.cert_config_fetcher.configure(False, None) + self._do_one_shot_client_rpc( + True, + root_certificates=CA_1_PEM, + private_key=CLIENT_KEY_2_PEM, + certificate_chain=CLIENT_CERT_CHAIN_2_PEM) + actual_calls = self.cert_config_fetcher.getCalls() + self.assertEqual(len(actual_calls), 1) + self.assertFalse(actual_calls[0].did_raise) + self.assertIsNone(actual_calls[0].returned_cert_config) + + # client should reject server... + # fails because client trusts ca2 and so will reject server + self.cert_config_fetcher.reset() + self.cert_config_fetcher.configure(False, None) + self._do_one_shot_client_rpc( + False, + root_certificates=CA_2_PEM, + private_key=CLIENT_KEY_2_PEM, + certificate_chain=CLIENT_CERT_CHAIN_2_PEM) + actual_calls = self.cert_config_fetcher.getCalls() + self.assertGreaterEqual(len(actual_calls), 1) + self.assertFalse(actual_calls[0].did_raise) + for i, call in enumerate(actual_calls): + self.assertFalse(call.did_raise, 'i= {}'.format(i)) + self.assertIsNone(call.returned_cert_config, 'i= {}'.format(i)) + + # should work again... + self.cert_config_fetcher.reset() + self.cert_config_fetcher.configure(True, None) + self._do_one_shot_client_rpc( + True, + root_certificates=CA_1_PEM, + private_key=CLIENT_KEY_2_PEM, + certificate_chain=CLIENT_CERT_CHAIN_2_PEM) + actual_calls = self.cert_config_fetcher.getCalls() + self.assertEqual(len(actual_calls), 1) + self.assertTrue(actual_calls[0].did_raise) + self.assertIsNone(actual_calls[0].returned_cert_config) + + # if with_client_auth, then client should be rejected by + # server because client uses key/cert1, but server trusts ca2, + # so server will reject + self.cert_config_fetcher.reset() + self.cert_config_fetcher.configure(False, None) + self._do_one_shot_client_rpc( + not self.require_client_auth(), + root_certificates=CA_1_PEM, + private_key=CLIENT_KEY_1_PEM, + certificate_chain=CLIENT_CERT_CHAIN_1_PEM) + actual_calls = self.cert_config_fetcher.getCalls() + self.assertGreaterEqual(len(actual_calls), 1) + for i, call in enumerate(actual_calls): + self.assertFalse(call.did_raise, 'i= {}'.format(i)) + self.assertIsNone(call.returned_cert_config, 'i= {}'.format(i)) + + # should work again... + self.cert_config_fetcher.reset() + self.cert_config_fetcher.configure(False, None) + self._do_one_shot_client_rpc( + True, + root_certificates=CA_1_PEM, + private_key=CLIENT_KEY_2_PEM, + certificate_chain=CLIENT_CERT_CHAIN_2_PEM) + actual_calls = self.cert_config_fetcher.getCalls() + self.assertEqual(len(actual_calls), 1) + self.assertFalse(actual_calls[0].did_raise) + self.assertIsNone(actual_calls[0].returned_cert_config) + + # now create the "persistent" clients + self.cert_config_fetcher.reset() + self.cert_config_fetcher.configure(False, None) + persistent_client_stub_A = _create_client_stub( + self.port, + True, + root_certificates=CA_1_PEM, + private_key=CLIENT_KEY_2_PEM, + certificate_chain=CLIENT_CERT_CHAIN_2_PEM) + self._perform_rpc(persistent_client_stub_A, True) + actual_calls = self.cert_config_fetcher.getCalls() + self.assertEqual(len(actual_calls), 1) + self.assertFalse(actual_calls[0].did_raise) + self.assertIsNone(actual_calls[0].returned_cert_config) + + self.cert_config_fetcher.reset() + self.cert_config_fetcher.configure(False, None) + persistent_client_stub_B = _create_client_stub( + self.port, + True, + root_certificates=CA_1_PEM, + private_key=CLIENT_KEY_2_PEM, + certificate_chain=CLIENT_CERT_CHAIN_2_PEM) + self._perform_rpc(persistent_client_stub_B, True) + actual_calls = self.cert_config_fetcher.getCalls() + self.assertEqual(len(actual_calls), 1) + self.assertFalse(actual_calls[0].did_raise) + self.assertIsNone(actual_calls[0].returned_cert_config) + + # moment of truth!! client should reject server because the + # server switch cert... + cert_config = grpc.ssl_server_certificate_config( + [(SERVER_KEY_2_PEM, SERVER_CERT_CHAIN_2_PEM)], + root_certificates=CA_1_PEM) + self.cert_config_fetcher.reset() + self.cert_config_fetcher.configure(False, cert_config) + self._do_one_shot_client_rpc( + False, + root_certificates=CA_1_PEM, + private_key=CLIENT_KEY_2_PEM, + certificate_chain=CLIENT_CERT_CHAIN_2_PEM) + actual_calls = self.cert_config_fetcher.getCalls() + self.assertGreaterEqual(len(actual_calls), 1) + self.assertFalse(actual_calls[0].did_raise) + for i, call in enumerate(actual_calls): + self.assertFalse(call.did_raise, 'i= {}'.format(i)) + self.assertEqual(call.returned_cert_config, cert_config, + 'i= {}'.format(i)) + + # now should work again... + self.cert_config_fetcher.reset() + self.cert_config_fetcher.configure(False, None) + self._do_one_shot_client_rpc( + True, + root_certificates=CA_2_PEM, + private_key=CLIENT_KEY_1_PEM, + certificate_chain=CLIENT_CERT_CHAIN_1_PEM) + actual_calls = self.cert_config_fetcher.getCalls() + self.assertEqual(len(actual_calls), 1) + self.assertFalse(actual_calls[0].did_raise) + self.assertIsNone(actual_calls[0].returned_cert_config) + + # client should be rejected by server if with_client_auth + self.cert_config_fetcher.reset() + self.cert_config_fetcher.configure(False, None) + self._do_one_shot_client_rpc( + not self.require_client_auth(), + root_certificates=CA_2_PEM, + private_key=CLIENT_KEY_2_PEM, + certificate_chain=CLIENT_CERT_CHAIN_2_PEM) + actual_calls = self.cert_config_fetcher.getCalls() + self.assertGreaterEqual(len(actual_calls), 1) + for i, call in enumerate(actual_calls): + self.assertFalse(call.did_raise, 'i= {}'.format(i)) + self.assertIsNone(call.returned_cert_config, 'i= {}'.format(i)) + + # here client should reject server... + self.cert_config_fetcher.reset() + self.cert_config_fetcher.configure(False, None) + self._do_one_shot_client_rpc( + False, + root_certificates=CA_1_PEM, + private_key=CLIENT_KEY_2_PEM, + certificate_chain=CLIENT_CERT_CHAIN_2_PEM) + actual_calls = self.cert_config_fetcher.getCalls() + self.assertGreaterEqual(len(actual_calls), 1) + for i, call in enumerate(actual_calls): + self.assertFalse(call.did_raise, 'i= {}'.format(i)) + self.assertIsNone(call.returned_cert_config, 'i= {}'.format(i)) + + # persistent clients should continue to work + self.cert_config_fetcher.reset() + self.cert_config_fetcher.configure(False, None) + self._perform_rpc(persistent_client_stub_A, True) + actual_calls = self.cert_config_fetcher.getCalls() + self.assertEqual(len(actual_calls), 0) + + self.cert_config_fetcher.reset() + self.cert_config_fetcher.configure(False, None) + self._perform_rpc(persistent_client_stub_B, True) + actual_calls = self.cert_config_fetcher.getCalls() + self.assertEqual(len(actual_calls), 0) + + +class ServerSSLCertConfigFetcherParamsChecks(unittest.TestCase): + + def test_check_on_initial_config(self): + with self.assertRaises(TypeError): + grpc.ssl_server_credentials_dynamic_cert_config(None, str) + with self.assertRaises(TypeError): + grpc.ssl_server_credentials_dynamic_cert_config(1, str) + + def test_check_on_config_fetcher(self): + cert_config = grpc.ssl_server_certificate_config( + [(SERVER_KEY_2_PEM, SERVER_CERT_CHAIN_2_PEM)], + root_certificates=CA_1_PEM) + with self.assertRaises(TypeError): + grpc.ssl_server_credentials_dynamic_cert_config(cert_config, None) + with self.assertRaises(TypeError): + grpc.ssl_server_credentials_dynamic_cert_config(cert_config, 1) + + +class ServerSSLCertReloadTestWithClientAuth(_ServerSSLCertReloadTest): + + def require_client_auth(self): + return True + + test = _ServerSSLCertReloadTest._test + + +class ServerSSLCertReloadTestWithoutClientAuth(_ServerSSLCertReloadTest): + + def require_client_auth(self): + return False + + test = _ServerSSLCertReloadTest._test + + +class ServerSSLCertReloadTestCertConfigReuse(_ServerSSLCertReloadTest): + """Ensures that `ServerCertificateConfig` instances can be reused. + + Because C-core takes ownership of the + `grpc_ssl_server_certificate_config` encapsulated by + `ServerCertificateConfig`, this test reuses the same + `ServerCertificateConfig` instances multiple times to make sure + gRPC Python takes care of maintaining the validity of + `ServerCertificateConfig` instances, so that such instances can be + re-used by user application. + """ + + def require_client_auth(self): + return True + + def setUp(self): + self.server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) + services_pb2_grpc.add_FirstServiceServicer_to_server( + _server_application.FirstServiceServicer(), self.server) + self.cert_config_A = grpc.ssl_server_certificate_config( + [(SERVER_KEY_1_PEM, SERVER_CERT_CHAIN_1_PEM)], + root_certificates=CA_2_PEM) + self.cert_config_B = grpc.ssl_server_certificate_config( + [(SERVER_KEY_2_PEM, SERVER_CERT_CHAIN_2_PEM)], + root_certificates=CA_1_PEM) + self.cert_config_fetcher = CertConfigFetcher() + server_credentials = grpc.ssl_server_credentials_dynamic_cert_config( + self.cert_config_A, + self.cert_config_fetcher, + require_client_auth=True) + self.port = self.server.add_secure_port('[::]:0', server_credentials) + self.server.start() + + def test_cert_config_reuse(self): + + # succeed with A + self.cert_config_fetcher.reset() + self.cert_config_fetcher.configure(False, self.cert_config_A) + self._do_one_shot_client_rpc( + True, + root_certificates=CA_1_PEM, + private_key=CLIENT_KEY_2_PEM, + certificate_chain=CLIENT_CERT_CHAIN_2_PEM) + actual_calls = self.cert_config_fetcher.getCalls() + self.assertEqual(len(actual_calls), 1) + self.assertFalse(actual_calls[0].did_raise) + self.assertEqual(actual_calls[0].returned_cert_config, + self.cert_config_A) + + # fail with A + self.cert_config_fetcher.reset() + self.cert_config_fetcher.configure(False, self.cert_config_A) + self._do_one_shot_client_rpc( + False, + root_certificates=CA_2_PEM, + private_key=CLIENT_KEY_1_PEM, + certificate_chain=CLIENT_CERT_CHAIN_1_PEM) + actual_calls = self.cert_config_fetcher.getCalls() + self.assertGreaterEqual(len(actual_calls), 1) + self.assertFalse(actual_calls[0].did_raise) + for i, call in enumerate(actual_calls): + self.assertFalse(call.did_raise, 'i= {}'.format(i)) + self.assertEqual(call.returned_cert_config, self.cert_config_A, + 'i= {}'.format(i)) + + # succeed again with A + self.cert_config_fetcher.reset() + self.cert_config_fetcher.configure(False, self.cert_config_A) + self._do_one_shot_client_rpc( + True, + root_certificates=CA_1_PEM, + private_key=CLIENT_KEY_2_PEM, + certificate_chain=CLIENT_CERT_CHAIN_2_PEM) + actual_calls = self.cert_config_fetcher.getCalls() + self.assertEqual(len(actual_calls), 1) + self.assertFalse(actual_calls[0].did_raise) + self.assertEqual(actual_calls[0].returned_cert_config, + self.cert_config_A) + + # succeed with B + self.cert_config_fetcher.reset() + self.cert_config_fetcher.configure(False, self.cert_config_B) + self._do_one_shot_client_rpc( + True, + root_certificates=CA_2_PEM, + private_key=CLIENT_KEY_1_PEM, + certificate_chain=CLIENT_CERT_CHAIN_1_PEM) + actual_calls = self.cert_config_fetcher.getCalls() + self.assertEqual(len(actual_calls), 1) + self.assertFalse(actual_calls[0].did_raise) + self.assertEqual(actual_calls[0].returned_cert_config, + self.cert_config_B) + + # fail with B + self.cert_config_fetcher.reset() + self.cert_config_fetcher.configure(False, self.cert_config_B) + self._do_one_shot_client_rpc( + False, + root_certificates=CA_1_PEM, + private_key=CLIENT_KEY_2_PEM, + certificate_chain=CLIENT_CERT_CHAIN_2_PEM) + actual_calls = self.cert_config_fetcher.getCalls() + self.assertGreaterEqual(len(actual_calls), 1) + self.assertFalse(actual_calls[0].did_raise) + for i, call in enumerate(actual_calls): + self.assertFalse(call.did_raise, 'i= {}'.format(i)) + self.assertEqual(call.returned_cert_config, self.cert_config_B, + 'i= {}'.format(i)) + + # succeed again with B + self.cert_config_fetcher.reset() + self.cert_config_fetcher.configure(False, self.cert_config_B) + self._do_one_shot_client_rpc( + True, + root_certificates=CA_2_PEM, + private_key=CLIENT_KEY_1_PEM, + certificate_chain=CLIENT_CERT_CHAIN_1_PEM) + actual_calls = self.cert_config_fetcher.getCalls() + self.assertEqual(len(actual_calls), 1) + self.assertFalse(actual_calls[0].did_raise) + self.assertEqual(actual_calls[0].returned_cert_config, + self.cert_config_B) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_tests/tests/unit/credentials/README b/src/python/grpcio_tests/tests/unit/credentials/README deleted file mode 100644 index cb20dcb49fb..00000000000 --- a/src/python/grpcio_tests/tests/unit/credentials/README +++ /dev/null @@ -1 +0,0 @@ -These are test keys *NOT* to be used in production. diff --git a/src/python/grpcio_tests/tests/unit/credentials/README.md b/src/python/grpcio_tests/tests/unit/credentials/README.md new file mode 100644 index 00000000000..100b43c1aaf --- /dev/null +++ b/src/python/grpcio_tests/tests/unit/credentials/README.md @@ -0,0 +1,15 @@ +These are test keys *NOT* to be used in production. + +The `certificate_hierarchy_1` and `certificate_hierarchy_2` contain +two disjoint but similarly organized certificate hierarchies. Each +contains: + +* The respective root CA cert in `certs/ca.cert.pem` + +* The intermediate CA cert in + `intermediate/certs/intermediate.cert.pem`, signed by the root CA + +* A client cert and a server cert--both signed by the intermediate + CA--in `intermediate/certs/client.cert.pem` and + `intermediate/certs/localhost-1.cert.pem`; the corresponding keys + are in `intermediate/private` diff --git a/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/certs/ca.cert.pem b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/certs/ca.cert.pem new file mode 100644 index 00000000000..604b86fdff3 --- /dev/null +++ b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/certs/ca.cert.pem @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFZDCCA0ygAwIBAgIJAKfkDFZ6+Ly/MA0GCSqGSIb3DQEBCwUAMD8xCzAJBgNV +BAYTAnVzMQ4wDAYDVQQIDAVkdW1teTEOMAwGA1UECgwFZHVtbXkxEDAOBgNVBAMM +B3Jvb3QgY2EwHhcNMTcxMTAyMDAzNzA1WhcNMzcxMDI4MDAzNzA1WjA/MQswCQYD +VQQGEwJ1czEOMAwGA1UECAwFZHVtbXkxDjAMBgNVBAoMBWR1bW15MRAwDgYDVQQD +DAdyb290IGNhMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxlSUuSbi +o66tT2ZCqu9wNqSX8VhAJkmrAT5y6m2V0VlQ8Gz7ddynW5UVSmtvDNTebZ15FrvO +6Ng7QnwXXNs/dEzl6oMe6AKDZpuWScVkiqH1UYWBkMLRygWCTEYpSTWTpZWk1zxj +DJ2LlIoO1X/ufLyLOfy2a2XEz8ICzJePmqVca6fmfEtCTj1/8FcwCBF6YlUWVzlR +wewjanQo/lorTYbub+Q6LGxPXZ8W0qoKZzLDSD9cnj4pcJzGGFeu9KkNaW4rldZG +t7mTGQqIRc98dDRc9Jb7PqL8tMPLidw1KErUi05ofxggc5vqNnj4xBl6aX6b/EYN +rBLzO2e0FazX6TwNKwwg68vbOanpDq5LVmIUH8bY1zNZ+JPBGO9pXlAA0YwLx86r +R7YhQ431ZpJ2KGnYjVhYnZ2L3NjV3UYX3x5Z3OrDj9hybhucJB48DMQ1+loEabwK +fSUJtcSPc8dCIibxVKidBFgaTPXtHy2MPXuhMhR7PCtMpE7RPUoYmdZLr9FNN1ty +/RAbwBfuhGLbRI2qqJgbOzHJHaOY/FtShfooLz7lt4LIjPTARaNsulG2rbv+m3z9 +mhNjL+peV8gni/xyOYYTbdzZagLrtSHeTWsITvmVt0flMHkjHyv35rw23+hBlSjp +6+S+0MmwuwxqBBccBSlZ9t3Xh1N+vFkb2UkCAwEAAaNjMGEwHQYDVR0OBBYEFJWE +tQwTbTCgZWNN08VSxjdNA0oaMB8GA1UdIwQYMBaAFJWEtQwTbTCgZWNN08VSxjdN +A0oaMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB +CwUAA4ICAQCNjPv/e3ozX1PuN5Tluf0yOmKCxKVCK3Pp98WkDzH4Rp1urEeYrGJL +vBNcl17avOJ0e+zTVYPXFviFbsBsU/zaf+TqEujXabsdL+nvvCJ2mMqYn4wyDFjS +zDNbGH6O0ENZz5NSY0/UGSOHYrYnYB94QRFLbbf0Y3PmBS2eRNjIUnv7ytPZNMi/ +piM+QhPb0Ebyk0rHQZ0RAJaC/wsEtqP8TGV/fx+AzG7zW/zxgPTrgIThy138tLQ+ +xCVDP9H2c17nVP6vjYzKnMZ94uGrGqUzV9vU7EqYl0uZflIf98pLfdKHnQ3heqds +8KQPNKRxVvcc92qv2pQY951wb1fkhLutjHn7TUvrenyAngz+Vs19NxbqLPys1CTw +iaL7vZ8VE/aEDm1tjt5SLM474tpATjk1+qMRaWnii8J5rTodYHP+Zu2GxyIrMiGq +tfNZMYI0tETK1XmEo75E/3s9pmIeQNGKLFp+qL7xrVyN/2ffNv0at8kkqXluunK9 +/Ki0gKYlGFm4Eu8t/nHMqhBx/njYg6pLDuarLW6ftUV7aHd7qKcCWOWqK6gnH/vX +3Apv31eltZBBVN69p3CFy2oMnjrom2Yn/DUXFwrJLBiNJ1dd1JyDxpqpJ74ZQy+/ +pSRWMTRM5SuC7lYARx5rYPmp6cZJWyWRH/3r7bwS699/W965pa5nug== +-----END CERTIFICATE----- diff --git a/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/intermediate/certs/client.cert.pem b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/intermediate/certs/client.cert.pem new file mode 100644 index 00000000000..44bc5625990 --- /dev/null +++ b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/intermediate/certs/client.cert.pem @@ -0,0 +1,28 @@ +-----BEGIN CERTIFICATE----- +MIIExzCCAq+gAwIBAgICEAMwDQYJKoZIhvcNAQELBQAwRzELMAkGA1UEBhMCdXMx +DjAMBgNVBAgMBWR1bW15MQ4wDAYDVQQKDAVkdW1teTEYMBYGA1UEAwwPaW50ZXJt +ZWRpYXRlIGNhMB4XDTE3MTEwMjAwMzcwNloXDTI3MTAzMTAwMzcwNlowPjELMAkG +A1UEBhMCdXMxDjAMBgNVBAgMBWR1bW15MQ4wDAYDVQQKDAVkdW1teTEPMA0GA1UE +AwwGY2xpZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwquL6gtP +R7P9xJK76FTj8fI5TSJa3cAMt1p6CmessjHQq7nQ6DWLGVi4XIt9Sc/1C3rXupOe +90Ok4L0tsuVZH78Wn0EBmBH7S4IbhU9P+aJ9mcigepj1lnxWqoVblgeJYKMOOwAf +pAKUNMWDSm+nCfwE+R5d8d8cfA41Awq1jTRjOVpiJq6aoKfs791a1ZkZde3kFrNV +AVjC06GgA1lZd3sHf94hmLeC+xJztRXVE9e+7dcc7nFDH0t5DIKYBAklsHg77mZa +3IK4aOZew7Lm6diPoMnAzXh2rWpJU6RrEE29gIkJBsF8CL1Ndg9MzssCg6KBjoai +Vt5dJ+4TSEGCOwIDAQABo4HFMIHCMAkGA1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQD +AgWgMDMGCWCGSAGG+EIBDQQmFiRPcGVuU1NMIEdlbmVyYXRlZCBDbGllbnQgQ2Vy +dGlmaWNhdGUwHQYDVR0OBBYEFPeuKDCswk8jaH9tl6X+uXjo+WM1MB8GA1UdIwQY +MBaAFCoqYgmKh3CUafVp+paXxfz+He+FMA4GA1UdDwEB/wQEAwIF4DAdBgNVHSUE +FjAUBggrBgEFBQcDAgYIKwYBBQUHAwQwDQYJKoZIhvcNAQELBQADggIBADYAp8XS +UjMEpX/zVjRWpAAT4HNEJylCV1QNyhBIAyx38A6xJYuFIx966Htd6W9/Rw4sUY6y +F4pOmnLCRxIPqFzzMYcBHSpynlcu5G7zqIod3wYIk7BNlB0AzkZn6yD8bM1y5Plf +myzQVDEGggrDtaW2EehhNIB+wOmbRGITjIcZUEr8V4BlLXkCqOuxauxl82d5/k2w +LAhjOb9d1VW6RT8+Lcn6drhHZdvtSCe8Z27BcXhaQLL8366mhbigKYJt5adD0KOx +pl0MQcoL1Rth5cJEj+1/lgUaxcnvh7TaIIGEx0h3olQXsTxSTypU/nww2Ic41xdG +xl3xvHsxe20IvOOAMRfS/LPW7MCtQ3k0BqB/rAQvmB0r5YITLlMJuBqg+zjYrG/j +s5szSGAz9r0leFuPraeuZA41d9UBTAJMoVrrQZ4xVHMXQi1oz9E9KlIdbO9+spvC +ulfO+D+Z4a9trYSWhnQL2dSHT0+kHqJ/8GipiUNP/yAC76dRpDVR3xtYNr73iw0j +hyDsVjihTD8JBebs3axnt+Bc+FwoCCd6CVcsggfGUNhu/N5LS78b13PcaRzrUNjU +Eh+8cJvMLst+UQzePlyazzpn7jjN3KsBzWUkbnXCtUs2qRMn8f2gZqliDo7JSFvy +WtBSCYpikOivuJSQUlrHQ8NaXeddyWQzLY79 +-----END CERTIFICATE----- diff --git a/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/intermediate/certs/intermediate.cert.pem b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/intermediate/certs/intermediate.cert.pem new file mode 100644 index 00000000000..98e13669c0f --- /dev/null +++ b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/intermediate/certs/intermediate.cert.pem @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFaDCCA1CgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwPzELMAkGA1UEBhMCdXMx +DjAMBgNVBAgMBWR1bW15MQ4wDAYDVQQKDAVkdW1teTEQMA4GA1UEAwwHcm9vdCBj +YTAeFw0xNzExMDIwMDM3MDZaFw0yNzEwMzEwMDM3MDZaMEcxCzAJBgNVBAYTAnVz +MQ4wDAYDVQQIDAVkdW1teTEOMAwGA1UECgwFZHVtbXkxGDAWBgNVBAMMD2ludGVy +bWVkaWF0ZSBjYTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOOxzve7 +CG2P9SvKfXkJVTXkj4y79JSZ77Kud/TiPfDbHTqZWKuLTXkOCkCCxfpuJvWXnnj5 +1AeLCdKx9hEwJQeU23EXDt1K+RsRyl09SXtPNnJnqHD1mUHRQR28vGX5ctrQzK8J +Sa6/mHW4bX8ol100npbgVMDnM4IDfLYcsv4BXMICGkSHOW6Gn0zJaeHzRVPpmnK/ +0k/GQAcIrU2sZ39kVlVQkWq3HJC28cNL/P04hjh4gAf0evo/k9VrEtxPWYMfiPDt +kOAKueoPv/VTA/zL5t8lyzfhrhxvsJxFg/klapPXK0gLLbhsHyOhnkbrzvmSR4Rw +xubYJ2dDK0DKx+BIZqlFznjP9BvOtvtuVVMyqg9cfgc7J/OjvAguO0f93MLSfIWP +uISqv7Llt/Blvy/xI5owvOKVc/hm3d+5pqjWBC1HkVwo4qugpWmM49dFWl4kc4c7 +ayYUjTmcgoj1ZR89w4Off/bPd1A6gXqSkw2VQfgFF+uOos84fP1V+zPWhp3UDY3P +bFeJtuTdv1gR5w1jCIq6xVJ+UsyDZBaYP7yBBRiNzS1/yXJpnXrvHmDfUeQHLBPR +N0nbMjqXJ1dVpZwydiI0Qx9DnJtOaq/spUreXr8+PU2jeQdCCAN21MB1umr2gZBJ +8MZBStTgE7SDByfGmGfp7B5/s/r4O/rNc4WzAgMBAAGjZjBkMB0GA1UdDgQWBBQq +KmIJiodwlGn1afqWl8X8/h3vhTAfBgNVHSMEGDAWgBSVhLUME20woGVjTdPFUsY3 +TQNKGjASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG +9w0BAQsFAAOCAgEAvzLu/Jc8DlfCltVufC54UZ8DVwUfxdGapNBGv4icrs1wMV3S +xqdaLO+vSp9NeEufi724+/hj4URapW9kSt2TMD7BNJ61QSATZFJajxTFgGa0Zz95 +RBDw8/b5Arz/2pOF4VX+FJ+wqHvoH/2A0T+fwz8hLORhxZHv/cUN6kif4FKCwryQ +s89e694kXkEiJfquvu7DR9hYCLOJwzMOOJiTnjz3hlQg4WGu7Z8ZvqzCM+how1hr +nYbUx6a+HfoUf79AHJB0N1EsEEetJ+omvTdrrayCvy1bHA3QgHlJ28QZIJ7MzX9E +n11/xQ95iTuSp8iWurzjTjbrm7eHnGUh+5QubYLXOzbqKzNZu72w0uvWv6ptIudU +usttltiwW8H9kP0ArWTcZDPhhPfS9impFlhiPDk1wUv2/7g+Zz1OaOb7IiSH0s8y +FG72AB8ucJ5dNa/2q5dJiM8Gm5CbiVw5RXTBjlfTTkNeM6LBI3dRghpPdU7Kbfhn +xYs9vnRZeRMJHrcodLuwVcpY/gyeJ0k5LD6eIPCJmatkYZ122isYyMX8lL2P5aR+ +7d2hhqcOCproOrtThjp6nW2jWTB+R/O2+s6mhKSPgfbY2cdky1Y9FSJxSNayb9B8 +eQ+A29iOHrGVAA0R/rvw119rLAYxjXzToM28owx7IyXKrBaU4RMv5yokgag= +-----END CERTIFICATE----- diff --git a/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/intermediate/certs/localhost-1.cert.pem b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/intermediate/certs/localhost-1.cert.pem new file mode 100644 index 00000000000..f15f1cf5c2f --- /dev/null +++ b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/intermediate/certs/localhost-1.cert.pem @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFFzCCAv+gAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwRzELMAkGA1UEBhMCdXMx +DjAMBgNVBAgMBWR1bW15MQ4wDAYDVQQKDAVkdW1teTEYMBYGA1UEAwwPaW50ZXJt +ZWRpYXRlIGNhMB4XDTE3MTEwMjAwMzcwNloXDTI3MTAzMTAwMzcwNlowTTELMAkG +A1UEBhMCdXMxDjAMBgNVBAgMBWR1bW15MQ4wDAYDVQQKDAVkdW1teTEKMAgGA1UE +CwwBMTESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEArRAy0Nim9P883BAisXdFoKmgHGTtcLH/SzwkkPWTFHz0rHU1Klwz +w8u3OkRyvgoQp7DqkohboNMDwg5VrOOcfKwtM2GZ5jixo+YKvJ25oj8Jfr+40baz +nyWTmOcfoviKrb7u2T9BPEEz5og+lXRDAsTFATGaQDX2LN3Dd9KIw+7sWY+gc3Zi +13HHaWYhtmfJjzFbH1vDxHKCdSdgtPyEhqcJ4OC6wbgp/mQ01VlPAr08kRfkC8mT +TS7atqc410irKViF3sWi4YNPf7LuBrjo75FIIOp+sQgZE6xwOuZ/9bT2Zx/IUtCC +TqzVgZI0s5NVlINtWR6eyyxQ1uDKTs4xrQIDAQABo4IBBTCCAQEwCQYDVR0TBAIw +ADARBglghkgBhvhCAQEEBAMCBkAwMwYJYIZIAYb4QgENBCYWJE9wZW5TU0wgR2Vu +ZXJhdGVkIFNlcnZlciBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUDE8pwi7aELJjvyNT +ed81/KIgGfowaAYDVR0jBGEwX4AUKipiCYqHcJRp9Wn6lpfF/P4d74WhQ6RBMD8x +CzAJBgNVBAYTAnVzMQ4wDAYDVQQIDAVkdW1teTEOMAwGA1UECgwFZHVtbXkxEDAO +BgNVBAMMB3Jvb3QgY2GCAhAAMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggr +BgEFBQcDATANBgkqhkiG9w0BAQsFAAOCAgEA2cvXxJw120Z9oWXyGwR6CH7TcXoy +1i77B1M5j0Krvkjh2/MkEU+JxpZcrhAgZODK9wMPeIUIpJNw2t6Hg+vigpInu7pY +MXR4IA5XLnhGV/hueXa0JLia5FG1TISxr4piW6Jd9P2pOt3ECm3Url/F0OeFF/74 +jGaAlWkbhqWJ9M7Gd4QP2wUNm0P4CwAqS9DC6dnMz+JXTakEUirOpmq7U8UKT+5N +QS1K4WuH671n4MiYye3+UoRYt4zPjOzN+QxzvAMtkUBspPmWD6txmD5tKUYDECqn +0sSbY6ytD30OTHIbICFp40arOffmEEJSriL+uQNPPmvqMxX1G2kUFGm15NLPs8Xa +J7ChrAaJzssN5J3myZUbDfCuxmTkWg+hGvGmxLraVNWc3fzKFmdszSkXrGIdf2HR +gZeFI3w6M4Ktx3KctXlsjwqQTYZI/WwLOEpsrHQBPBLQhISyNw4xjZ4MxK8SFZuQ +IiGps/do0eEgeQ+o3gD1dIXt8YxFIxrgk0pzJONpXGgv/cZrukbLNTBdkTSkIwtx +TXKdiJbO17H24MvW+UxFdsIoJXmfQZWdQC3p+Dl0iP+K80aI6WbaysmToHuOi216 +e49nmiM72Izul2zmBi7Cq2nRQbHAETsFfqC34FzJlx0aP8WS953IBD0jNi1BB+AX +BxwiZ1rPjeMvekI= +-----END CERTIFICATE----- diff --git a/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/intermediate/private/client.key.pem b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/intermediate/private/client.key.pem new file mode 100644 index 00000000000..d8a21632ab4 --- /dev/null +++ b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/intermediate/private/client.key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAwquL6gtPR7P9xJK76FTj8fI5TSJa3cAMt1p6CmessjHQq7nQ +6DWLGVi4XIt9Sc/1C3rXupOe90Ok4L0tsuVZH78Wn0EBmBH7S4IbhU9P+aJ9mcig +epj1lnxWqoVblgeJYKMOOwAfpAKUNMWDSm+nCfwE+R5d8d8cfA41Awq1jTRjOVpi +Jq6aoKfs791a1ZkZde3kFrNVAVjC06GgA1lZd3sHf94hmLeC+xJztRXVE9e+7dcc +7nFDH0t5DIKYBAklsHg77mZa3IK4aOZew7Lm6diPoMnAzXh2rWpJU6RrEE29gIkJ +BsF8CL1Ndg9MzssCg6KBjoaiVt5dJ+4TSEGCOwIDAQABAoIBABECKAFU56JeKYfp +Qh20fQ4Amd0RaVsCkpnaf9s037PaAl9eptADDZozVDhRv6qZTtGn8/1LNJJqCJfS +L5H30+egLHvRlDATMh+QyJLHMTegaNTs4IiVoK97QZ84c54SHoCg/ndNNXaA+y35 +K9VvF+sZZ93UN2UQl06Hdz5Cy0YA7L5HIIH3Ezk0ArAw4AarLil5mv4yEz2ApZhm +Tw4I4yNfxB7tZeP+ekNg0XXRL1quA0tGblp+A5fAFfVMDplqqB2d3/KxPR9FSEOi +4PzBZ5Mq2wQBPIaNog5um9qkw6VKxjl5sQGhP1GGTA8iZqR9iM2+xh57xdCZm3g3 +jcr+aPECgYEA42mXTsF/4oBQtU6hh/sOCMWHhxAPstKpQHFMKGYLHKEJ/V1qq0Sd +d0kswAYCmH5G9ookzu5p7pNf0hUUHO5EwelpSZ3FEmtIM+oBwSnDk3vGuadYXN5X +fPuVUla65B1F9SSwapYNBUAiRgrY69Knca2rkTSdcZQaBuWmo684UQcCgYEA2yRE +P23I/9N6AVhKB/zTRtil1AxnTW8o+j7AE4q1o+xly7DS7DT34INaLKLiuG6ylV1F +UoTiqmWqH3A7m3o3Id2AnVf/oDoKV78LCXRF3dJJWvzrPdob2fLlwyjgqXYvmD3O +UH/OFY2blYcAHOYib1Y1AAhHPlXiHA52BYZtnC0CgYAVjjitWmII0ijURrPA8+cM +pcyG3NrgFF++n/6cBbAf8pPD1Er8GPDkEaeQPAGa+r03OTjr9GVOG+IFQ8I4S81w +o/M66x129XxOj2vDJ3ZGUIExr88MXnbkfeRVfasRXET5S5T9RWPOj5mwEe8lyz3b +5J5SkS4rSeJ9rN7yvPUVmQKBgAvrrB67LRzldxSNpfFLSn7nGBYx2oi2zEbYlQA7 +ImhZWqw64S5iLz2yR3x4G9cmhmZjnXrAqcfVIez14PgzLL6V2wI0ID6qCZf+V25b +OdW4M69UZMOHks5HTUJRfe8Z87rXWdq9KQu5GUaIAnSP/D2MNfPbf2yfpV4bV0Yz +qtC9AoGAD3/XXaeGCdV5DPomEmehp84JXU2q/YECRvph46tr4jArG67PCvx2m84B ++W6my4Yi7QJcW4gC0gsdAuxbJl4Y7MCZBnTtNIRCRnHEIciKITJ/+brFln5QUgyn +WnXEPN8q7VjSVXGrljFuLWkzi2Vh8iZDgourNfW+iYDGCJjx1H0= +-----END RSA PRIVATE KEY----- diff --git a/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/intermediate/private/localhost-1.key.pem b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/intermediate/private/localhost-1.key.pem new file mode 100644 index 00000000000..aa83f1a4a2c --- /dev/null +++ b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/intermediate/private/localhost-1.key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEArRAy0Nim9P883BAisXdFoKmgHGTtcLH/SzwkkPWTFHz0rHU1 +Klwzw8u3OkRyvgoQp7DqkohboNMDwg5VrOOcfKwtM2GZ5jixo+YKvJ25oj8Jfr+4 +0baznyWTmOcfoviKrb7u2T9BPEEz5og+lXRDAsTFATGaQDX2LN3Dd9KIw+7sWY+g +c3Zi13HHaWYhtmfJjzFbH1vDxHKCdSdgtPyEhqcJ4OC6wbgp/mQ01VlPAr08kRfk +C8mTTS7atqc410irKViF3sWi4YNPf7LuBrjo75FIIOp+sQgZE6xwOuZ/9bT2Zx/I +UtCCTqzVgZI0s5NVlINtWR6eyyxQ1uDKTs4xrQIDAQABAoIBAC56mDswxH4uAmlT +yA2Da+a/R6n4jTBkDZ1mFKf93Dd3a7rZa6Lpylk+YAI9GdfiGiD/SbB7AKjLo0m9 +0dKx+ngdQbJ39v42obbT9HQ9o/poFaO91+QyvkDytZYuFHgPaidJjRo5e8qz9D1o +v+4hoFGhCQvOB5BRLcFU+cc3etWr5t61sNL/kKCWEDd+MWWsOCHpdhEoWC+o25pC +bhD3FG5xoz+8zL7WdNfke/4twfKoBJ/kq89bfIkl8eKpg387WBQY44RJF7/zVr7a +9dsUuW2y/wVXslCHChjSrxhRlOyy5ssv3EgKh8gPkZ+oeKuONqAGw27nyKyvpjxS +i62K+WECgYEA4oKpIS2D77RCC6rpYIK6KYfbUcNSOtHFvcbf0RH9Oi8vSRYum2ZA +/ITdWSFgWkhT6iOSPuvZlu/EvueWDgNgW1ZVsTMFeapz1+Jwk7JRoBKF1dUEwELh +jdAswdh0MLbgBYs6NXtVVkeK2ocgZtosmt1PUktl566NlyIyhOjH6vkCgYEAw5g0 +cteTpz+noKsfWcbnjyesuQy0caICfZIE01nKv9rKTF8BtCO6Qxj10iM2o00jW7Vl +tZa/igjuqvozXAHBI3xegtrWV05urkjj3FB/Pyuqsx3wxhAdSNchQjdTjwUBQEzp +3ztGSlDTRPpijnpW28lg8Kkr3weryaHvl0xM1VUCgYBqnTN8QU8rgT3g/gYw/fcf +2ylY98V5mAkqBTSN1JjLTTBFh2JSlLOb5/HDpRkUBZ0xxKJuaVaWW67QaHLRj7dH +5oAZErnOBXPXNmbkrfcLkAxclJJS6Gf/9u9KIla2Iy2YjmrMh4uoO65Yo2eV4bVD +A031nzWM8jUE4PzEYEjRCQKBgHDdTj6KiQg0Yg0DUabjcNEZasCpRSJhAyDkdmZi +5OzKWnuxQvFowF1hdM/aQ/f9Vg7gYJ1lLIeBWf9NOv+3f3RzmrHVh2N/vbxSETIb +PSH9l5WeDEauG8fhY66q8EuR7sPk3ftTX98YPqEJ/n8Ktz5COO8GH2umKInEKNXc +UGW1AoGAfENy7vInNv0tzFWPSYdFgesvzo7e8mXyVO8hCyWvY3rxW2on7qfLF3Z9 +fHjd7P9gULja0n1kvmxwUC3u20RrvpY59F4hfi+ji2EiubS9Yhszd2e1CLeRMkln +ojDjnflN32ZbWVHX/i6g3Dabq9JOD0FsOaOlriLMuofdA6jTUFE= +-----END RSA PRIVATE KEY----- diff --git a/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/certs/ca.cert.pem b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/certs/ca.cert.pem new file mode 100644 index 00000000000..212b5862cb2 --- /dev/null +++ b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/certs/ca.cert.pem @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFZDCCA0ygAwIBAgIJALhSfZ8i0rWTMA0GCSqGSIb3DQEBCwUAMD8xCzAJBgNV +BAYTAnVzMQ4wDAYDVQQIDAVkdW1teTEOMAwGA1UECgwFZHVtbXkxEDAOBgNVBAMM +B3Jvb3QgY2EwHhcNMTcxMTAyMDAzNzU4WhcNMzcxMDI4MDAzNzU4WjA/MQswCQYD +VQQGEwJ1czEOMAwGA1UECAwFZHVtbXkxDjAMBgNVBAoMBWR1bW15MRAwDgYDVQQD +DAdyb290IGNhMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEArHaQ3uyp +wVaVPZDYvy/EJbnP7KbZNPBvKpQCDEqg9B2TPaC8WVjiqT6I88c+KcGuB8nADJdk +o10iZC5AwbrXK4ELSCOvBpxYI5sjoFcv3lZ/oe4e650QO2L/ADmtwLaLYK6rZkwW +Sd90yCGF7ZOTZTJZDwmWEl+ohi+2ow6sRMHKcSKUNfx9G5BB7TOzoqUxqH+moEds +YpjVMEcKzQi2FmbRd+8Dlg2eGqA2V4faprGQwoYz8NqJZGa/KPpRvXE2VjSTDN6b +rJ7mmui6eYN53mZEBRYogyoQHdFXhK02FgyoPEgR/wQlLLbQ+xxOcv02YsOljtza +hl5LjeNUYPMjyhef0QpONp+5NcFhZf38DsSq5EWZLLxPScxwl0lBQkJTjo5ARuFl +Mrv50RYrLwv4ImsiO2ftE7gAX4vNsgcixnCHd6rNzoGimf1+DSvDVJ9ujWo7HPN3 +7ONuoyjsU4mUJJpYXs8zHx5WSxaYiPJRcmG3LjcU5/A+Fs7bkqSrlEjJsG29xDrO +vKR7hH+m6MwcIcXSh9wjjAIvHxAALdU9xaYE3hmVkoxew1mRBsYq34h2vpwGOY5r +0njRQyGGZnVa8qkQd6P3U5fcvLOM8v9QImZqRDS2jAGZXYruo/RIgJpklVX7ZY0+ +CnGdz4YxgLyOBJCDu3aEgL1oON3mg2SsrVMCAwEAAaNjMGEwHQYDVR0OBBYEFOBO +9R6yEY6KOE+aSClwD2BQtWXKMB8GA1UdIwQYMBaAFOBO9R6yEY6KOE+aSClwD2BQ +tWXKMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB +CwUAA4ICAQBElio7lLZ2eNt0tDPVSkChksc0IJ2/DpjYjqJzqxF/jZ2oToaAn2Er +9iHl8ggTLB5WrTQxO1ev7qOwQsk9hrgvZ+EQCBTbyDyfNwVjgftH5jdQGdbyrnuJ +yaks1mnd8is5ZZQmmQSd7GVOMTYi/7yH0xI4DHQ386dwnf5eKdoOI7yrVufEMxRx +tB3Wv8KrX4z47zsIO28H/O0T26oUkrd4PEqFwDa5HQr6iG7QQgoGD/DPLgbBudlO +kEos9fmXxiX60RLziKCE/DAxI3YWPvG3WhIWnVj22Oz6apz2pYWpOKwlaihNYrhq +8xc02vIFwKh+t7D+wF4KHfduyMJ/wKVc5dzpNbTgkZePPKSB7QgbsMeRqbdPoXQF +pMuzfj8VCWzpqBeHqE/adSCZhzeTrnyiYavF4T2vkSC5KJu+MHmbZ3nU9bcnnEy+ +24oEv9cEAiYNkvftbD+5ByEtkcBB2uT47sbiGrAeco+GxFGUVqi1IjObqrkIrPzV +OjQhTZV6qgYCOuniJiGfoiMeHqdaDybpqo1bIrxSlvGRNcVoOsKt2/KP1DzW4ARZ +hoRvayU2apHz/T5TAailqAW2MsrjGRaVHQTmeZKag8CKtAcjWzui0J2DnfXxUMn8 +R3ruFu3xJduOT1VghT9L9udvX9YhPCIKVL9+B5eFX9eMV6N7hUnVug== +-----END CERTIFICATE----- diff --git a/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/intermediate/certs/client.cert.pem b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/intermediate/certs/client.cert.pem new file mode 100644 index 00000000000..b6f42801680 --- /dev/null +++ b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/intermediate/certs/client.cert.pem @@ -0,0 +1,28 @@ +-----BEGIN CERTIFICATE----- +MIIExzCCAq+gAwIBAgICEAMwDQYJKoZIhvcNAQELBQAwRzELMAkGA1UEBhMCdXMx +DjAMBgNVBAgMBWR1bW15MQ4wDAYDVQQKDAVkdW1teTEYMBYGA1UEAwwPaW50ZXJt +ZWRpYXRlIGNhMB4XDTE3MTEwMjAwMzgwMFoXDTI3MTAzMTAwMzgwMFowPjELMAkG +A1UEBhMCdXMxDjAMBgNVBAgMBWR1bW15MQ4wDAYDVQQKDAVkdW1teTEPMA0GA1UE +AwwGY2xpZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyxZFTLqv +Gd9SpFAykyRyLQgHcR5hgD55mz+9fl1OfnMoAc7yTdPVLksDLmeFUlxcvCtLHysJ +klIBX62c6LzbsVcfLg/DPJlQxFnkhJCRKen4fp7x9h62qqJkDFVXsiEFza9L1lsN +4OwqU8i4RRgZ/xggM/s/wVBtynioeW9QADNmKZ1n6HVKkYwdOynbFSggYfFrL3HL +54bC9roZUETin0G5wZ9QU+srgivT0a/KC3ourBYHXAI40iHuuOBf3syDVJ6xId/r +3UO3qkiQ5q7pwglg+8Nx7Q3CFtGZY3ewxSSSDo6BOyweGYMsBaxMO3EyTqecyfXn +3n4XPqwmDalWYQIDAQABo4HFMIHCMAkGA1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQD +AgWgMDMGCWCGSAGG+EIBDQQmFiRPcGVuU1NMIEdlbmVyYXRlZCBDbGllbnQgQ2Vy +dGlmaWNhdGUwHQYDVR0OBBYEFP2bodoNQ1tCNEOALPnygGMUfNI+MB8GA1UdIwQY +MBaAFOWzLd7eBJwSNbzRqNsD7MQDCHg/MA4GA1UdDwEB/wQEAwIF4DAdBgNVHSUE +FjAUBggrBgEFBQcDAgYIKwYBBQUHAwQwDQYJKoZIhvcNAQELBQADggIBAHqUuCLt +olOdR9p/g+KgGPnKuVgMn15Wc2VLCrbbl2P0fuCcNWmnBKqHHgQ1EJEpgnQ2N8m6 +tOGucX7IAzlZj36RP4lN3gZqFRSO/OiTOUYpE6Uv1hYRxeMzAYo5sBdCiiypjV9z +H0Ew5NuWRf2/0nFWoywB9ktHcfD8lRFI3o8zUFXmE2JSUPQtKhW3tBkPPjYBlgzD +RD8cq8dVK9P7i3tUENP+MNHJToNLFBqfA9De6bKnhCWHhZkfB0VeeSm4Ja9HkCg/ +DB+PAKMfbLCH5T8gCpEWxNlvj09r9mn37fNjtJPO/goAcNZNO2AURmb/ZQ4ggdry +xb6lm832qplMUMWx//Ore0faEodlEc5d2kEtmcjj79gAypcLmm74q7CPt7xmniyd +XvNT33S2tkh4dSirpCVwq0xyqOP3ZqTsTjudTveTBaTZNhTbCjDbaV7ga47TcH9/ ++OZ3fQKjt2LAC6162wgEFZf10nUgaAXvSlI74gru93vEwWd8Pd3sWfGwuAFX3oKI +JuwL2kxEuoZQmeRiVJu6KQb+Im7d5CIoWViDmfxcSDJfdtSePTqmDURIx87fw14Z +XBWJP4PiK5PRmG/L0cGiDckmDKm/MuD13Z2I/NMl81GNY/q3WY2O7BmddPpAG5dr +sc5hOqA9+jX08XbxKnfBPYllK5skYMkFH5tN +-----END CERTIFICATE----- diff --git a/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/intermediate/certs/intermediate.cert.pem b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/intermediate/certs/intermediate.cert.pem new file mode 100644 index 00000000000..4305e5333f8 --- /dev/null +++ b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/intermediate/certs/intermediate.cert.pem @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFaDCCA1CgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwPzELMAkGA1UEBhMCdXMx +DjAMBgNVBAgMBWR1bW15MQ4wDAYDVQQKDAVkdW1teTEQMA4GA1UEAwwHcm9vdCBj +YTAeFw0xNzExMDIwMDM3NTlaFw0yNzEwMzEwMDM3NTlaMEcxCzAJBgNVBAYTAnVz +MQ4wDAYDVQQIDAVkdW1teTEOMAwGA1UECgwFZHVtbXkxGDAWBgNVBAMMD2ludGVy +bWVkaWF0ZSBjYTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKuM2iFz +CCKmbs4uLj/8wjtxf+jFmkwzN4Pps4hqJ3NJqWB326LhzWyqM4WiTAJWE02wSJbS +16RPfbjkVC77OI4+PUdwqxU9vNAP/95w0h6hBSFtkysuT5VVUt5jiY7wnUKgqTCi +MYhYOl+HEP32O4cnxAazkUKKvtyrd4/PvejJ9zz+iYexRnaaGfOFR3co7jQ5QKar +oK4UgJC3mVDZQEMBV0oljkpgVQMAVb4XQU7e+o25JOUkOoK6LdK/b/95khR0jTyD +OBLqd4mCEzxNi+jZ0jLTLPk0c+DnGmRfoNUxnFb40R8QnEIEKwf+JKyl6p89oqOl +pvIZFLZlUWIS4qL+993l1SCqPkWJOAdTg+s/Zh6DeAOhrUn9/wk0aQwZrK7wQQLJ +4GGhxC/FfuUGsLqZszAVkP8jDEWnzhN2rw3V+C7v6lj4qHhUwqGHuYzCx2Hxl+B8 +UyBmZb9gXKVUtAvaZjaL2PDj1ZAxT0KVMlw1ZVrZu45OsHNQuBx/4uIAt6Rga8yt +av1lsq+hFqwI4bU/oZC/oPMacOsB4qEkAA1101WjMc5bg6JOPWobwIqmUXQR1WJE +j30e99HCpk1Cc2+9sUCzNu8KvU5kUY2K90zwqProvj5IfMuDetAVXsEjgW+ZqSho +UMIpJ2M/hzAFl8Z5IRlG+YNfZNXl0FqJ5LzLAgMBAAGjZjBkMB0GA1UdDgQWBBTl +sy3e3gScEjW80ajbA+zEAwh4PzAfBgNVHSMEGDAWgBTgTvUeshGOijhPmkgpcA9g +ULVlyjASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG +9w0BAQsFAAOCAgEAOS7DtliOUPcVosRfyx9dHcSUc3O+uY8uKjSHhdIrxDJm4lwP +Q6lKg5j8CdMVb+sDQmyBkqQIA/6E13corP6R283jO6W8D4A8kjOiQWpXfjW6OcP3 +4rrDEWhCdeLFSNJIYOFkr2qWJpI/k0VpyDnmY0YluS5WbNjg6zTzGelzhFbV7/S1 +cteNAZD0vHD8NmbLVDJjjIY3E/iwzoUzBncLYbDwqyVS1g6utWdSy8LEJxzzqqWJ +pBKlNYILAdh8efBgvotafaxsn2nfjmVmekPn3KcQZuE4Kzv1EQ2PrHpGeJKwh6up +YBL2tav5cAki8bWoGPr2oGmWUf9L2tB57SdWdaY60ifzmQaeGiWPZBSmAz7PRSrz +sR9SMIkBfYVRxXgWwlvr8JYnd2h/Ef5K9fI32nGfje+7/0kPEjNyjehri7sV4Sjt +zzkDiFO+JklrRuLBPMFYOokq6Pcko32FKlE82pe8QkMDS8Sk//9PqCTK9ceB7y6E +NYLNBW/X9SAw/TR5kdRinHHgHyEug7N4+DCU3lU1wl72ZjoiGE7V6c2AssFC2VcE +E+WYxJT1ROJ1/5+U6BKdaIpTwMtRIFRomOEI66iOwOSEwqLIztkqxwpQ7THraWKm +2W5e54u/efapIDcQFnP3E8r7TD0PdIeU6mD28o0+WiK3uL/OZpvyKaHPeFU= +-----END CERTIFICATE----- diff --git a/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/intermediate/certs/localhost-1.cert.pem b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/intermediate/certs/localhost-1.cert.pem new file mode 100644 index 00000000000..2850e42fe91 --- /dev/null +++ b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/intermediate/certs/localhost-1.cert.pem @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFFzCCAv+gAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwRzELMAkGA1UEBhMCdXMx +DjAMBgNVBAgMBWR1bW15MQ4wDAYDVQQKDAVkdW1teTEYMBYGA1UEAwwPaW50ZXJt +ZWRpYXRlIGNhMB4XDTE3MTEwMjAwMzc1OVoXDTI3MTAzMTAwMzc1OVowTTELMAkG +A1UEBhMCdXMxDjAMBgNVBAgMBWR1bW15MQ4wDAYDVQQKDAVkdW1teTEKMAgGA1UE +CwwBMTESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAycY/7H1xk3/XHZRopULV7YsOzPIrMG25zoACbpDZxjS0I+2r1c7V +wnvE8TszAkloLi+Skku5CYC7IvVEEEuKuIuV+8M48FJEwlCPge8LPiy18C+npCEd +fgDzCV/O9DfJj6UaiCUayVE7UujXoke7AlKQEJcqvnD/CoTv2Y8jV1A6mPf6CTEI +Sl1BMeFSmeFyvZll+xJ8Up1KfQZxKhtpP1s/rp6ZNlqSs1LM5+vcDHHZ6COTbq7t +2vvcmGDTqeCLsqicBg1kJyMPRtqa0bNPj2bcVtcK0Ndfn6eL2hi+EoBy2nIXi6aG +PpXf85b9bCLd5pZI80nHzFlhdvV+SxqrfwIDAQABo4IBBTCCAQEwCQYDVR0TBAIw +ADARBglghkgBhvhCAQEEBAMCBkAwMwYJYIZIAYb4QgENBCYWJE9wZW5TU0wgR2Vu +ZXJhdGVkIFNlcnZlciBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUoYjECaDz/ZELru/r +jfTB1ShlVrAwaAYDVR0jBGEwX4AU5bMt3t4EnBI1vNGo2wPsxAMIeD+hQ6RBMD8x +CzAJBgNVBAYTAnVzMQ4wDAYDVQQIDAVkdW1teTEOMAwGA1UECgwFZHVtbXkxEDAO +BgNVBAMMB3Jvb3QgY2GCAhAAMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggr +BgEFBQcDATANBgkqhkiG9w0BAQsFAAOCAgEAiiR2knMMym4O+3fD1KlYSnc2UR3v +0FlRVAsr8wvTlVjJhx7DbRusBNJHWX66mUgK9x5OLnhyvyqlFVhR9AwlnxgfLWz9 +nnACeXzcjQZnKWFQWu8bJSC6Ene6rd1g2acK6SOjxavVbj7JVFnmlHF/naZUzvMl +mJivYta4k7ob8UcX0I5TlJpzglU3UHyyJd5d9zhbF8wqbBq63zR2ovWci4pYCg+F +jYcTGYVZJti3SHO+9/EqTC9x2KDNs3o0+rreJ3GuoonkInKZMQQZJQ6qILvkxlhT +jyU5xlcaJ+0tSaiFK3eF0nXIpFYdZbIHYPCdLjh9AZ2dkFcAgSa/L8+tsVt60k8D +HTO0Hz6dW5D2ckeebZvz5LACMN89gVzrc/rVkeg7QmpSbjkTSLC2KJS53hJzWcEI +3KB73B9iY+ZYytcYBTYLizsAxd5g7j9z8UXrmVQ4mWbh2+xKiG+9aVOzCZ09AYi6 +WVK2aRcMQshgkkqPOloN9OeQNCE8Exf7N/zHsBhygorJXoD/PFgnV1VZm8xkOdiJ +zTb3bpGdmL5+bzzS6wP8Q7pGZGYdlnB7JNO8oMYPPtzX8OOx92BTkPnqJnnRWTpR +SjMEEdQe8K7iXxejQkjaAq5BlwaAOjCjPTqYomECcYjC0WaXsmrPcnZwSqpnHZZ2 +OiINYJub5cvBLNo= +-----END CERTIFICATE----- diff --git a/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/intermediate/private/client.key.pem b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/intermediate/private/client.key.pem new file mode 100644 index 00000000000..a4c5fd4a564 --- /dev/null +++ b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/intermediate/private/client.key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAyxZFTLqvGd9SpFAykyRyLQgHcR5hgD55mz+9fl1OfnMoAc7y +TdPVLksDLmeFUlxcvCtLHysJklIBX62c6LzbsVcfLg/DPJlQxFnkhJCRKen4fp7x +9h62qqJkDFVXsiEFza9L1lsN4OwqU8i4RRgZ/xggM/s/wVBtynioeW9QADNmKZ1n +6HVKkYwdOynbFSggYfFrL3HL54bC9roZUETin0G5wZ9QU+srgivT0a/KC3ourBYH +XAI40iHuuOBf3syDVJ6xId/r3UO3qkiQ5q7pwglg+8Nx7Q3CFtGZY3ewxSSSDo6B +OyweGYMsBaxMO3EyTqecyfXn3n4XPqwmDalWYQIDAQABAoIBAFhIOR3OtVlw3BLz +jdiq6jsrF1kUFNxTzDcxsSUiWIHde1G17Vzpre0uzJY6iBkyb1mZFFHbOpDxtwkp +hmEh3/qqXbJ/RaatGxAP56e81G28+LnKTHJqDYwFhapa2wFjG4u7HSN0d4cEAq5j +Pb9DZ+GdUjpmiON3HBL8+ne3bLZ42uI+DSVe8d3irbqg2rqsiANf0gdimMW4nuI4 +rVxf8HrY43PdQn/Vby+7qLRE3tmIlpbTqJGRtWRjdeBBI91APCrRljjXrKqT6Zpa +E6Daz3YIQvXkIT0q+WkeN1VmQbtRnk7kRsPNp15kSwpHfmv6o/vkO9OUb1n71P2F +wnB0WDECgYEA8iltnKxXnjqwZ/vzIWzcd94j+mdZg/K2/JCOqjwMvpSGCvx2zUmq +Y2nxO2K85AVeOm/Yt87SMODB6AQ9CsrVGEUAzzacvCJDb8oUhaOL5gypnyvZiGCy +snzXfgB+v/xuGekIjs2y7E8h3GG40j0aNQnUY1Fuc6iaeJG4BtjkuQUCgYEA1rE4 +DrTSsUh3hLYQusIHZR8Lecrrd4QUZSMKLkWjobiSTw3m4mglx1s2G4eZ3WuzOyFq +Dp3/b3yfT8prdPBGA6shHNFf+1TO1q1/pIt15dc3sFwxMkuunai8N4QZJRqZLbYq +FkNFkZ20hFHcH/NHDsAsRL/0tJdEmJ2ruP+Qdq0CgYBsdPGKwgVb8J0hdU4nIkJ7 +zRoABFmrJwGdjIDY7Zwnnw2JzhjHSL7vV3ubRVWkKmNReNZvPEoXahJuf7d3JfDa +tczvAV6hRBc/8hnO4Li/h9xQVatP0T83gYJiBIbAJaaKJDyY+Lex7p8TvRCx2Hvs +VUKyWL5HPrQwW9M3/dwyoQKBgQCNQoPA4Wcz8Jt7PZQaXaoh9eBGHab6t3P366s6 +MOXudZQG4f3FgINC/ZfHW1x43PFL+btfrMOyJkxoYqZ7hdB7f3DFFlpR80Y46GVw +7bYAKbBhoPdZwYQ+BhT5bjhhOnQJKK/egBrZKevpmDb+6sIZSYaXIbovzMv8otmn +WrhB7QKBgAdl+KYBQULCUBp8qCQH5sAQoWErpyuD2FNN6LGknpPqn4DdujvwEP0Z +OSvbauLkI0Qc9/MezKPTeYXlFqdbpItwyySJsUkiI3HhVYlBgDkZ7xb6uHIH5E6I +bKgIW5JEf5I7Eu1iurORkXxCCGMkiQmEs4X5kSXXRYgXfNgAD0FX +-----END RSA PRIVATE KEY----- diff --git a/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/intermediate/private/localhost-1.key.pem b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/intermediate/private/localhost-1.key.pem new file mode 100644 index 00000000000..8cba174841a --- /dev/null +++ b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/intermediate/private/localhost-1.key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAycY/7H1xk3/XHZRopULV7YsOzPIrMG25zoACbpDZxjS0I+2r +1c7VwnvE8TszAkloLi+Skku5CYC7IvVEEEuKuIuV+8M48FJEwlCPge8LPiy18C+n +pCEdfgDzCV/O9DfJj6UaiCUayVE7UujXoke7AlKQEJcqvnD/CoTv2Y8jV1A6mPf6 +CTEISl1BMeFSmeFyvZll+xJ8Up1KfQZxKhtpP1s/rp6ZNlqSs1LM5+vcDHHZ6COT +bq7t2vvcmGDTqeCLsqicBg1kJyMPRtqa0bNPj2bcVtcK0Ndfn6eL2hi+EoBy2nIX +i6aGPpXf85b9bCLd5pZI80nHzFlhdvV+SxqrfwIDAQABAoIBAQC022161aoTEtjH +m7n8v56vUCCRFVQfEYsljFohrtZ0sdLyDVwjxkSWEYiizXRYTWIDXALd/N+7o9aZ +bAx5Kq0J45wpUYBc8PDO15T6W0DRlxPxWVXDaSddRQ6TTXxcLREPH2dbtx5+asBo +/Woi/Haki0q0hDr8/p2sWSH/+SwtWpOezGVlrWrkMeIhlBwHZfdHVoZvSx65Uv7x +WU07vsjrbXNDwf+2fmklAQrzhedCeh8loGyjtN3cfrTjrE1zqpEsHnlZcJxe6sRB +1nOqpoUnpZXklDDIYC8EmeubmDJ0jnXOQCDDep3MzVcnZGyF5E/+szaa1NL70Ayj +rbKk1Y3ZAoGBAPy/1ym7Cjl4OGHN2fdkR6iL68ebJozpr+eTSxDNLuBSi5IJxJyG +1+B4+v1u0RwZ3DjrSQsO5DCbZ+DHU6O/DAJK2CxUED+M+G2kRyffailRQmNzjpRG +75dIhSkSRYH8vdvEOnGpeQBZwBcCRH/2YUMlZeSfx9fHJhk1nyUxJeHjAoGBAMxe +k+cBb0zYok+Ww1xTwOdq0PwKj0oDsEg8hOdWc8pH0SlOAB4BI5kmfd1JDMHfRc49 +7tpNqjsPrnlb9xd8l0281Lj2NoVSE5KX1JtsOsKecQsvHH5zRk4eJ3h/mNixpjfe +79Zc/O40T4rWpQRqhat+WHveJC0/ON4AH4uT0BK1AoGBAPcTioCu6YXYsjVaCJPB +IhPwBGOylfL2lxDoel9IVWTRDMOMbPkfEHXNjn6lECJKXW//Af6fZg7mPJwN/wN5 +xYGQLNbYrrGRW2HDUBP4YU1WtHGIC3+EAL+BEztdMzmpGuh1YTSvmSvwkMltXA1D +iz0amArw72lOsz29n3+6FfBFAoGAIpRqMC8k9vq80/yth6TAQifnvo3G2v4uyLo8 +vqv5IaPvNy70hB8rN9G0gEnI99Dgjdoa3SNBB4dKvUwbTgUN0OB/meBHL13I5Af+ +uGGiu6V1eS/6gUbeAX/Gq/PjF99PQareKAZJ4cBGKTbSayHfBjp1nFflBSbqZ13b ++JEFJvUCgYBOs2J2XXamPbI7gu7B2TE9j/62v0SJyoHq2LHMmYUDRuPdPk3eKCt3 +283w+E8XUIFbctaxsbo8msNjjvV22D/Nci3d87aPe8bn1SVto3GnTuwnOpRq3E+3 +wAarqrhiZbGZSCcAkEOk7FlxAwYnCM6paqMxDEMCJ4qChMM42E9ZyQ== +-----END RSA PRIVATE KEY----- diff --git a/src/python/grpcio_tests/tests/unit/resources.py b/src/python/grpcio_tests/tests/unit/resources.py index 823d2307d33..11ef9e85656 100644 --- a/src/python/grpcio_tests/tests/unit/resources.py +++ b/src/python/grpcio_tests/tests/unit/resources.py @@ -11,7 +11,7 @@ # 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. -"""Constants and functions for data used in interoperability testing.""" +"""Constants and functions for data used in testing.""" import os @@ -34,3 +34,81 @@ def private_key(): def certificate_chain(): return pkg_resources.resource_string(__name__, _CERTIFICATE_CHAIN_RESOURCE_PATH) + + +def cert_hier_1_root_ca_cert(): + return pkg_resources.resource_string( + __name__, 'credentials/certificate_hierarchy_1/certs/ca.cert.pem') + + +def cert_hier_1_intermediate_ca_cert(): + return pkg_resources.resource_string( + __name__, + 'credentials/certificate_hierarchy_1/intermediate/certs/intermediate.cert.pem' + ) + + +def cert_hier_1_client_1_key(): + return pkg_resources.resource_string( + __name__, + 'credentials/certificate_hierarchy_1/intermediate/private/client.key.pem' + ) + + +def cert_hier_1_client_1_cert(): + return pkg_resources.resource_string( + __name__, + 'credentials/certificate_hierarchy_1/intermediate/certs/client.cert.pem') + + +def cert_hier_1_server_1_key(): + return pkg_resources.resource_string( + __name__, + 'credentials/certificate_hierarchy_1/intermediate/private/localhost-1.key.pem' + ) + + +def cert_hier_1_server_1_cert(): + return pkg_resources.resource_string( + __name__, + 'credentials/certificate_hierarchy_1/intermediate/certs/localhost-1.cert.pem' + ) + + +def cert_hier_2_root_ca_cert(): + return pkg_resources.resource_string( + __name__, 'credentials/certificate_hierarchy_2/certs/ca.cert.pem') + + +def cert_hier_2_intermediate_ca_cert(): + return pkg_resources.resource_string( + __name__, + 'credentials/certificate_hierarchy_2/intermediate/certs/intermediate.cert.pem' + ) + + +def cert_hier_2_client_1_key(): + return pkg_resources.resource_string( + __name__, + 'credentials/certificate_hierarchy_2/intermediate/private/client.key.pem' + ) + + +def cert_hier_2_client_1_cert(): + return pkg_resources.resource_string( + __name__, + 'credentials/certificate_hierarchy_2/intermediate/certs/client.cert.pem') + + +def cert_hier_2_server_1_key(): + return pkg_resources.resource_string( + __name__, + 'credentials/certificate_hierarchy_2/intermediate/private/localhost-1.key.pem' + ) + + +def cert_hier_2_server_1_cert(): + return pkg_resources.resource_string( + __name__, + 'credentials/certificate_hierarchy_2/intermediate/certs/localhost-1.cert.pem' + ) From 90dbd2b6701acacace74df6bbdd8bcfe2248b431 Mon Sep 17 00:00:00 2001 From: Mehrdad Afshari Date: Thu, 16 Nov 2017 15:21:02 -0800 Subject: [PATCH 58/70] Alter health/reflection packages' build criterion Using the presence of the `*_pb2_grpc` module, as opposed to the absence of the build script (`*_commands` module) is a problematic choice, because even if a generated file is present, the test infrastructure may want to regenerate it under a different environment (e.g. different Python/proto package version). This will ensure the protos always get recompiled if we have a `*_commands` module present, signaling we are in a build environment, thereby making the process hermetic. --- src/python/grpcio_health_checking/setup.py | 18 ++++++++---------- src/python/grpcio_reflection/setup.py | 18 ++++++++---------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/src/python/grpcio_health_checking/setup.py b/src/python/grpcio_health_checking/setup.py index 01d796f4e6c..7a2e6f50276 100644 --- a/src/python/grpcio_health_checking/setup.py +++ b/src/python/grpcio_health_checking/setup.py @@ -60,17 +60,8 @@ INSTALL_REQUIRES = ('protobuf>=3.3.0', 'grpcio>={version}'.format(version=grpc_version.VERSION),) try: - # ensure we can load the _pb2_grpc module: - from grpc_health.v1 import health_pb2_grpc as _pb2_grpc - # if we can find the _pb2_grpc module, the package has already been built. - SETUP_REQUIRES = () - COMMAND_CLASS = { - # wire up commands to no-op not to break the external dependencies - 'preprocess': _NoOpCommand, - 'build_package_protos': _NoOpCommand, - } -except ImportError: # we are in the build environment import health_commands as _health_commands + # we are in the build environment, otherwise the above import fails SETUP_REQUIRES = ( 'grpcio-tools=={version}'.format(version=grpc_version.VERSION),) COMMAND_CLASS = { @@ -78,6 +69,13 @@ except ImportError: # we are in the build environment 'preprocess': _health_commands.CopyProtoModules, 'build_package_protos': _health_commands.BuildPackageProtos, } +except ImportError: + SETUP_REQUIRES = () + COMMAND_CLASS = { + # wire up commands to no-op not to break the external dependencies + 'preprocess': _NoOpCommand, + 'build_package_protos': _NoOpCommand, + } setuptools.setup( name='grpcio-health-checking', diff --git a/src/python/grpcio_reflection/setup.py b/src/python/grpcio_reflection/setup.py index ad9e86990fc..25312c7c0a2 100644 --- a/src/python/grpcio_reflection/setup.py +++ b/src/python/grpcio_reflection/setup.py @@ -61,17 +61,8 @@ INSTALL_REQUIRES = ('protobuf>=3.3.0', 'grpcio>={version}'.format(version=grpc_version.VERSION),) try: - # ensure we can load the _pb2_grpc module: - from grpc_reflection.v1alpha import reflection_pb2_grpc as _pb2_grpc - # if we can find the _pb2_grpc module, the package has already been built. - SETUP_REQUIRES = () - COMMAND_CLASS = { - # wire up commands to no-op not to break the external dependencies - 'preprocess': _NoOpCommand, - 'build_package_protos': _NoOpCommand, - } -except ImportError: # we are in the build environment import reflection_commands as _reflection_commands + # we are in the build environment, otherwise the above import fails SETUP_REQUIRES = ( 'grpcio-tools=={version}'.format(version=grpc_version.VERSION),) COMMAND_CLASS = { @@ -79,6 +70,13 @@ except ImportError: # we are in the build environment 'preprocess': _reflection_commands.CopyProtoModules, 'build_package_protos': _reflection_commands.BuildPackageProtos, } +except ImportError: + SETUP_REQUIRES = () + COMMAND_CLASS = { + # wire up commands to no-op not to break the external dependencies + 'preprocess': _NoOpCommand, + 'build_package_protos': _NoOpCommand, + } setuptools.setup( name='grpcio-reflection', From 11c1c47fe95e6ec8c9ff97d1e702561eee36434e Mon Sep 17 00:00:00 2001 From: Noah Eisen Date: Mon, 20 Nov 2017 21:41:51 -0800 Subject: [PATCH 59/70] Fix real TSAN/ASAN bug --- src/core/lib/channel/channel_stack.h | 2 +- src/core/lib/iomgr/error.cc | 2 +- src/core/lib/surface/call.cc | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/lib/channel/channel_stack.h b/src/core/lib/channel/channel_stack.h index 41851382ae2..6419f1ac480 100644 --- a/src/core/lib/channel/channel_stack.h +++ b/src/core/lib/channel/channel_stack.h @@ -84,7 +84,7 @@ typedef struct { typedef struct { grpc_call_stats stats; grpc_status_code final_status; - const char* error_string; + const char** error_string; } grpc_call_final_info; /* Channel filters specify: diff --git a/src/core/lib/iomgr/error.cc b/src/core/lib/iomgr/error.cc index 5fe6514802b..581b903f1a0 100644 --- a/src/core/lib/iomgr/error.cc +++ b/src/core/lib/iomgr/error.cc @@ -754,7 +754,7 @@ const char* grpc_error_string(grpc_error* err) { if (!gpr_atm_rel_cas(&err->atomics.error_string, 0, (gpr_atm)out)) { gpr_free(out); - out = (char*)gpr_atm_acq_load(&err->atomics.error_string); + out = (char*)gpr_atm_no_barrier_load(&err->atomics.error_string); } GPR_TIMER_END("grpc_error_string", 0); diff --git a/src/core/lib/surface/call.cc b/src/core/lib/surface/call.cc index c67b577ba10..d9185ec81e3 100644 --- a/src/core/lib/surface/call.cc +++ b/src/core/lib/surface/call.cc @@ -551,7 +551,7 @@ static void destroy_call(grpc_exec_ctx* exec_ctx, void* call, get_final_status(exec_ctx, c, set_status_value_directly, &c->final_info.final_status, nullptr, - &c->final_info.error_string); + c->final_info.error_string); c->final_info.stats.latency = gpr_time_sub(gpr_now(GPR_CLOCK_MONOTONIC), c->start_time); From 9a244d61d4a0b2bbe775041b5ce63c1df6a97e37 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Fri, 17 Nov 2017 20:02:31 +0100 Subject: [PATCH 60/70] avoid unnecessary allocation in completion registry --- src/csharp/Grpc.Core.Tests/PInvokeTest.cs | 4 +- .../Internal/BatchContextSafeHandle.cs | 28 ++++++++- .../Grpc.Core/Internal/CompletionRegistry.cs | 58 +++---------------- .../Grpc.Core/Internal/GrpcThreadPool.cs | 8 +-- .../Grpc.Core/Internal/NativeMethods.cs | 4 +- .../Internal/RequestCallContextSafeHandle.cs | 23 +++++++- .../CompletionRegistryBenchmark.cs | 12 +++- .../SendMessageBenchmark.cs | 2 +- 8 files changed, 77 insertions(+), 62 deletions(-) diff --git a/src/csharp/Grpc.Core.Tests/PInvokeTest.cs b/src/csharp/Grpc.Core.Tests/PInvokeTest.cs index 7529c44c4e6..43f816bb1c6 100644 --- a/src/csharp/Grpc.Core.Tests/PInvokeTest.cs +++ b/src/csharp/Grpc.Core.Tests/PInvokeTest.cs @@ -63,7 +63,7 @@ namespace Grpc.Core.Tests [Ignore("Prevent running on Jenkins")] public void NativeCallbackBenchmark() { - OpCompletionDelegate handler = Handler; + NativeCallbackTestDelegate handler = Handler; counter = 0; BenchmarkUtil.RunBenchmark( @@ -91,7 +91,7 @@ namespace Grpc.Core.Tests 10000, 10000, () => { - Native.grpcsharp_test_callback(new OpCompletionDelegate(Handler)); + Native.grpcsharp_test_callback(new NativeCallbackTestDelegate(Handler)); }); Assert.AreNotEqual(0, counter); } diff --git a/src/csharp/Grpc.Core/Internal/BatchContextSafeHandle.cs b/src/csharp/Grpc.Core/Internal/BatchContextSafeHandle.cs index cd5e3d8911a..d839413a808 100644 --- a/src/csharp/Grpc.Core/Internal/BatchContextSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/BatchContextSafeHandle.cs @@ -20,15 +20,22 @@ using System; using System.Runtime.InteropServices; using System.Text; using Grpc.Core; +using Grpc.Core.Logging; namespace Grpc.Core.Internal { + internal interface IOpCompletionCallback + { + void OnComplete(bool success); + } + /// /// grpcsharp_batch_context /// - internal class BatchContextSafeHandle : SafeHandleZeroIsInvalid + internal class BatchContextSafeHandle : SafeHandleZeroIsInvalid, IOpCompletionCallback { static readonly NativeMethods Native = NativeMethods.Get(); + static readonly ILogger Logger = GrpcEnvironment.Logger.ForType(); private BatchContextSafeHandle() { @@ -47,6 +54,8 @@ namespace Grpc.Core.Internal } } + public BatchCompletionDelegate CompletionCallback { get; set; } + // Gets data of recv_initial_metadata completion. public Metadata GetReceivedInitialMetadata() { @@ -92,5 +101,22 @@ namespace Grpc.Core.Internal Native.grpcsharp_batch_context_destroy(handle); return true; } + + void IOpCompletionCallback.OnComplete(bool success) + { + try + { + CompletionCallback(success, this); + } + catch (Exception e) + { + Logger.Error(e, "Exception occured while invoking batch completion delegate."); + } + finally + { + CompletionCallback = null; + Dispose(); + } + } } } diff --git a/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs b/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs index 1102c8d14fb..33734864bdd 100644 --- a/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs +++ b/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs @@ -25,8 +25,6 @@ using Grpc.Core.Utils; namespace Grpc.Core.Internal { - internal delegate void OpCompletionDelegate(bool success); - internal delegate void BatchCompletionDelegate(bool success, BatchContextSafeHandle ctx); internal delegate void RequestCallCompletionDelegate(bool success, RequestCallContextSafeHandle ctx); @@ -36,7 +34,7 @@ namespace Grpc.Core.Internal static readonly ILogger Logger = GrpcEnvironment.Logger.ForType(); readonly GrpcEnvironment environment; - readonly Dictionary dict = new Dictionary(new IntPtrComparer()); + readonly Dictionary dict = new Dictionary(new IntPtrComparer()); readonly object myLock = new object(); IntPtr lastRegisteredKey; // only for testing @@ -45,7 +43,7 @@ namespace Grpc.Core.Internal this.environment = environment; } - public void Register(IntPtr key, OpCompletionDelegate callback) + public void Register(IntPtr key, IOpCompletionCallback callback) { environment.DebugStats.PendingBatchCompletions.Increment(); lock (myLock) @@ -57,21 +55,19 @@ namespace Grpc.Core.Internal public void RegisterBatchCompletion(BatchContextSafeHandle ctx, BatchCompletionDelegate callback) { - // TODO(jtattermusch): get rid of new delegate creation here - OpCompletionDelegate opCallback = ((success) => HandleBatchCompletion(success, ctx, callback)); - Register(ctx.Handle, opCallback); + ctx.CompletionCallback = callback; + Register(ctx.Handle, ctx); } public void RegisterRequestCallCompletion(RequestCallContextSafeHandle ctx, RequestCallCompletionDelegate callback) { - // TODO(jtattermusch): get rid of new delegate creation here - OpCompletionDelegate opCallback = ((success) => HandleRequestCallCompletion(success, ctx, callback)); - Register(ctx.Handle, opCallback); + ctx.CompletionCallback = callback; + Register(ctx.Handle, ctx); } - public OpCompletionDelegate Extract(IntPtr key) + public IOpCompletionCallback Extract(IntPtr key) { - OpCompletionDelegate value = null; + IOpCompletionCallback value = null; lock (myLock) { value = dict[key]; @@ -89,44 +85,6 @@ namespace Grpc.Core.Internal get { return this.lastRegisteredKey; } } - private static void HandleBatchCompletion(bool success, BatchContextSafeHandle ctx, BatchCompletionDelegate callback) - { - try - { - callback(success, ctx); - } - catch (Exception e) - { - Logger.Error(e, "Exception occured while invoking batch completion delegate."); - } - finally - { - if (ctx != null) - { - ctx.Dispose(); - } - } - } - - private static void HandleRequestCallCompletion(bool success, RequestCallContextSafeHandle ctx, RequestCallCompletionDelegate callback) - { - try - { - callback(success, ctx); - } - catch (Exception e) - { - Logger.Error(e, "Exception occured while invoking request call completion delegate."); - } - finally - { - if (ctx != null) - { - ctx.Dispose(); - } - } - } - /// /// IntPtr doesn't implement IEquatable{IntPtr} so we need to use custom comparer to avoid boxing. /// diff --git a/src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs b/src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs index f7f723c00bb..bd0229a9dd4 100644 --- a/src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs +++ b/src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs @@ -68,8 +68,8 @@ namespace Grpc.Core.Internal GrpcPreconditions.CheckArgument(poolSize >= completionQueueCount, "Thread pool size cannot be smaller than the number of completion queues used."); - this.runCompletionQueueEventCallbackSuccess = new WaitCallback((callback) => RunCompletionQueueEventCallback((OpCompletionDelegate) callback, true)); - this.runCompletionQueueEventCallbackFailure = new WaitCallback((callback) => RunCompletionQueueEventCallback((OpCompletionDelegate) callback, false)); + this.runCompletionQueueEventCallbackSuccess = new WaitCallback((callback) => RunCompletionQueueEventCallback((IOpCompletionCallback) callback, true)); + this.runCompletionQueueEventCallbackFailure = new WaitCallback((callback) => RunCompletionQueueEventCallback((IOpCompletionCallback) callback, false)); } public void Start() @@ -225,11 +225,11 @@ namespace Grpc.Core.Internal return list.AsReadOnly(); } - private void RunCompletionQueueEventCallback(OpCompletionDelegate callback, bool success) + private void RunCompletionQueueEventCallback(IOpCompletionCallback callback, bool success) { try { - callback(success); + callback.OnComplete(success); } catch (Exception e) { diff --git a/src/csharp/Grpc.Core/Internal/NativeMethods.cs b/src/csharp/Grpc.Core/Internal/NativeMethods.cs index 22faa19d9bf..d517252cfe6 100644 --- a/src/csharp/Grpc.Core/Internal/NativeMethods.cs +++ b/src/csharp/Grpc.Core/Internal/NativeMethods.cs @@ -29,6 +29,8 @@ using Grpc.Core.Utils; namespace Grpc.Core.Internal { + internal delegate void NativeCallbackTestDelegate(bool success); + /// /// Provides access to all native methods provided by NativeExtension. /// An extra level of indirection is added to P/Invoke calls to allow intelligent loading @@ -420,7 +422,7 @@ namespace Grpc.Core.Internal public delegate Timespec gprsharp_convert_clock_type_delegate(Timespec t, ClockType targetClock); public delegate int gprsharp_sizeof_timespec_delegate(); - public delegate CallError grpcsharp_test_callback_delegate([MarshalAs(UnmanagedType.FunctionPtr)] OpCompletionDelegate callback); + public delegate CallError grpcsharp_test_callback_delegate([MarshalAs(UnmanagedType.FunctionPtr)] NativeCallbackTestDelegate callback); public delegate IntPtr grpcsharp_test_nop_delegate(IntPtr ptr); public delegate void grpcsharp_test_override_method_delegate(string methodName, string variant); } diff --git a/src/csharp/Grpc.Core/Internal/RequestCallContextSafeHandle.cs b/src/csharp/Grpc.Core/Internal/RequestCallContextSafeHandle.cs index b7af0c102d2..09f5c3e4526 100644 --- a/src/csharp/Grpc.Core/Internal/RequestCallContextSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/RequestCallContextSafeHandle.cs @@ -19,15 +19,17 @@ using System; using System.Runtime.InteropServices; using Grpc.Core; +using Grpc.Core.Logging; namespace Grpc.Core.Internal { /// /// grpcsharp_request_call_context /// - internal class RequestCallContextSafeHandle : SafeHandleZeroIsInvalid + internal class RequestCallContextSafeHandle : SafeHandleZeroIsInvalid, IOpCompletionCallback { static readonly NativeMethods Native = NativeMethods.Get(); + static readonly ILogger Logger = GrpcEnvironment.Logger.ForType(); private RequestCallContextSafeHandle() { @@ -46,6 +48,8 @@ namespace Grpc.Core.Internal } } + public RequestCallCompletionDelegate CompletionCallback { get; set; } + // Gets data of server_rpc_new completion. public ServerRpcNew GetServerRpcNew(Server server) { @@ -72,5 +76,22 @@ namespace Grpc.Core.Internal Native.grpcsharp_request_call_context_destroy(handle); return true; } + + void IOpCompletionCallback.OnComplete(bool success) + { + try + { + CompletionCallback(success, this); + } + catch (Exception e) + { + Logger.Error(e, "Exception occured while invoking request call completion delegate."); + } + finally + { + CompletionCallback = null; + Dispose(); + } + } } } diff --git a/src/csharp/Grpc.Microbenchmarks/CompletionRegistryBenchmark.cs b/src/csharp/Grpc.Microbenchmarks/CompletionRegistryBenchmark.cs index 810de119a8b..0e2fb070f19 100644 --- a/src/csharp/Grpc.Microbenchmarks/CompletionRegistryBenchmark.cs +++ b/src/csharp/Grpc.Microbenchmarks/CompletionRegistryBenchmark.cs @@ -52,18 +52,26 @@ namespace Grpc.Microbenchmarks { var completionRegistry = new CompletionRegistry(environment); var ctx = BatchContextSafeHandle.Create(); - var completionDelegate = new OpCompletionDelegate((success) => {}); var stopwatch = Stopwatch.StartNew(); for (int i = 0; i < iterations; i++) { - completionRegistry.Register(ctx.DangerousGetHandle(), completionDelegate); + completionRegistry.Register(ctx.Handle, ctx); var callback = completionRegistry.Extract(completionRegistry.LastRegisteredKey); + // NOTE: we are not calling the callback to avoid disposing ctx. } stopwatch.Stop(); Console.WriteLine("Elapsed millis: " + stopwatch.ElapsedMilliseconds); ctx.Dispose(); } + + private class NopCompletionCallback : IOpCompletionCallback + { + public void OnComplete(bool success) + { + + } + } } } diff --git a/src/csharp/Grpc.Microbenchmarks/SendMessageBenchmark.cs b/src/csharp/Grpc.Microbenchmarks/SendMessageBenchmark.cs index de67874580d..6a8a29d7701 100644 --- a/src/csharp/Grpc.Microbenchmarks/SendMessageBenchmark.cs +++ b/src/csharp/Grpc.Microbenchmarks/SendMessageBenchmark.cs @@ -68,7 +68,7 @@ namespace Grpc.Microbenchmarks { call.StartSendMessage(sendCompletionHandler, payload, writeFlags, false); var callback = completionRegistry.Extract(completionRegistry.LastRegisteredKey); - callback(true); + callback.OnComplete(true); } stopwatch.Stop(); Console.WriteLine("Elapsed millis: " + stopwatch.ElapsedMilliseconds); From 2dc792de17c56fe838685eb71aceea1f1ef788e5 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Mon, 20 Nov 2017 11:31:32 +0100 Subject: [PATCH 61/70] add "state" filed to batch completion delegate --- .../Internal/BatchContextSafeHandle.cs | 32 +++++++++++++++---- .../Grpc.Core/Internal/CompletionRegistry.cs | 6 ++-- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/csharp/Grpc.Core/Internal/BatchContextSafeHandle.cs b/src/csharp/Grpc.Core/Internal/BatchContextSafeHandle.cs index d839413a808..1e6f1fba376 100644 --- a/src/csharp/Grpc.Core/Internal/BatchContextSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/BatchContextSafeHandle.cs @@ -21,6 +21,7 @@ using System.Runtime.InteropServices; using System.Text; using Grpc.Core; using Grpc.Core.Logging; +using Grpc.Core.Utils; namespace Grpc.Core.Internal { @@ -37,6 +38,8 @@ namespace Grpc.Core.Internal static readonly NativeMethods Native = NativeMethods.Get(); static readonly ILogger Logger = GrpcEnvironment.Logger.ForType(); + CompletionCallbackData completionCallbackData; + private BatchContextSafeHandle() { } @@ -54,7 +57,12 @@ namespace Grpc.Core.Internal } } - public BatchCompletionDelegate CompletionCallback { get; set; } + public void SetCompletionCallback(BatchCompletionDelegate callback, object state) + { + GrpcPreconditions.CheckState(completionCallbackData.Callback == null); + GrpcPreconditions.CheckNotNull(callback, nameof(callback)); + completionCallbackData = new CompletionCallbackData(callback, state); + } // Gets data of recv_initial_metadata completion. public Metadata GetReceivedInitialMetadata() @@ -62,13 +70,13 @@ namespace Grpc.Core.Internal IntPtr metadataArrayPtr = Native.grpcsharp_batch_context_recv_initial_metadata(this); return MetadataArraySafeHandle.ReadMetadataFromPtrUnsafe(metadataArrayPtr); } - + // Gets data of recv_status_on_client completion. public ClientSideStatus GetReceivedStatusOnClient() { UIntPtr detailsLength; IntPtr detailsPtr = Native.grpcsharp_batch_context_recv_status_on_client_details(this, out detailsLength); - string details = MarshalUtils.PtrToStringUTF8(detailsPtr, (int) detailsLength.ToUInt32()); + string details = MarshalUtils.PtrToStringUTF8(detailsPtr, (int)detailsLength.ToUInt32()); var status = new Status(Native.grpcsharp_batch_context_recv_status_on_client_status(this), details); IntPtr metadataArrayPtr = Native.grpcsharp_batch_context_recv_status_on_client_trailing_metadata(this); @@ -95,7 +103,7 @@ namespace Grpc.Core.Internal { return Native.grpcsharp_batch_context_recv_close_on_server_cancelled(this) != 0; } - + protected override bool ReleaseHandle() { Native.grpcsharp_batch_context_destroy(handle); @@ -106,7 +114,7 @@ namespace Grpc.Core.Internal { try { - CompletionCallback(success, this); + completionCallbackData.Callback(success, this, completionCallbackData.State); } catch (Exception e) { @@ -114,9 +122,21 @@ namespace Grpc.Core.Internal } finally { - CompletionCallback = null; + completionCallbackData = default(CompletionCallbackData); Dispose(); } } + + struct CompletionCallbackData + { + public CompletionCallbackData(BatchCompletionDelegate callback, object state) + { + this.Callback = callback; + this.State = state; + } + + public BatchCompletionDelegate Callback { get; } + public object State { get; } + } } } diff --git a/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs b/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs index 33734864bdd..f4aea685c31 100644 --- a/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs +++ b/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs @@ -25,7 +25,7 @@ using Grpc.Core.Utils; namespace Grpc.Core.Internal { - internal delegate void BatchCompletionDelegate(bool success, BatchContextSafeHandle ctx); + internal delegate void BatchCompletionDelegate(bool success, BatchContextSafeHandle ctx, object state); internal delegate void RequestCallCompletionDelegate(bool success, RequestCallContextSafeHandle ctx); @@ -53,9 +53,9 @@ namespace Grpc.Core.Internal } } - public void RegisterBatchCompletion(BatchContextSafeHandle ctx, BatchCompletionDelegate callback) + public void RegisterBatchCompletion(BatchContextSafeHandle ctx, BatchCompletionDelegate callback, object state) { - ctx.CompletionCallback = callback; + ctx.SetCompletionCallback(callback, state); Register(ctx.Handle, ctx); } From 8b451ed31dc25f397ce9d270a5f293999505c2cc Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Mon, 20 Nov 2017 11:46:48 +0100 Subject: [PATCH 62/70] server and channel adjustments --- src/csharp/Grpc.Core/Channel.cs | 28 +++++++++++-------- .../Grpc.Core/Internal/ChannelSafeHandle.cs | 4 +-- .../Grpc.Core/Internal/ServerSafeHandle.cs | 6 ++-- src/csharp/Grpc.Core/Server.cs | 2 +- 4 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/csharp/Grpc.Core/Channel.cs b/src/csharp/Grpc.Core/Channel.cs index 18039206628..f9925a8a766 100644 --- a/src/csharp/Grpc.Core/Channel.cs +++ b/src/csharp/Grpc.Core/Channel.cs @@ -127,6 +127,20 @@ namespace Grpc.Core } } + // cached handler for watch connectivity state + static readonly BatchCompletionDelegate WatchConnectivityStateHandler = (success, ctx, state) => + { + var tcs = (TaskCompletionSource) state; + if (success) + { + tcs.SetResult(null); + } + else + { + tcs.SetCanceled(); + } + }; + /// /// Returned tasks completes once channel state has become different from /// given lastObservedState. @@ -138,18 +152,8 @@ namespace Grpc.Core "Shutdown is a terminal state. No further state changes can occur."); var tcs = new TaskCompletionSource(); var deadlineTimespec = deadline.HasValue ? Timespec.FromDateTime(deadline.Value) : Timespec.InfFuture; - var handler = new BatchCompletionDelegate((success, ctx) => - { - if (success) - { - tcs.SetResult(null); - } - else - { - tcs.SetCanceled(); - } - }); - handle.WatchConnectivityState(lastObservedState, deadlineTimespec, completionQueue, handler); + // pass "tcs" as "state" for WatchConnectivityStateHandler. + handle.WatchConnectivityState(lastObservedState, deadlineTimespec, completionQueue, WatchConnectivityStateHandler, tcs); return tcs.Task; } diff --git a/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs index f826a17bad2..1eeb0e3d97f 100644 --- a/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs @@ -64,10 +64,10 @@ namespace Grpc.Core.Internal return Native.grpcsharp_channel_check_connectivity_state(this, tryToConnect ? 1 : 0); } - public void WatchConnectivityState(ChannelState lastObservedState, Timespec deadline, CompletionQueueSafeHandle cq, BatchCompletionDelegate callback) + public void WatchConnectivityState(ChannelState lastObservedState, Timespec deadline, CompletionQueueSafeHandle cq, BatchCompletionDelegate callback, object callbackState) { var ctx = BatchContextSafeHandle.Create(); - cq.CompletionRegistry.RegisterBatchCompletion(ctx, callback); + cq.CompletionRegistry.RegisterBatchCompletion(ctx, callback, callbackState); Native.grpcsharp_channel_watch_connectivity_state(this, lastObservedState, deadline, cq, ctx); } diff --git a/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs index 63000e9a221..a308890cde2 100644 --- a/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs @@ -59,13 +59,15 @@ namespace Grpc.Core.Internal { Native.grpcsharp_server_start(this); } - + public void ShutdownAndNotify(BatchCompletionDelegate callback, CompletionQueueSafeHandle completionQueue) { using (completionQueue.NewScope()) { var ctx = BatchContextSafeHandle.Create(); - completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, callback); + // TODO(jtattermusch): delegate allocation by caller can be avoided by utilizing the "state" object, + // but server shutdown isn't worth optimizing right now. + completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, callback, null); Native.grpcsharp_server_shutdown_and_notify_callback(this, completionQueue, ctx); } } diff --git a/src/csharp/Grpc.Core/Server.cs b/src/csharp/Grpc.Core/Server.cs index 77ad876bdf6..71c7f108f33 100644 --- a/src/csharp/Grpc.Core/Server.cs +++ b/src/csharp/Grpc.Core/Server.cs @@ -387,7 +387,7 @@ namespace Grpc.Core /// /// Handles native callback. /// - private void HandleServerShutdown(bool success, BatchContextSafeHandle ctx) + private void HandleServerShutdown(bool success, BatchContextSafeHandle ctx, object state) { shutdownTcs.SetResult(null); } From 466d77b26f1a842873817cb945f561dd4a679c53 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Mon, 20 Nov 2017 12:59:09 +0100 Subject: [PATCH 63/70] avoid delegate allocations in callsafehandle --- .../Grpc.Core/Internal/CallSafeHandle.cs | 57 ++++++++++++------- src/csharp/Grpc.Core/Internal/INativeCall.cs | 31 ++++++++++ 2 files changed, 67 insertions(+), 21 deletions(-) diff --git a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs index 3a7f97707b8..589f6cbe169 100644 --- a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs @@ -32,6 +32,21 @@ namespace Grpc.Core.Internal public static readonly CallSafeHandle NullInstance = new CallSafeHandle(); static readonly NativeMethods Native = NativeMethods.Get(); + // Completion handlers are pre-allocated to avoid unneccessary delegate allocations. + // The "state" field is used to store the actual callback to invoke. + static readonly BatchCompletionDelegate CompletionHandler_IUnaryResponseClientCallback = + (success, context, state) => ((IUnaryResponseClientCallback)state).OnUnaryResponseClient(success, context.GetReceivedStatusOnClient(), context.GetReceivedMessage(), context.GetReceivedInitialMetadata()); + static readonly BatchCompletionDelegate CompletionHandler_IReceivedStatusOnClientCallback = + (success, context, state) => ((IReceivedStatusOnClientCallback)state).OnReceivedStatusOnClient(success, context.GetReceivedStatusOnClient()); + static readonly BatchCompletionDelegate CompletionHandler_IReceivedMessageCallback = + (success, context, state) => ((IReceivedMessageCallback)state).OnReceivedMessage(success, context.GetReceivedMessage()); + static readonly BatchCompletionDelegate CompletionHandler_IReceivedResponseHeadersCallback = + (success, context, state) => ((IReceivedResponseHeadersCallback)state).OnReceivedResponseHeaders(success, context.GetReceivedInitialMetadata()); + static readonly BatchCompletionDelegate CompletionHandler_ISendCompletionCallback = + (success, context, state) => ((ISendCompletionCallback)state).OnSendCompletion(success); + static readonly BatchCompletionDelegate CompletionHandler_IReceivedCloseOnServerCallback = + (success, context, state) => ((IReceivedCloseOnServerCallback)state).OnReceivedCloseOnServerHandler(success, context.GetReceivedCloseOnServerCancelled()); + const uint GRPC_WRITE_BUFFER_HINT = 1; CompletionQueueSafeHandle completionQueue; @@ -49,12 +64,12 @@ namespace Grpc.Core.Internal Native.grpcsharp_call_set_credentials(this, credentials).CheckOk(); } - public void StartUnary(UnaryResponseClientHandler callback, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags) + public void StartUnary(IUnaryResponseClientCallback callback, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags) { using (completionQueue.NewScope()) { var ctx = BatchContextSafeHandle.Create(); - completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success, context.GetReceivedStatusOnClient(), context.GetReceivedMessage(), context.GetReceivedInitialMetadata())); + completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, CompletionHandler_IUnaryResponseClientCallback, callback); Native.grpcsharp_call_start_unary(this, ctx, payload, new UIntPtr((ulong)payload.Length), writeFlags, metadataArray, callFlags) .CheckOk(); } @@ -71,101 +86,101 @@ namespace Grpc.Core.Internal using (completionQueue.NewScope()) { var ctx = BatchContextSafeHandle.Create(); - completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success, context.GetReceivedStatusOnClient(), context.GetReceivedMessage(), context.GetReceivedInitialMetadata())); + completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, CompletionHandler_IUnaryResponseClientCallback, callback); Native.grpcsharp_call_start_client_streaming(this, ctx, metadataArray, callFlags).CheckOk(); } } - public void StartServerStreaming(ReceivedStatusOnClientHandler callback, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags) + public void StartServerStreaming(IReceivedStatusOnClientCallback callback, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags) { using (completionQueue.NewScope()) { var ctx = BatchContextSafeHandle.Create(); - completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success, context.GetReceivedStatusOnClient())); + completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, CompletionHandler_IReceivedStatusOnClientCallback, callback); Native.grpcsharp_call_start_server_streaming(this, ctx, payload, new UIntPtr((ulong)payload.Length), writeFlags, metadataArray, callFlags).CheckOk(); } } - public void StartDuplexStreaming(ReceivedStatusOnClientHandler callback, MetadataArraySafeHandle metadataArray, CallFlags callFlags) + public void StartDuplexStreaming(IReceivedStatusOnClientCallback callback, MetadataArraySafeHandle metadataArray, CallFlags callFlags) { using (completionQueue.NewScope()) { var ctx = BatchContextSafeHandle.Create(); - completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success, context.GetReceivedStatusOnClient())); + completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, CompletionHandler_IReceivedStatusOnClientCallback, callback); Native.grpcsharp_call_start_duplex_streaming(this, ctx, metadataArray, callFlags).CheckOk(); } } - public void StartSendMessage(SendCompletionHandler callback, byte[] payload, WriteFlags writeFlags, bool sendEmptyInitialMetadata) + public void StartSendMessage(ISendCompletionCallback callback, byte[] payload, WriteFlags writeFlags, bool sendEmptyInitialMetadata) { using (completionQueue.NewScope()) { var ctx = BatchContextSafeHandle.Create(); - completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success)); + completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, CompletionHandler_ISendCompletionCallback, callback); Native.grpcsharp_call_send_message(this, ctx, payload, new UIntPtr((ulong)payload.Length), writeFlags, sendEmptyInitialMetadata ? 1 : 0).CheckOk(); } } - public void StartSendCloseFromClient(SendCompletionHandler callback) + public void StartSendCloseFromClient(ISendCompletionCallback callback) { using (completionQueue.NewScope()) { var ctx = BatchContextSafeHandle.Create(); - completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success)); + completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, CompletionHandler_ISendCompletionCallback, callback); Native.grpcsharp_call_send_close_from_client(this, ctx).CheckOk(); } } - public void StartSendStatusFromServer(SendCompletionHandler callback, Status status, MetadataArraySafeHandle metadataArray, bool sendEmptyInitialMetadata, + public void StartSendStatusFromServer(ISendCompletionCallback callback, Status status, MetadataArraySafeHandle metadataArray, bool sendEmptyInitialMetadata, byte[] optionalPayload, WriteFlags writeFlags) { using (completionQueue.NewScope()) { var ctx = BatchContextSafeHandle.Create(); var optionalPayloadLength = optionalPayload != null ? new UIntPtr((ulong)optionalPayload.Length) : UIntPtr.Zero; - completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success)); + completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, CompletionHandler_ISendCompletionCallback, callback); var statusDetailBytes = MarshalUtils.GetBytesUTF8(status.Detail); Native.grpcsharp_call_send_status_from_server(this, ctx, status.StatusCode, statusDetailBytes, new UIntPtr((ulong)statusDetailBytes.Length), metadataArray, sendEmptyInitialMetadata ? 1 : 0, optionalPayload, optionalPayloadLength, writeFlags).CheckOk(); } } - public void StartReceiveMessage(ReceivedMessageHandler callback) + public void StartReceiveMessage(IReceivedMessageCallback callback) { using (completionQueue.NewScope()) { var ctx = BatchContextSafeHandle.Create(); - completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success, context.GetReceivedMessage())); + completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, CompletionHandler_IReceivedMessageCallback, callback); Native.grpcsharp_call_recv_message(this, ctx).CheckOk(); } } - public void StartReceiveInitialMetadata(ReceivedResponseHeadersHandler callback) + public void StartReceiveInitialMetadata(IReceivedResponseHeadersCallback callback) { using (completionQueue.NewScope()) { var ctx = BatchContextSafeHandle.Create(); - completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success, context.GetReceivedInitialMetadata())); + completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, CompletionHandler_IReceivedResponseHeadersCallback, callback); Native.grpcsharp_call_recv_initial_metadata(this, ctx).CheckOk(); } } - public void StartServerSide(ReceivedCloseOnServerHandler callback) + public void StartServerSide(IReceivedCloseOnServerCallback callback) { using (completionQueue.NewScope()) { var ctx = BatchContextSafeHandle.Create(); - completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success, context.GetReceivedCloseOnServerCancelled())); + completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, CompletionHandler_IReceivedCloseOnServerCallback, callback); Native.grpcsharp_call_start_serverside(this, ctx).CheckOk(); } } - public void StartSendInitialMetadata(SendCompletionHandler callback, MetadataArraySafeHandle metadataArray) + public void StartSendInitialMetadata(ISendCompletionCallback callback, MetadataArraySafeHandle metadataArray) { using (completionQueue.NewScope()) { var ctx = BatchContextSafeHandle.Create(); - completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success)); + completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, CompletionHandler_ISendCompletionCallback, callback); Native.grpcsharp_call_send_initial_metadata(this, ctx, metadataArray).CheckOk(); } } diff --git a/src/csharp/Grpc.Core/Internal/INativeCall.cs b/src/csharp/Grpc.Core/Internal/INativeCall.cs index f9c06583c8b..cedf82a0376 100644 --- a/src/csharp/Grpc.Core/Internal/INativeCall.cs +++ b/src/csharp/Grpc.Core/Internal/INativeCall.cs @@ -33,6 +33,37 @@ namespace Grpc.Core.Internal internal delegate void ReceivedCloseOnServerHandler(bool success, bool cancelled); + + internal interface IUnaryResponseClientCallback + { + void OnUnaryResponseClient(bool success, ClientSideStatus receivedStatus, byte[] receivedMessage, Metadata responseHeaders); + } + + internal interface IReceivedStatusOnClientCallback + { + void OnReceivedStatusOnClient(bool success, ClientSideStatus receivedStatus); + } + + internal interface IReceivedMessageCallback + { + void OnReceivedMessage(bool success, byte[] receivedMessage); + } + + internal interface IReceivedResponseHeadersCallback + { + void OnReceivedResponseHeaders(bool success, Metadata responseHeaders); + } + + internal interface ISendCompletionCallback + { + void OnSendCompletion(bool success); + } + + internal interface IReceivedCloseOnServerCallback + { + void OnReceivedCloseOnServerHandler(bool success, bool cancelled); + } + /// /// Abstraction of a native call object. /// From c5638887bfa899c796db911efde5ae770b7dc043 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Mon, 20 Nov 2017 16:06:13 +0100 Subject: [PATCH 64/70] make everything compile --- .../Internal/AsyncCallServerTest.cs | 28 ++--- .../Grpc.Core.Tests/Internal/AsyncCallTest.cs | 106 +++++++++--------- .../Internal/FakeNativeCall.cs | 58 +++++----- src/csharp/Grpc.Core/Internal/AsyncCall.cs | 37 ++++-- .../Grpc.Core/Internal/AsyncCallBase.cs | 20 +++- .../Grpc.Core/Internal/AsyncCallServer.cs | 22 +++- .../Grpc.Core/Internal/CallSafeHandle.cs | 8 +- src/csharp/Grpc.Core/Internal/INativeCall.cs | 27 +++-- .../SendMessageBenchmark.cs | 12 +- 9 files changed, 191 insertions(+), 127 deletions(-) diff --git a/src/csharp/Grpc.Core.Tests/Internal/AsyncCallServerTest.cs b/src/csharp/Grpc.Core.Tests/Internal/AsyncCallServerTest.cs index 9488ce29e9d..41d7ee40754 100644 --- a/src/csharp/Grpc.Core.Tests/Internal/AsyncCallServerTest.cs +++ b/src/csharp/Grpc.Core.Tests/Internal/AsyncCallServerTest.cs @@ -64,7 +64,7 @@ namespace Grpc.Core.Internal.Tests public void CancelNotificationAfterStartDisposes() { var finishedTask = asyncCallServer.ServerSideCallAsync(); - fakeCall.ReceivedCloseOnServerHandler(true, cancelled: true); + fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServerHandler(true, cancelled: true); AssertFinished(asyncCallServer, fakeCall, finishedTask); } @@ -76,8 +76,8 @@ namespace Grpc.Core.Internal.Tests var moveNextTask = requestStream.MoveNext(); - fakeCall.ReceivedCloseOnServerHandler(true, cancelled: true); - fakeCall.ReceivedMessageHandler(true, null); + fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServerHandler(true, cancelled: true); + fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null); Assert.IsFalse(moveNextTask.Result); AssertFinished(asyncCallServer, fakeCall, finishedTask); @@ -89,7 +89,7 @@ namespace Grpc.Core.Internal.Tests var finishedTask = asyncCallServer.ServerSideCallAsync(); var requestStream = new ServerRequestStream(asyncCallServer); - fakeCall.ReceivedCloseOnServerHandler(true, cancelled: true); + fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServerHandler(true, cancelled: true); // Check that starting a read after cancel notification has been processed is legal. var moveNextTask = requestStream.MoveNext(); @@ -107,10 +107,10 @@ namespace Grpc.Core.Internal.Tests // if a read completion's success==false, the request stream will silently finish // and we rely on C core cancelling the call. var moveNextTask = requestStream.MoveNext(); - fakeCall.ReceivedMessageHandler(false, null); + fakeCall.ReceivedMessageCallback.OnReceivedMessage(false, null); Assert.IsFalse(moveNextTask.Result); - fakeCall.ReceivedCloseOnServerHandler(true, cancelled: true); + fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServerHandler(true, cancelled: true); AssertFinished(asyncCallServer, fakeCall, finishedTask); } @@ -120,7 +120,7 @@ namespace Grpc.Core.Internal.Tests var finishedTask = asyncCallServer.ServerSideCallAsync(); var responseStream = new ServerResponseStream(asyncCallServer); - fakeCall.ReceivedCloseOnServerHandler(true, cancelled: true); + fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServerHandler(true, cancelled: true); // TODO(jtattermusch): should we throw a different exception type instead? Assert.Throws(typeof(InvalidOperationException), () => responseStream.WriteAsync("request1")); @@ -134,10 +134,10 @@ namespace Grpc.Core.Internal.Tests var responseStream = new ServerResponseStream(asyncCallServer); var writeTask = responseStream.WriteAsync("request1"); - fakeCall.SendCompletionHandler(false); + fakeCall.SendCompletionCallback.OnSendCompletion(false); Assert.ThrowsAsync(typeof(IOException), async () => await writeTask); - fakeCall.ReceivedCloseOnServerHandler(true, cancelled: true); + fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServerHandler(true, cancelled: true); AssertFinished(asyncCallServer, fakeCall, finishedTask); } @@ -150,13 +150,13 @@ namespace Grpc.Core.Internal.Tests var writeTask = responseStream.WriteAsync("request1"); var writeStatusTask = asyncCallServer.SendStatusFromServerAsync(Status.DefaultSuccess, new Metadata(), null); - fakeCall.SendCompletionHandler(true); - fakeCall.SendStatusFromServerHandler(true); + fakeCall.SendCompletionCallback.OnSendCompletion(true); + fakeCall.SendStatusFromServerCallback.OnSendStatusFromServerCompletion(true); Assert.DoesNotThrowAsync(async () => await writeTask); Assert.DoesNotThrowAsync(async () => await writeStatusTask); - fakeCall.ReceivedCloseOnServerHandler(true, cancelled: true); + fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServerHandler(true, cancelled: true); AssertFinished(asyncCallServer, fakeCall, finishedTask); } @@ -170,8 +170,8 @@ namespace Grpc.Core.Internal.Tests asyncCallServer.SendStatusFromServerAsync(Status.DefaultSuccess, new Metadata(), null); Assert.ThrowsAsync(typeof(InvalidOperationException), async () => await responseStream.WriteAsync("request1")); - fakeCall.SendStatusFromServerHandler(true); - fakeCall.ReceivedCloseOnServerHandler(true, cancelled: true); + fakeCall.SendStatusFromServerCallback.OnSendStatusFromServerCompletion(true); + fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServerHandler(true, cancelled: true); AssertFinished(asyncCallServer, fakeCall, finishedTask); } diff --git a/src/csharp/Grpc.Core.Tests/Internal/AsyncCallTest.cs b/src/csharp/Grpc.Core.Tests/Internal/AsyncCallTest.cs index b2b49f3a48d..9aab54d2d08 100644 --- a/src/csharp/Grpc.Core.Tests/Internal/AsyncCallTest.cs +++ b/src/csharp/Grpc.Core.Tests/Internal/AsyncCallTest.cs @@ -73,7 +73,7 @@ namespace Grpc.Core.Internal.Tests public void AsyncUnary_Success() { var resultTask = asyncCall.UnaryCallAsync("request1"); - fakeCall.UnaryResponseClientHandler(true, + fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata()), CreateResponsePayload(), new Metadata()); @@ -85,7 +85,7 @@ namespace Grpc.Core.Internal.Tests public void AsyncUnary_NonSuccessStatusCode() { var resultTask = asyncCall.UnaryCallAsync("request1"); - fakeCall.UnaryResponseClientHandler(true, + fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true, CreateClientSideStatus(StatusCode.InvalidArgument), null, new Metadata()); @@ -97,7 +97,7 @@ namespace Grpc.Core.Internal.Tests public void AsyncUnary_NullResponsePayload() { var resultTask = asyncCall.UnaryCallAsync("request1"); - fakeCall.UnaryResponseClientHandler(true, + fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata()), null, new Metadata()); @@ -118,7 +118,7 @@ namespace Grpc.Core.Internal.Tests public void ClientStreaming_NoRequest_Success() { var resultTask = asyncCall.ClientStreamingCallAsync(); - fakeCall.UnaryResponseClientHandler(true, + fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata()), CreateResponsePayload(), new Metadata()); @@ -130,7 +130,7 @@ namespace Grpc.Core.Internal.Tests public void ClientStreaming_NoRequest_NonSuccessStatusCode() { var resultTask = asyncCall.ClientStreamingCallAsync(); - fakeCall.UnaryResponseClientHandler(true, + fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true, CreateClientSideStatus(StatusCode.InvalidArgument), null, new Metadata()); @@ -145,18 +145,18 @@ namespace Grpc.Core.Internal.Tests var requestStream = new ClientRequestStream(asyncCall); var writeTask = requestStream.WriteAsync("request1"); - fakeCall.SendCompletionHandler(true); + fakeCall.SendCompletionCallback.OnSendCompletion(true); writeTask.Wait(); var writeTask2 = requestStream.WriteAsync("request2"); - fakeCall.SendCompletionHandler(true); + fakeCall.SendCompletionCallback.OnSendCompletion(true); writeTask2.Wait(); var completeTask = requestStream.CompleteAsync(); - fakeCall.SendCompletionHandler(true); + fakeCall.SendCompletionCallback.OnSendCompletion(true); completeTask.Wait(); - fakeCall.UnaryResponseClientHandler(true, + fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata()), CreateResponsePayload(), new Metadata()); @@ -171,12 +171,12 @@ namespace Grpc.Core.Internal.Tests var requestStream = new ClientRequestStream(asyncCall); var writeTask = requestStream.WriteAsync("request1"); - fakeCall.SendCompletionHandler(false); + fakeCall.SendCompletionCallback.OnSendCompletion(false); // The write will wait for call to finish to receive the status code. Assert.IsFalse(writeTask.IsCompleted); - fakeCall.UnaryResponseClientHandler(true, + fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true, CreateClientSideStatus(StatusCode.Internal), null, new Metadata()); @@ -195,12 +195,12 @@ namespace Grpc.Core.Internal.Tests var writeTask = requestStream.WriteAsync("request1"); - fakeCall.UnaryResponseClientHandler(true, + fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true, CreateClientSideStatus(StatusCode.Internal), null, new Metadata()); - fakeCall.SendCompletionHandler(false); + fakeCall.SendCompletionCallback.OnSendCompletion(false); var ex = Assert.ThrowsAsync(async () => await writeTask); Assert.AreEqual(StatusCode.Internal, ex.Status.StatusCode); @@ -215,13 +215,13 @@ namespace Grpc.Core.Internal.Tests var requestStream = new ClientRequestStream(asyncCall); var writeTask = requestStream.WriteAsync("request1"); - fakeCall.SendCompletionHandler(false); + fakeCall.SendCompletionCallback.OnSendCompletion(false); // Until the delayed write completion has been triggered, // we still act as if there was an active write. Assert.Throws(typeof(InvalidOperationException), () => requestStream.WriteAsync("request2")); - fakeCall.UnaryResponseClientHandler(true, + fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true, CreateClientSideStatus(StatusCode.Internal), null, new Metadata()); @@ -242,7 +242,7 @@ namespace Grpc.Core.Internal.Tests var resultTask = asyncCall.ClientStreamingCallAsync(); var requestStream = new ClientRequestStream(asyncCall); - fakeCall.UnaryResponseClientHandler(true, + fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata()), CreateResponsePayload(), new Metadata()); @@ -260,7 +260,7 @@ namespace Grpc.Core.Internal.Tests var resultTask = asyncCall.ClientStreamingCallAsync(); var requestStream = new ClientRequestStream(asyncCall); - fakeCall.UnaryResponseClientHandler(true, + fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true, new ClientSideStatus(new Status(StatusCode.OutOfRange, ""), new Metadata()), CreateResponsePayload(), new Metadata()); @@ -282,9 +282,9 @@ namespace Grpc.Core.Internal.Tests Assert.Throws(typeof(InvalidOperationException), () => requestStream.WriteAsync("request1")); - fakeCall.SendCompletionHandler(true); + fakeCall.SendCompletionCallback.OnSendCompletion(true); - fakeCall.UnaryResponseClientHandler(true, + fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata()), CreateResponsePayload(), new Metadata()); @@ -298,7 +298,7 @@ namespace Grpc.Core.Internal.Tests var resultTask = asyncCall.ClientStreamingCallAsync(); var requestStream = new ClientRequestStream(asyncCall); - fakeCall.UnaryResponseClientHandler(true, + fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata()), CreateResponsePayload(), new Metadata()); @@ -319,7 +319,7 @@ namespace Grpc.Core.Internal.Tests var writeTask = requestStream.WriteAsync("request1"); Assert.ThrowsAsync(typeof(TaskCanceledException), async () => await writeTask); - fakeCall.UnaryResponseClientHandler(true, + fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true, CreateClientSideStatus(StatusCode.Cancelled), null, new Metadata()); @@ -342,11 +342,11 @@ namespace Grpc.Core.Internal.Tests var responseStream = new ClientResponseStream(asyncCall); var readTask = responseStream.MoveNext(); - fakeCall.ReceivedResponseHeadersHandler(true, new Metadata()); + fakeCall.ReceivedResponseHeadersCallback.OnReceivedResponseHeaders(true, new Metadata()); Assert.AreEqual(0, asyncCall.ResponseHeadersAsync.Result.Count); - fakeCall.ReceivedMessageHandler(true, null); - fakeCall.ReceivedStatusOnClientHandler(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata())); + fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null); + fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata())); AssertStreamingResponseSuccess(asyncCall, fakeCall, readTask); } @@ -359,8 +359,8 @@ namespace Grpc.Core.Internal.Tests var readTask = responseStream.MoveNext(); // try alternative order of completions - fakeCall.ReceivedStatusOnClientHandler(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata())); - fakeCall.ReceivedMessageHandler(true, null); + fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata())); + fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null); AssertStreamingResponseSuccess(asyncCall, fakeCall, readTask); } @@ -372,8 +372,8 @@ namespace Grpc.Core.Internal.Tests var responseStream = new ClientResponseStream(asyncCall); var readTask = responseStream.MoveNext(); - fakeCall.ReceivedMessageHandler(false, null); // after a failed read, we rely on C core to deliver appropriate status code. - fakeCall.ReceivedStatusOnClientHandler(true, CreateClientSideStatus(StatusCode.Internal)); + fakeCall.ReceivedMessageCallback.OnReceivedMessage(false, null); // after a failed read, we rely on C core to deliver appropriate status code. + fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, CreateClientSideStatus(StatusCode.Internal)); AssertStreamingResponseError(asyncCall, fakeCall, readTask, StatusCode.Internal); } @@ -385,18 +385,18 @@ namespace Grpc.Core.Internal.Tests var responseStream = new ClientResponseStream(asyncCall); var readTask1 = responseStream.MoveNext(); - fakeCall.ReceivedMessageHandler(true, CreateResponsePayload()); + fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateResponsePayload()); Assert.IsTrue(readTask1.Result); Assert.AreEqual("response1", responseStream.Current); var readTask2 = responseStream.MoveNext(); - fakeCall.ReceivedMessageHandler(true, CreateResponsePayload()); + fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateResponsePayload()); Assert.IsTrue(readTask2.Result); Assert.AreEqual("response1", responseStream.Current); var readTask3 = responseStream.MoveNext(); - fakeCall.ReceivedStatusOnClientHandler(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata())); - fakeCall.ReceivedMessageHandler(true, null); + fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata())); + fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null); AssertStreamingResponseSuccess(asyncCall, fakeCall, readTask3); } @@ -409,12 +409,12 @@ namespace Grpc.Core.Internal.Tests var responseStream = new ClientResponseStream(asyncCall); var writeTask1 = requestStream.CompleteAsync(); - fakeCall.SendCompletionHandler(true); + fakeCall.SendCompletionCallback.OnSendCompletion(true); Assert.DoesNotThrowAsync(async () => await writeTask1); var readTask = responseStream.MoveNext(); - fakeCall.ReceivedMessageHandler(true, null); - fakeCall.ReceivedStatusOnClientHandler(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata())); + fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null); + fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata())); AssertStreamingResponseSuccess(asyncCall, fakeCall, readTask); } @@ -427,8 +427,8 @@ namespace Grpc.Core.Internal.Tests var responseStream = new ClientResponseStream(asyncCall); var readTask = responseStream.MoveNext(); - fakeCall.ReceivedMessageHandler(true, null); - fakeCall.ReceivedStatusOnClientHandler(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata())); + fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null); + fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata())); AssertStreamingResponseSuccess(asyncCall, fakeCall, readTask); @@ -445,8 +445,8 @@ namespace Grpc.Core.Internal.Tests var responseStream = new ClientResponseStream(asyncCall); var readTask = responseStream.MoveNext(); - fakeCall.ReceivedMessageHandler(true, null); - fakeCall.ReceivedStatusOnClientHandler(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata())); + fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null); + fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata())); AssertStreamingResponseSuccess(asyncCall, fakeCall, readTask); @@ -461,14 +461,14 @@ namespace Grpc.Core.Internal.Tests var responseStream = new ClientResponseStream(asyncCall); var writeTask = requestStream.WriteAsync("request1"); - fakeCall.SendCompletionHandler(false); + fakeCall.SendCompletionCallback.OnSendCompletion(false); // The write will wait for call to finish to receive the status code. Assert.IsFalse(writeTask.IsCompleted); var readTask = responseStream.MoveNext(); - fakeCall.ReceivedMessageHandler(true, null); - fakeCall.ReceivedStatusOnClientHandler(true, CreateClientSideStatus(StatusCode.PermissionDenied)); + fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null); + fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, CreateClientSideStatus(StatusCode.PermissionDenied)); var ex = Assert.ThrowsAsync(async () => await writeTask); Assert.AreEqual(StatusCode.PermissionDenied, ex.Status.StatusCode); @@ -486,9 +486,9 @@ namespace Grpc.Core.Internal.Tests var writeTask = requestStream.WriteAsync("request1"); var readTask = responseStream.MoveNext(); - fakeCall.ReceivedMessageHandler(true, null); - fakeCall.ReceivedStatusOnClientHandler(true, CreateClientSideStatus(StatusCode.PermissionDenied)); - fakeCall.SendCompletionHandler(false); + fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null); + fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, CreateClientSideStatus(StatusCode.PermissionDenied)); + fakeCall.SendCompletionCallback.OnSendCompletion(false); var ex = Assert.ThrowsAsync(async () => await writeTask); Assert.AreEqual(StatusCode.PermissionDenied, ex.Status.StatusCode); @@ -510,8 +510,8 @@ namespace Grpc.Core.Internal.Tests Assert.ThrowsAsync(typeof(TaskCanceledException), async () => await writeTask); var readTask = responseStream.MoveNext(); - fakeCall.ReceivedMessageHandler(true, null); - fakeCall.ReceivedStatusOnClientHandler(true, CreateClientSideStatus(StatusCode.Cancelled)); + fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null); + fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, CreateClientSideStatus(StatusCode.Cancelled)); AssertStreamingResponseError(asyncCall, fakeCall, readTask, StatusCode.Cancelled); } @@ -526,13 +526,13 @@ namespace Grpc.Core.Internal.Tests Assert.IsTrue(fakeCall.IsCancelled); var readTask1 = responseStream.MoveNext(); - fakeCall.ReceivedMessageHandler(true, CreateResponsePayload()); + fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateResponsePayload()); Assert.IsTrue(readTask1.Result); Assert.AreEqual("response1", responseStream.Current); var readTask2 = responseStream.MoveNext(); - fakeCall.ReceivedMessageHandler(true, null); - fakeCall.ReceivedStatusOnClientHandler(true, CreateClientSideStatus(StatusCode.Cancelled)); + fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null); + fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, CreateClientSideStatus(StatusCode.Cancelled)); AssertStreamingResponseError(asyncCall, fakeCall, readTask2, StatusCode.Cancelled); } @@ -547,13 +547,13 @@ namespace Grpc.Core.Internal.Tests asyncCall.Cancel(); Assert.IsTrue(fakeCall.IsCancelled); - fakeCall.ReceivedMessageHandler(true, CreateResponsePayload()); + fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateResponsePayload()); Assert.IsTrue(readTask1.Result); Assert.AreEqual("response1", responseStream.Current); var readTask2 = responseStream.MoveNext(); - fakeCall.ReceivedMessageHandler(true, null); - fakeCall.ReceivedStatusOnClientHandler(true, CreateClientSideStatus(StatusCode.Cancelled)); + fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null); + fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, CreateClientSideStatus(StatusCode.Cancelled)); AssertStreamingResponseError(asyncCall, fakeCall, readTask2, StatusCode.Cancelled); } diff --git a/src/csharp/Grpc.Core.Tests/Internal/FakeNativeCall.cs b/src/csharp/Grpc.Core.Tests/Internal/FakeNativeCall.cs index c3a27167f9f..581ac3384be 100644 --- a/src/csharp/Grpc.Core.Tests/Internal/FakeNativeCall.cs +++ b/src/csharp/Grpc.Core.Tests/Internal/FakeNativeCall.cs @@ -31,43 +31,43 @@ namespace Grpc.Core.Internal.Tests /// internal class FakeNativeCall : INativeCall { - public UnaryResponseClientHandler UnaryResponseClientHandler + public IUnaryResponseClientCallback UnaryResponseClientCallback { get; set; } - public ReceivedStatusOnClientHandler ReceivedStatusOnClientHandler + public IReceivedStatusOnClientCallback ReceivedStatusOnClientCallback { get; set; } - public ReceivedMessageHandler ReceivedMessageHandler + public IReceivedMessageCallback ReceivedMessageCallback { get; set; } - public ReceivedResponseHeadersHandler ReceivedResponseHeadersHandler + public IReceivedResponseHeadersCallback ReceivedResponseHeadersCallback { get; set; } - public SendCompletionHandler SendCompletionHandler + public ISendCompletionCallback SendCompletionCallback { get; set; } - public SendCompletionHandler SendStatusFromServerHandler + public ISendStatusFromServerCompletionCallback SendStatusFromServerCallback { get; set; } - public ReceivedCloseOnServerHandler ReceivedCloseOnServerHandler + public IReceivedCloseOnServerCallback ReceivedCloseOnServerCallback { get; set; @@ -100,9 +100,9 @@ namespace Grpc.Core.Internal.Tests return "PEER"; } - public void StartUnary(UnaryResponseClientHandler callback, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags) + public void StartUnary(IUnaryResponseClientCallback callback, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags) { - UnaryResponseClientHandler = callback; + UnaryResponseClientCallback = callback; } public void StartUnary(BatchContextSafeHandle ctx, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags) @@ -110,55 +110,55 @@ namespace Grpc.Core.Internal.Tests throw new NotImplementedException(); } - public void StartClientStreaming(UnaryResponseClientHandler callback, MetadataArraySafeHandle metadataArray, CallFlags callFlags) + public void StartClientStreaming(IUnaryResponseClientCallback callback, MetadataArraySafeHandle metadataArray, CallFlags callFlags) { - UnaryResponseClientHandler = callback; + UnaryResponseClientCallback = callback; } - public void StartServerStreaming(ReceivedStatusOnClientHandler callback, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags) + public void StartServerStreaming(IReceivedStatusOnClientCallback callback, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags) { - ReceivedStatusOnClientHandler = callback; + ReceivedStatusOnClientCallback = callback; } - public void StartDuplexStreaming(ReceivedStatusOnClientHandler callback, MetadataArraySafeHandle metadataArray, CallFlags callFlags) + public void StartDuplexStreaming(IReceivedStatusOnClientCallback callback, MetadataArraySafeHandle metadataArray, CallFlags callFlags) { - ReceivedStatusOnClientHandler = callback; + ReceivedStatusOnClientCallback = callback; } - public void StartReceiveMessage(ReceivedMessageHandler callback) + public void StartReceiveMessage(IReceivedMessageCallback callback) { - ReceivedMessageHandler = callback; + ReceivedMessageCallback = callback; } - public void StartReceiveInitialMetadata(ReceivedResponseHeadersHandler callback) + public void StartReceiveInitialMetadata(IReceivedResponseHeadersCallback callback) { - ReceivedResponseHeadersHandler = callback; + ReceivedResponseHeadersCallback = callback; } - public void StartSendInitialMetadata(SendCompletionHandler callback, MetadataArraySafeHandle metadataArray) + public void StartSendInitialMetadata(ISendCompletionCallback callback, MetadataArraySafeHandle metadataArray) { - SendCompletionHandler = callback; + SendCompletionCallback = callback; } - public void StartSendMessage(SendCompletionHandler callback, byte[] payload, WriteFlags writeFlags, bool sendEmptyInitialMetadata) + public void StartSendMessage(ISendCompletionCallback callback, byte[] payload, WriteFlags writeFlags, bool sendEmptyInitialMetadata) { - SendCompletionHandler = callback; + SendCompletionCallback = callback; } - public void StartSendCloseFromClient(SendCompletionHandler callback) + public void StartSendCloseFromClient(ISendCompletionCallback callback) { - SendCompletionHandler = callback; + SendCompletionCallback = callback; } - public void StartSendStatusFromServer(SendCompletionHandler callback, Status status, MetadataArraySafeHandle metadataArray, bool sendEmptyInitialMetadata, + public void StartSendStatusFromServer(ISendStatusFromServerCompletionCallback callback, Status status, MetadataArraySafeHandle metadataArray, bool sendEmptyInitialMetadata, byte[] optionalPayload, WriteFlags writeFlags) { - SendStatusFromServerHandler = callback; + SendStatusFromServerCallback = callback; } - public void StartServerSide(ReceivedCloseOnServerHandler callback) + public void StartServerSide(IReceivedCloseOnServerCallback callback) { - ReceivedCloseOnServerHandler = callback; + ReceivedCloseOnServerCallback = callback; } public void Dispose() diff --git a/src/csharp/Grpc.Core/Internal/AsyncCall.cs b/src/csharp/Grpc.Core/Internal/AsyncCall.cs index 09fb722c816..aa2161267a5 100644 --- a/src/csharp/Grpc.Core/Internal/AsyncCall.cs +++ b/src/csharp/Grpc.Core/Internal/AsyncCall.cs @@ -27,7 +27,7 @@ namespace Grpc.Core.Internal /// /// Manages client side native call lifecycle. /// - internal class AsyncCall : AsyncCallBase + internal class AsyncCall : AsyncCallBase, IUnaryResponseClientCallback, IReceivedStatusOnClientCallback, IReceivedResponseHeadersCallback { static readonly ILogger Logger = GrpcEnvironment.Logger.ForType>(); @@ -138,7 +138,7 @@ namespace Grpc.Core.Internal unaryResponseTcs = new TaskCompletionSource(); using (var metadataArray = MetadataArraySafeHandle.Create(details.Options.Headers)) { - call.StartUnary(HandleUnaryResponse, payload, GetWriteFlagsForCall(), metadataArray, details.Options.Flags); + call.StartUnary(UnaryResponseClientCallback, payload, GetWriteFlagsForCall(), metadataArray, details.Options.Flags); } return unaryResponseTcs.Task; } @@ -162,7 +162,7 @@ namespace Grpc.Core.Internal unaryResponseTcs = new TaskCompletionSource(); using (var metadataArray = MetadataArraySafeHandle.Create(details.Options.Headers)) { - call.StartClientStreaming(HandleUnaryResponse, metadataArray, details.Options.Flags); + call.StartClientStreaming(UnaryResponseClientCallback, metadataArray, details.Options.Flags); } return unaryResponseTcs.Task; @@ -188,9 +188,9 @@ namespace Grpc.Core.Internal streamingResponseCallFinishedTcs = new TaskCompletionSource(); using (var metadataArray = MetadataArraySafeHandle.Create(details.Options.Headers)) { - call.StartServerStreaming(HandleFinished, payload, GetWriteFlagsForCall(), metadataArray, details.Options.Flags); + call.StartServerStreaming(ReceivedStatusOnClientCallback, payload, GetWriteFlagsForCall(), metadataArray, details.Options.Flags); } - call.StartReceiveInitialMetadata(HandleReceivedResponseHeaders); + call.StartReceiveInitialMetadata(ReceivedResponseHeadersCallback); } } @@ -210,9 +210,9 @@ namespace Grpc.Core.Internal streamingResponseCallFinishedTcs = new TaskCompletionSource(); using (var metadataArray = MetadataArraySafeHandle.Create(details.Options.Headers)) { - call.StartDuplexStreaming(HandleFinished, metadataArray, details.Options.Flags); + call.StartDuplexStreaming(ReceivedStatusOnClientCallback, metadataArray, details.Options.Flags); } - call.StartReceiveInitialMetadata(HandleReceivedResponseHeaders); + call.StartReceiveInitialMetadata(ReceivedResponseHeadersCallback); } } @@ -256,7 +256,7 @@ namespace Grpc.Core.Internal halfcloseRequested = true; return TaskUtils.CompletedTask; } - call.StartSendCloseFromClient(HandleSendFinished); + call.StartSendCloseFromClient(SendCompletionCallback); halfcloseRequested = true; streamingWriteTcs = new TaskCompletionSource(); @@ -516,5 +516,26 @@ namespace Grpc.Core.Internal streamingResponseCallFinishedTcs.SetResult(null); } + + IUnaryResponseClientCallback UnaryResponseClientCallback => this; + + void IUnaryResponseClientCallback.OnUnaryResponseClient(bool success, ClientSideStatus receivedStatus, byte[] receivedMessage, Metadata responseHeaders) + { + HandleUnaryResponse(success, receivedStatus, receivedMessage, responseHeaders); + } + + IReceivedStatusOnClientCallback ReceivedStatusOnClientCallback => this; + + void IReceivedStatusOnClientCallback.OnReceivedStatusOnClient(bool success, ClientSideStatus receivedStatus) + { + HandleFinished(success, receivedStatus); + } + + IReceivedResponseHeadersCallback ReceivedResponseHeadersCallback => this; + + void IReceivedResponseHeadersCallback.OnReceivedResponseHeaders(bool success, Metadata responseHeaders) + { + HandleReceivedResponseHeaders(success, responseHeaders); + } } } diff --git a/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs b/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs index f379c85e00c..3273c26b88f 100644 --- a/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs +++ b/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs @@ -35,7 +35,7 @@ namespace Grpc.Core.Internal /// Base for handling both client side and server side calls. /// Manages native call lifecycle and provides convenience methods. /// - internal abstract class AsyncCallBase + internal abstract class AsyncCallBase : IReceivedMessageCallback, ISendCompletionCallback { static readonly ILogger Logger = GrpcEnvironment.Logger.ForType>(); protected static readonly Status DeserializeResponseFailureStatus = new Status(StatusCode.Internal, "Failed to deserialize response message."); @@ -126,7 +126,7 @@ namespace Grpc.Core.Internal return earlyResult; } - call.StartSendMessage(HandleSendFinished, payload, writeFlags, !initialMetadataSent); + call.StartSendMessage(SendCompletionCallback, payload, writeFlags, !initialMetadataSent); initialMetadataSent = true; streamingWritesCounter++; @@ -154,7 +154,7 @@ namespace Grpc.Core.Internal GrpcPreconditions.CheckState(streamingReadTcs == null, "Only one read can be pending at a time"); GrpcPreconditions.CheckState(!disposed); - call.StartReceiveMessage(HandleReadFinished); + call.StartReceiveMessage(ReceivedMessageCallback); streamingReadTcs = new TaskCompletionSource(); return streamingReadTcs.Task; } @@ -342,5 +342,19 @@ namespace Grpc.Core.Internal } origTcs.SetResult(msg); } + + protected ISendCompletionCallback SendCompletionCallback => this; + + void ISendCompletionCallback.OnSendCompletion(bool success) + { + HandleSendFinished(success); + } + + IReceivedMessageCallback ReceivedMessageCallback => this; + + void IReceivedMessageCallback.OnReceivedMessage(bool success, byte[] receivedMessage) + { + HandleReadFinished(success, receivedMessage); + } } } diff --git a/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs b/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs index 271a6ffadfa..4f445149741 100644 --- a/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs +++ b/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs @@ -31,7 +31,7 @@ namespace Grpc.Core.Internal /// /// Manages server side native call lifecycle. /// - internal class AsyncCallServer : AsyncCallBase + internal class AsyncCallServer : AsyncCallBase, IReceivedCloseOnServerCallback, ISendStatusFromServerCompletionCallback { readonly TaskCompletionSource finishedServersideTcs = new TaskCompletionSource(); readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); @@ -70,7 +70,7 @@ namespace Grpc.Core.Internal started = true; - call.StartServerSide(HandleFinishedServerside); + call.StartServerSide(ReceiveCloseOnServerCallback); return finishedServersideTcs.Task; } } @@ -114,7 +114,7 @@ namespace Grpc.Core.Internal using (var metadataArray = MetadataArraySafeHandle.Create(headers)) { - call.StartSendInitialMetadata(HandleSendFinished, metadataArray); + call.StartSendInitialMetadata(SendCompletionCallback, metadataArray); } this.initialMetadataSent = true; @@ -140,7 +140,7 @@ namespace Grpc.Core.Internal using (var metadataArray = MetadataArraySafeHandle.Create(trailers)) { - call.StartSendStatusFromServer(HandleSendStatusFromServerFinished, status, metadataArray, !initialMetadataSent, + call.StartSendStatusFromServer(SendStatusFromServerCompletionCallback, status, metadataArray, !initialMetadataSent, payload, writeFlags); } halfcloseRequested = true; @@ -227,5 +227,19 @@ namespace Grpc.Core.Internal finishedServersideTcs.SetResult(null); } + + IReceivedCloseOnServerCallback ReceiveCloseOnServerCallback => this; + + void IReceivedCloseOnServerCallback.OnReceivedCloseOnServerHandler(bool success, bool cancelled) + { + HandleFinishedServerside(success, cancelled); + } + + ISendStatusFromServerCompletionCallback SendStatusFromServerCompletionCallback => this; + + void ISendStatusFromServerCompletionCallback.OnSendStatusFromServerCompletion(bool success) + { + HandleSendStatusFromServerFinished(success); + } } } diff --git a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs index 589f6cbe169..7316bc27878 100644 --- a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs @@ -44,6 +44,8 @@ namespace Grpc.Core.Internal (success, context, state) => ((IReceivedResponseHeadersCallback)state).OnReceivedResponseHeaders(success, context.GetReceivedInitialMetadata()); static readonly BatchCompletionDelegate CompletionHandler_ISendCompletionCallback = (success, context, state) => ((ISendCompletionCallback)state).OnSendCompletion(success); + static readonly BatchCompletionDelegate CompletionHandler_ISendStatusFromServerCompletionCallback = + (success, context, state) => ((ISendStatusFromServerCompletionCallback)state).OnSendStatusFromServerCompletion(success); static readonly BatchCompletionDelegate CompletionHandler_IReceivedCloseOnServerCallback = (success, context, state) => ((IReceivedCloseOnServerCallback)state).OnReceivedCloseOnServerHandler(success, context.GetReceivedCloseOnServerCancelled()); @@ -81,7 +83,7 @@ namespace Grpc.Core.Internal .CheckOk(); } - public void StartClientStreaming(UnaryResponseClientHandler callback, MetadataArraySafeHandle metadataArray, CallFlags callFlags) + public void StartClientStreaming(IUnaryResponseClientCallback callback, MetadataArraySafeHandle metadataArray, CallFlags callFlags) { using (completionQueue.NewScope()) { @@ -131,14 +133,14 @@ namespace Grpc.Core.Internal } } - public void StartSendStatusFromServer(ISendCompletionCallback callback, Status status, MetadataArraySafeHandle metadataArray, bool sendEmptyInitialMetadata, + public void StartSendStatusFromServer(ISendStatusFromServerCompletionCallback callback, Status status, MetadataArraySafeHandle metadataArray, bool sendEmptyInitialMetadata, byte[] optionalPayload, WriteFlags writeFlags) { using (completionQueue.NewScope()) { var ctx = BatchContextSafeHandle.Create(); var optionalPayloadLength = optionalPayload != null ? new UIntPtr((ulong)optionalPayload.Length) : UIntPtr.Zero; - completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, CompletionHandler_ISendCompletionCallback, callback); + completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, CompletionHandler_ISendStatusFromServerCompletionCallback, callback); var statusDetailBytes = MarshalUtils.GetBytesUTF8(status.Detail); Native.grpcsharp_call_send_status_from_server(this, ctx, status.StatusCode, statusDetailBytes, new UIntPtr((ulong)statusDetailBytes.Length), metadataArray, sendEmptyInitialMetadata ? 1 : 0, optionalPayload, optionalPayloadLength, writeFlags).CheckOk(); diff --git a/src/csharp/Grpc.Core/Internal/INativeCall.cs b/src/csharp/Grpc.Core/Internal/INativeCall.cs index cedf82a0376..3474ad6208d 100644 --- a/src/csharp/Grpc.Core/Internal/INativeCall.cs +++ b/src/csharp/Grpc.Core/Internal/INativeCall.cs @@ -59,6 +59,11 @@ namespace Grpc.Core.Internal void OnSendCompletion(bool success); } + internal interface ISendStatusFromServerCompletionCallback + { + void OnSendStatusFromServerCompletion(bool success); + } + internal interface IReceivedCloseOnServerCallback { void OnReceivedCloseOnServerHandler(bool success, bool cancelled); @@ -75,28 +80,28 @@ namespace Grpc.Core.Internal string GetPeer(); - void StartUnary(UnaryResponseClientHandler callback, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags); + void StartUnary(IUnaryResponseClientCallback callback, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags); void StartUnary(BatchContextSafeHandle ctx, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags); - void StartClientStreaming(UnaryResponseClientHandler callback, MetadataArraySafeHandle metadataArray, CallFlags callFlags); + void StartClientStreaming(IUnaryResponseClientCallback callback, MetadataArraySafeHandle metadataArray, CallFlags callFlags); - void StartServerStreaming(ReceivedStatusOnClientHandler callback, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags); + void StartServerStreaming(IReceivedStatusOnClientCallback callback, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags); - void StartDuplexStreaming(ReceivedStatusOnClientHandler callback, MetadataArraySafeHandle metadataArray, CallFlags callFlags); + void StartDuplexStreaming(IReceivedStatusOnClientCallback callback, MetadataArraySafeHandle metadataArray, CallFlags callFlags); - void StartReceiveMessage(ReceivedMessageHandler callback); + void StartReceiveMessage(IReceivedMessageCallback callback); - void StartReceiveInitialMetadata(ReceivedResponseHeadersHandler callback); + void StartReceiveInitialMetadata(IReceivedResponseHeadersCallback callback); - void StartSendInitialMetadata(SendCompletionHandler callback, MetadataArraySafeHandle metadataArray); + void StartSendInitialMetadata(ISendCompletionCallback callback, MetadataArraySafeHandle metadataArray); - void StartSendMessage(SendCompletionHandler callback, byte[] payload, WriteFlags writeFlags, bool sendEmptyInitialMetadata); + void StartSendMessage(ISendCompletionCallback callback, byte[] payload, WriteFlags writeFlags, bool sendEmptyInitialMetadata); - void StartSendCloseFromClient(SendCompletionHandler callback); + void StartSendCloseFromClient(ISendCompletionCallback callback); - void StartSendStatusFromServer(SendCompletionHandler callback, Status status, MetadataArraySafeHandle metadataArray, bool sendEmptyInitialMetadata, byte[] optionalPayload, WriteFlags writeFlags); + void StartSendStatusFromServer(ISendStatusFromServerCompletionCallback callback, Status status, MetadataArraySafeHandle metadataArray, bool sendEmptyInitialMetadata, byte[] optionalPayload, WriteFlags writeFlags); - void StartServerSide(ReceivedCloseOnServerHandler callback); + void StartServerSide(IReceivedCloseOnServerCallback callback); } } diff --git a/src/csharp/Grpc.Microbenchmarks/SendMessageBenchmark.cs b/src/csharp/Grpc.Microbenchmarks/SendMessageBenchmark.cs index 6a8a29d7701..9cff97eb883 100644 --- a/src/csharp/Grpc.Microbenchmarks/SendMessageBenchmark.cs +++ b/src/csharp/Grpc.Microbenchmarks/SendMessageBenchmark.cs @@ -59,14 +59,14 @@ namespace Grpc.Microbenchmarks var cq = CompletionQueueSafeHandle.CreateAsync(completionRegistry); var call = CreateFakeCall(cq); - var sendCompletionHandler = new SendCompletionHandler((success) => { }); + var sendCompletionCallback = new NopSendCompletionCallback(); var payload = new byte[payloadSize]; var writeFlags = default(WriteFlags); var stopwatch = Stopwatch.StartNew(); for (int i = 0; i < iterations; i++) { - call.StartSendMessage(sendCompletionHandler, payload, writeFlags, false); + call.StartSendMessage(sendCompletionCallback, payload, writeFlags, false); var callback = completionRegistry.Extract(completionRegistry.LastRegisteredKey); callback.OnComplete(true); } @@ -87,5 +87,13 @@ namespace Grpc.Microbenchmarks } return call; } + + private class NopSendCompletionCallback : ISendCompletionCallback + { + public void OnSendCompletion(bool success) + { + // NOP + } + } } } From 2622fdc07d5a82bd71c08f836b54237984000a88 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Mon, 20 Nov 2017 16:38:05 +0100 Subject: [PATCH 65/70] remove legacy delegates --- src/csharp/Grpc.Core/Internal/INativeCall.cs | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/csharp/Grpc.Core/Internal/INativeCall.cs b/src/csharp/Grpc.Core/Internal/INativeCall.cs index 3474ad6208d..717c9460abe 100644 --- a/src/csharp/Grpc.Core/Internal/INativeCall.cs +++ b/src/csharp/Grpc.Core/Internal/INativeCall.cs @@ -20,25 +20,12 @@ using Grpc.Core; namespace Grpc.Core.Internal { - internal delegate void UnaryResponseClientHandler(bool success, ClientSideStatus receivedStatus, byte[] receivedMessage, Metadata responseHeaders); - - // Received status for streaming response calls. - internal delegate void ReceivedStatusOnClientHandler(bool success, ClientSideStatus receivedStatus); - - internal delegate void ReceivedMessageHandler(bool success, byte[] receivedMessage); - - internal delegate void ReceivedResponseHeadersHandler(bool success, Metadata responseHeaders); - - internal delegate void SendCompletionHandler(bool success); - - internal delegate void ReceivedCloseOnServerHandler(bool success, bool cancelled); - - internal interface IUnaryResponseClientCallback { void OnUnaryResponseClient(bool success, ClientSideStatus receivedStatus, byte[] receivedMessage, Metadata responseHeaders); } + // Received status for streaming response calls. internal interface IReceivedStatusOnClientCallback { void OnReceivedStatusOnClient(bool success, ClientSideStatus receivedStatus); From 85807230fb6c9cb6c65d3976e97ac0720323678d Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Tue, 21 Nov 2017 11:48:29 +0100 Subject: [PATCH 66/70] consistent naming for OnReceivedCloseOnServer --- .../Internal/AsyncCallServerTest.cs | 16 ++++++++-------- src/csharp/Grpc.Core/Internal/AsyncCallServer.cs | 2 +- src/csharp/Grpc.Core/Internal/CallSafeHandle.cs | 2 +- src/csharp/Grpc.Core/Internal/INativeCall.cs | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/csharp/Grpc.Core.Tests/Internal/AsyncCallServerTest.cs b/src/csharp/Grpc.Core.Tests/Internal/AsyncCallServerTest.cs index 41d7ee40754..e7d89399789 100644 --- a/src/csharp/Grpc.Core.Tests/Internal/AsyncCallServerTest.cs +++ b/src/csharp/Grpc.Core.Tests/Internal/AsyncCallServerTest.cs @@ -64,7 +64,7 @@ namespace Grpc.Core.Internal.Tests public void CancelNotificationAfterStartDisposes() { var finishedTask = asyncCallServer.ServerSideCallAsync(); - fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServerHandler(true, cancelled: true); + fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true); AssertFinished(asyncCallServer, fakeCall, finishedTask); } @@ -76,7 +76,7 @@ namespace Grpc.Core.Internal.Tests var moveNextTask = requestStream.MoveNext(); - fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServerHandler(true, cancelled: true); + fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true); fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null); Assert.IsFalse(moveNextTask.Result); @@ -89,7 +89,7 @@ namespace Grpc.Core.Internal.Tests var finishedTask = asyncCallServer.ServerSideCallAsync(); var requestStream = new ServerRequestStream(asyncCallServer); - fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServerHandler(true, cancelled: true); + fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true); // Check that starting a read after cancel notification has been processed is legal. var moveNextTask = requestStream.MoveNext(); @@ -110,7 +110,7 @@ namespace Grpc.Core.Internal.Tests fakeCall.ReceivedMessageCallback.OnReceivedMessage(false, null); Assert.IsFalse(moveNextTask.Result); - fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServerHandler(true, cancelled: true); + fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true); AssertFinished(asyncCallServer, fakeCall, finishedTask); } @@ -120,7 +120,7 @@ namespace Grpc.Core.Internal.Tests var finishedTask = asyncCallServer.ServerSideCallAsync(); var responseStream = new ServerResponseStream(asyncCallServer); - fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServerHandler(true, cancelled: true); + fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true); // TODO(jtattermusch): should we throw a different exception type instead? Assert.Throws(typeof(InvalidOperationException), () => responseStream.WriteAsync("request1")); @@ -137,7 +137,7 @@ namespace Grpc.Core.Internal.Tests fakeCall.SendCompletionCallback.OnSendCompletion(false); Assert.ThrowsAsync(typeof(IOException), async () => await writeTask); - fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServerHandler(true, cancelled: true); + fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true); AssertFinished(asyncCallServer, fakeCall, finishedTask); } @@ -156,7 +156,7 @@ namespace Grpc.Core.Internal.Tests Assert.DoesNotThrowAsync(async () => await writeTask); Assert.DoesNotThrowAsync(async () => await writeStatusTask); - fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServerHandler(true, cancelled: true); + fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true); AssertFinished(asyncCallServer, fakeCall, finishedTask); } @@ -171,7 +171,7 @@ namespace Grpc.Core.Internal.Tests Assert.ThrowsAsync(typeof(InvalidOperationException), async () => await responseStream.WriteAsync("request1")); fakeCall.SendStatusFromServerCallback.OnSendStatusFromServerCompletion(true); - fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServerHandler(true, cancelled: true); + fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true); AssertFinished(asyncCallServer, fakeCall, finishedTask); } diff --git a/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs b/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs index 4f445149741..bb8287c5491 100644 --- a/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs +++ b/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs @@ -230,7 +230,7 @@ namespace Grpc.Core.Internal IReceivedCloseOnServerCallback ReceiveCloseOnServerCallback => this; - void IReceivedCloseOnServerCallback.OnReceivedCloseOnServerHandler(bool success, bool cancelled) + void IReceivedCloseOnServerCallback.OnReceivedCloseOnServer(bool success, bool cancelled) { HandleFinishedServerside(success, cancelled); } diff --git a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs index 7316bc27878..d6a5ba586bd 100644 --- a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs @@ -47,7 +47,7 @@ namespace Grpc.Core.Internal static readonly BatchCompletionDelegate CompletionHandler_ISendStatusFromServerCompletionCallback = (success, context, state) => ((ISendStatusFromServerCompletionCallback)state).OnSendStatusFromServerCompletion(success); static readonly BatchCompletionDelegate CompletionHandler_IReceivedCloseOnServerCallback = - (success, context, state) => ((IReceivedCloseOnServerCallback)state).OnReceivedCloseOnServerHandler(success, context.GetReceivedCloseOnServerCancelled()); + (success, context, state) => ((IReceivedCloseOnServerCallback)state).OnReceivedCloseOnServer(success, context.GetReceivedCloseOnServerCancelled()); const uint GRPC_WRITE_BUFFER_HINT = 1; CompletionQueueSafeHandle completionQueue; diff --git a/src/csharp/Grpc.Core/Internal/INativeCall.cs b/src/csharp/Grpc.Core/Internal/INativeCall.cs index 717c9460abe..5c35b2ba461 100644 --- a/src/csharp/Grpc.Core/Internal/INativeCall.cs +++ b/src/csharp/Grpc.Core/Internal/INativeCall.cs @@ -53,7 +53,7 @@ namespace Grpc.Core.Internal internal interface IReceivedCloseOnServerCallback { - void OnReceivedCloseOnServerHandler(bool success, bool cancelled); + void OnReceivedCloseOnServer(bool success, bool cancelled); } /// From da8ef4046555b9f3c36ce6e1d77f3bf532067333 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Mon, 20 Nov 2017 19:37:17 +0100 Subject: [PATCH 67/70] save allocation by using a struct instead of a tuple --- .../Grpc.Core/Internal/AsyncCallServer.cs | 20 +++++++++++++++---- .../Grpc.Core/Internal/ServerCallHandler.cs | 12 +++++------ 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs b/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs index bb8287c5491..11acb275334 100644 --- a/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs +++ b/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs @@ -127,10 +127,10 @@ namespace Grpc.Core.Internal /// Sends call result status, indicating we are done with writes. /// Sending a status different from StatusCode.OK will also implicitly cancel the call. /// - public Task SendStatusFromServerAsync(Status status, Metadata trailers, Tuple optionalWrite) + public Task SendStatusFromServerAsync(Status status, Metadata trailers, ResponseWithFlags? optionalWrite) { - byte[] payload = optionalWrite != null ? UnsafeSerialize(optionalWrite.Item1) : null; - var writeFlags = optionalWrite != null ? optionalWrite.Item2 : default(WriteFlags); + byte[] payload = optionalWrite.HasValue ? UnsafeSerialize(optionalWrite.Value.Response) : null; + var writeFlags = optionalWrite.HasValue ? optionalWrite.Value.WriteFlags : default(WriteFlags); lock (myLock) { @@ -146,7 +146,7 @@ namespace Grpc.Core.Internal halfcloseRequested = true; initialMetadataSent = true; sendStatusFromServerTcs = new TaskCompletionSource(); - if (optionalWrite != null) + if (optionalWrite.HasValue) { streamingWritesCounter++; } @@ -241,5 +241,17 @@ namespace Grpc.Core.Internal { HandleSendStatusFromServerFinished(success); } + + public struct ResponseWithFlags + { + public ResponseWithFlags(TResponse response, WriteFlags writeFlags) + { + this.Response = response; + this.WriteFlags = writeFlags; + } + + public TResponse Response { get; } + public WriteFlags WriteFlags { get; } + } } } diff --git a/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs b/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs index 6019f8e7934..98995a0862e 100644 --- a/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs +++ b/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs @@ -60,7 +60,7 @@ namespace Grpc.Core.Internal var responseStream = new ServerResponseStream(asyncCall); Status status; - Tuple responseTuple = null; + AsyncCallServer.ResponseWithFlags? responseWithFlags = null; var context = HandlerUtils.NewContext(newRpc, responseStream, asyncCall.CancellationToken); try { @@ -68,7 +68,7 @@ namespace Grpc.Core.Internal var request = requestStream.Current; var response = await handler(request, context).ConfigureAwait(false); status = context.Status; - responseTuple = Tuple.Create(response, HandlerUtils.GetWriteFlags(context.WriteOptions)); + responseWithFlags = new AsyncCallServer.ResponseWithFlags(response, HandlerUtils.GetWriteFlags(context.WriteOptions)); } catch (Exception e) { @@ -80,7 +80,7 @@ namespace Grpc.Core.Internal } try { - await asyncCall.SendStatusFromServerAsync(status, context.ResponseTrailers, responseTuple).ConfigureAwait(false); + await asyncCall.SendStatusFromServerAsync(status, context.ResponseTrailers, responseWithFlags).ConfigureAwait(false); } catch (Exception) { @@ -177,13 +177,13 @@ namespace Grpc.Core.Internal var responseStream = new ServerResponseStream(asyncCall); Status status; - Tuple responseTuple = null; + AsyncCallServer.ResponseWithFlags? responseWithFlags = null; var context = HandlerUtils.NewContext(newRpc, responseStream, asyncCall.CancellationToken); try { var response = await handler(requestStream, context).ConfigureAwait(false); status = context.Status; - responseTuple = Tuple.Create(response, HandlerUtils.GetWriteFlags(context.WriteOptions)); + responseWithFlags = new AsyncCallServer.ResponseWithFlags(response, HandlerUtils.GetWriteFlags(context.WriteOptions)); } catch (Exception e) { @@ -196,7 +196,7 @@ namespace Grpc.Core.Internal try { - await asyncCall.SendStatusFromServerAsync(status, context.ResponseTrailers, responseTuple).ConfigureAwait(false); + await asyncCall.SendStatusFromServerAsync(status, context.ResponseTrailers, responseWithFlags).ConfigureAwait(false); } catch (Exception) { From 8bae77cdd17b9bb9056cfff63366ad7fd97f352d Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Mon, 20 Nov 2017 19:59:40 +0100 Subject: [PATCH 68/70] add completion registry benchmark with shared registry too --- .../CompletionRegistryBenchmark.cs | 13 +++++++------ src/csharp/Grpc.Microbenchmarks/Program.cs | 5 ++++- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/csharp/Grpc.Microbenchmarks/CompletionRegistryBenchmark.cs b/src/csharp/Grpc.Microbenchmarks/CompletionRegistryBenchmark.cs index 0e2fb070f19..2d1c33e9a0c 100644 --- a/src/csharp/Grpc.Microbenchmarks/CompletionRegistryBenchmark.cs +++ b/src/csharp/Grpc.Microbenchmarks/CompletionRegistryBenchmark.cs @@ -40,24 +40,25 @@ namespace Grpc.Microbenchmarks GrpcEnvironment.ReleaseAsync().Wait(); } - public void Run(int threadCount, int iterations) + public void Run(int threadCount, int iterations, bool useSharedRegistry) { - Console.WriteLine(string.Format("CompletionRegistryBenchmark: threads={0}, iterations={1}", threadCount, iterations)); - var threadedBenchmark = new ThreadedBenchmark(threadCount, () => ThreadBody(iterations)); + Console.WriteLine(string.Format("CompletionRegistryBenchmark: threads={0}, iterations={1}, useSharedRegistry={2}", threadCount, iterations, useSharedRegistry)); + CompletionRegistry sharedRegistry = useSharedRegistry ? new CompletionRegistry(environment) : null; + var threadedBenchmark = new ThreadedBenchmark(threadCount, () => ThreadBody(iterations, sharedRegistry)); threadedBenchmark.Run(); // TODO: parametrize by number of pending completions } - private void ThreadBody(int iterations) + private void ThreadBody(int iterations, CompletionRegistry optionalSharedRegistry) { - var completionRegistry = new CompletionRegistry(environment); + var completionRegistry = optionalSharedRegistry ?? new CompletionRegistry(environment); var ctx = BatchContextSafeHandle.Create(); var stopwatch = Stopwatch.StartNew(); for (int i = 0; i < iterations; i++) { completionRegistry.Register(ctx.Handle, ctx); - var callback = completionRegistry.Extract(completionRegistry.LastRegisteredKey); + var callback = completionRegistry.Extract(ctx.Handle); // NOTE: we are not calling the callback to avoid disposing ctx. } stopwatch.Stop(); diff --git a/src/csharp/Grpc.Microbenchmarks/Program.cs b/src/csharp/Grpc.Microbenchmarks/Program.cs index 79ec308501d..a64c2979abe 100644 --- a/src/csharp/Grpc.Microbenchmarks/Program.cs +++ b/src/csharp/Grpc.Microbenchmarks/Program.cs @@ -77,7 +77,10 @@ namespace Grpc.Microbenchmarks benchmark.Init(); foreach (int threadCount in new int[] {1, 1, 2, 4, 8, 12}) { - benchmark.Run(threadCount, 4 * 1000 * 1000); + foreach (bool useSharedRegistry in new bool[] {false, true}) + { + benchmark.Run(threadCount, 4 * 1000 * 1000, useSharedRegistry); + } } benchmark.Cleanup(); } From eec8b84c71bc81a7073142506f369a9b8a3a63a6 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Mon, 20 Nov 2017 20:23:30 +0100 Subject: [PATCH 69/70] spinlock in completion registry is slightly faster --- .../Grpc.Core/Internal/CompletionRegistry.cs | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs b/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs index f4aea685c31..b68655b33ca 100644 --- a/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs +++ b/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs @@ -19,7 +19,9 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics; using System.Runtime.InteropServices; +using System.Threading; using Grpc.Core.Logging; using Grpc.Core.Utils; @@ -35,7 +37,7 @@ namespace Grpc.Core.Internal readonly GrpcEnvironment environment; readonly Dictionary dict = new Dictionary(new IntPtrComparer()); - readonly object myLock = new object(); + SpinLock spinLock = new SpinLock(Debugger.IsAttached); IntPtr lastRegisteredKey; // only for testing public CompletionRegistry(GrpcEnvironment environment) @@ -46,11 +48,19 @@ namespace Grpc.Core.Internal public void Register(IntPtr key, IOpCompletionCallback callback) { environment.DebugStats.PendingBatchCompletions.Increment(); - lock (myLock) + + bool lockTaken = false; + try { + spinLock.Enter(ref lockTaken); + dict.Add(key, callback); this.lastRegisteredKey = key; } + finally + { + if (lockTaken) spinLock.Exit(); + } } public void RegisterBatchCompletion(BatchContextSafeHandle ctx, BatchCompletionDelegate callback, object state) @@ -68,11 +78,18 @@ namespace Grpc.Core.Internal public IOpCompletionCallback Extract(IntPtr key) { IOpCompletionCallback value = null; - lock (myLock) + bool lockTaken = false; + try { + spinLock.Enter(ref lockTaken); + value = dict[key]; dict.Remove(key); } + finally + { + if (lockTaken) spinLock.Exit(); + } environment.DebugStats.PendingBatchCompletions.Decrement(); return value; } From 264560ff4a497de14e070de0323dd5eb48d96b6f Mon Sep 17 00:00:00 2001 From: ncteisen Date: Tue, 21 Nov 2017 13:55:52 -0500 Subject: [PATCH 70/70] Reviewer feedback --- src/core/lib/support/abstract.h | 2 +- src/core/lib/support/manual_constructor.h | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/core/lib/support/abstract.h b/src/core/lib/support/abstract.h index ddf472bc37a..5498769a7d9 100644 --- a/src/core/lib/support/abstract.h +++ b/src/core/lib/support/abstract.h @@ -21,7 +21,7 @@ // This is needed to support abstract base classes in the c core. Since gRPC // doesn't have a c++ runtime, it will hit a linker error on delete unless -// a we define a virtual operator delete. See this blog for more info: +// we define a virtual operator delete. See this blog for more info: // https://eli.thegreenplace.net/2015/c-deleting-destructors-and-virtual-operator-delete/ #define GRPC_ABSTRACT_BASE_CLASS \ static void operator delete(void* p) { abort(); } diff --git a/src/core/lib/support/manual_constructor.h b/src/core/lib/support/manual_constructor.h index 25731936b1c..fda7653dbce 100644 --- a/src/core/lib/support/manual_constructor.h +++ b/src/core/lib/support/manual_constructor.h @@ -35,25 +35,25 @@ namespace grpc_core { // in this file. namespace manual_ctor_impl { -// is_one_of returns true it a class, Needle, is present in a variadic list of -// classes, Haystack. -template +// is_one_of returns true it a class, Member, is present in a variadic list of +// classes, List. +template class is_one_of; -template -class is_one_of { +template +class is_one_of { public: static constexpr const bool value = true; }; -template -class is_one_of { +template +class is_one_of { public: - static constexpr const bool value = is_one_of::value; + static constexpr const bool value = is_one_of::value; }; -template -class is_one_of { +template +class is_one_of { public: static constexpr const bool value = false; };