Merge remote-tracking branch 'upstream/master'

pull/18881/head
Na-Na Pang 6 years ago
commit a7888f0f1a
  1. 1
      BUILD
  2. 12
      bazel/BUILD
  3. 108
      bazel/cc_grpc_library.bzl
  4. 30
      bazel/generate_cc.bzl
  5. 26
      bazel/protobuf.bzl
  6. 17
      bazel/python_rules.bzl
  7. 2
      doc/statuscodes.md
  8. 47
      examples/BUILD
  9. 3
      include/grpc/impl/codegen/port_platform.h
  10. 3
      include/grpc/impl/codegen/status.h
  11. 2
      include/grpc/support/alloc.h
  12. 3
      include/grpcpp/impl/codegen/status_code_enum.h
  13. 132
      src/compiler/cpp_plugin.cc
  14. 154
      src/compiler/cpp_plugin.h
  15. 16
      src/core/ext/filters/client_channel/subchannel.cc
  16. 43
      src/core/ext/transport/chttp2/transport/chttp2_transport.cc
  17. 6
      src/core/ext/transport/chttp2/transport/hpack_parser.cc
  18. 37
      src/core/ext/transport/chttp2/transport/internal.h
  19. 49
      src/core/ext/transport/chttp2/transport/writing.cc
  20. 37
      src/core/lib/channel/channel_stack.cc
  21. 73
      src/core/lib/gpr/alloc.cc
  22. 10
      src/core/lib/gpr/alloc.h
  23. 8
      src/core/lib/gprpp/arena.cc
  24. 6
      src/core/lib/gprpp/arena.h
  25. 4
      src/core/lib/gprpp/orphanable.h
  26. 4
      src/core/lib/gprpp/ref_counted.h
  27. 7
      src/core/lib/iomgr/ev_epoll1_linux.cc
  28. 2
      src/core/lib/slice/slice_buffer.cc
  29. 2
      src/core/lib/slice/slice_internal.h
  30. 6
      src/core/lib/surface/call.cc
  31. 4
      src/core/lib/transport/metadata.cc
  32. 2
      src/core/lib/transport/transport.cc
  33. 2
      src/core/lib/transport/transport.h
  34. 19
      src/csharp/Grpc.Core.Api/DeserializationContext.cs
  35. 8
      src/csharp/Grpc.Core.Api/Grpc.Core.Api.csproj
  36. 3
      src/csharp/Grpc.Core.Api/StatusCode.cs
  37. 4
      src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj
  38. 12
      src/csharp/Grpc.Core.Tests/Internal/AsyncCallServerTest.cs
  39. 49
      src/csharp/Grpc.Core.Tests/Internal/AsyncCallTest.cs
  40. 240
      src/csharp/Grpc.Core.Tests/Internal/DefaultDeserializationContextTest.cs
  41. 118
      src/csharp/Grpc.Core.Tests/Internal/FakeBufferReaderManager.cs
  42. 121
      src/csharp/Grpc.Core.Tests/Internal/FakeBufferReaderManagerTest.cs
  43. 151
      src/csharp/Grpc.Core.Tests/Internal/ReusableSliceBufferTest.cs
  44. 83
      src/csharp/Grpc.Core.Tests/Internal/SliceTest.cs
  45. 9
      src/csharp/Grpc.Core/Grpc.Core.csproj
  46. 10
      src/csharp/Grpc.Core/Internal/AsyncCall.cs
  47. 17
      src/csharp/Grpc.Core/Internal/AsyncCallBase.cs
  48. 44
      src/csharp/Grpc.Core/Internal/BatchContextSafeHandle.cs
  49. 4
      src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
  50. 61
      src/csharp/Grpc.Core/Internal/DefaultDeserializationContext.cs
  51. 4
      src/csharp/Grpc.Core/Internal/INativeCall.cs
  52. 14
      src/csharp/Grpc.Core/Internal/NativeMethods.Generated.cs
  53. 148
      src/csharp/Grpc.Core/Internal/ReusableSliceBuffer.cs
  54. 68
      src/csharp/Grpc.Core/Internal/Slice.cs
  55. 1
      src/csharp/Grpc.Tools/build/_protobuf/Google.Protobuf.Tools.targets
  56. 54
      src/csharp/ext/grpc_csharp_ext.c
  57. 4
      src/csharp/tests.json
  58. 2
      src/csharp/unitypackage/unitypackage_skeleton/Plugins/Grpc.Core/runtimes/grpc_csharp_ext_dummy_stubs.c
  59. 3
      src/objective-c/GRPCClient/GRPCCall.h
  60. 31
      src/proto/grpc/testing/BUILD
  61. 6
      src/ruby/lib/grpc/errors.rb
  62. 2
      templates/src/csharp/Grpc.Core/Internal/native_methods.include
  63. 18
      test/core/gpr/alloc_test.cc
  64. 26
      test/core/util/memory_counters.cc
  65. 6
      tools/internal_ci/linux/pull_request/grpc_basictests_csharp.cfg
  66. 6
      tools/internal_ci/linux/pull_request/grpc_basictests_node.cfg
  67. 30
      tools/internal_ci/linux/pull_request/grpc_basictests_php.cfg
  68. 30
      tools/internal_ci/linux/pull_request/grpc_basictests_python.cfg
  69. 30
      tools/internal_ci/linux/pull_request/grpc_basictests_ruby.cfg
  70. 2
      tools/internal_ci/macos/grpc_basictests_c_cpp.cfg
  71. 31
      tools/internal_ci/macos/pull_request/grpc_basictests_c_cpp.cfg
  72. 31
      tools/internal_ci/macos/pull_request/grpc_basictests_csharp.cfg
  73. 31
      tools/internal_ci/macos/pull_request/grpc_basictests_node.cfg
  74. 31
      tools/internal_ci/macos/pull_request/grpc_basictests_php.cfg
  75. 31
      tools/internal_ci/macos/pull_request/grpc_basictests_python.cfg
  76. 31
      tools/internal_ci/macos/pull_request/grpc_basictests_ruby.cfg
  77. 2
      tools/internal_ci/windows/grpc_basictests_c.cfg
  78. 30
      tools/internal_ci/windows/pull_request/grpc_basictests_c.cfg
  79. 30
      tools/internal_ci/windows/pull_request/grpc_basictests_csharp.cfg
  80. 30
      tools/internal_ci/windows/pull_request/grpc_basictests_python.cfg
  81. 32
      tools/internal_ci/windows/pull_request/grpc_bazel_rbe_dbg.cfg

@ -433,6 +433,7 @@ grpc_cc_library(
"src/compiler/config.h", "src/compiler/config.h",
"src/compiler/cpp_generator.h", "src/compiler/cpp_generator.h",
"src/compiler/cpp_generator_helpers.h", "src/compiler/cpp_generator_helpers.h",
"src/compiler/cpp_plugin.h",
"src/compiler/csharp_generator.h", "src/compiler/csharp_generator.h",
"src/compiler/csharp_generator_helpers.h", "src/compiler/csharp_generator_helpers.h",
"src/compiler/generator_helpers.h", "src/compiler/generator_helpers.h",

@ -17,15 +17,3 @@ licenses(["notice"]) # Apache v2
package(default_visibility = ["//:__subpackages__"]) package(default_visibility = ["//:__subpackages__"])
load(":cc_grpc_library.bzl", "cc_grpc_library") load(":cc_grpc_library.bzl", "cc_grpc_library")
proto_library(
name = "well_known_protos_list",
srcs = ["@com_google_protobuf//:well_known_protos"],
)
cc_grpc_library(
name = "well_known_protos",
srcs = "well_known_protos_list",
proto_only = True,
deps = [],
)

@ -1,32 +1,68 @@
"""Generates and compiles C++ grpc stubs from proto_library rules.""" """Generates and compiles C++ grpc stubs from proto_library rules."""
load("//bazel:generate_cc.bzl", "generate_cc") load("//bazel:generate_cc.bzl", "generate_cc")
load("//bazel:protobuf.bzl", "well_known_proto_libs")
def cc_grpc_library(name, srcs, deps, proto_only, well_known_protos, generate_mocks = False, use_external = False, **kwargs): def cc_grpc_library(
"""Generates C++ grpc classes from a .proto file. name,
srcs,
deps,
proto_only = False,
well_known_protos = False,
generate_mocks = False,
use_external = False,
grpc_only = False,
**kwargs):
"""Generates C++ grpc classes for services defined in a proto file.
If grpc_only is True, this rule is compatible with proto_library and
cc_proto_library native rules such that it expects proto_library target
as srcs argument and generates only grpc library classes, expecting
protobuf messages classes library (cc_proto_library target) to be passed in
deps argument. By default grpc_only is False which makes this rule to behave
in a backwards-compatible mode (trying to generate both proto and grpc
classes).
Assumes the generated classes will be used in cc_api_version = 2. Assumes the generated classes will be used in cc_api_version = 2.
Arguments: Args:
name: name of rule. name (str): Name of rule.
srcs: a single proto_library, which wraps the .proto files with services. srcs (list): A single .proto file which contains services definitions,
deps: a list of C++ proto_library (or cc_proto_library) which provides or if grpc_only parameter is True, a single proto_library which
the compiled code of any message that the services depend on. contains services descriptors.
well_known_protos: Should this library additionally depend on well known deps (list): A list of C++ proto_library (or cc_proto_library) which
protos provides the compiled code of any message that the services depend on.
use_external: When True the grpc deps are prefixed with //external. This proto_only (bool): If True, create only C++ proto classes library,
allows grpc to be used as a dependency in other bazel projects. avoid creating C++ grpc classes library (expect it in deps).
generate_mocks: When True, Google Mock code for client stub is generated. Deprecated, use native cc_proto_library instead. False by default.
**kwargs: rest of arguments, e.g., compatible_with and visibility. well_known_protos (bool): Should this library additionally depend on
well known protos. Deprecated, the well known protos should be
specified as explicit dependencies of the proto_library target
(passed in srcs parameter) instead. False by default.
generate_mocks (bool): when True, Google Mock code for client stub is
generated. False by default.
use_external (bool): Not used.
grpc_only (bool): if True, generate only grpc library, expecting
protobuf messages library (cc_proto_library target) to be passed as
deps. False by default (will become True by default eventually).
**kwargs: rest of arguments, e.g., compatible_with and visibility
""" """
if len(srcs) > 1: if len(srcs) > 1:
fail("Only one srcs value supported", "srcs") fail("Only one srcs value supported", "srcs")
if grpc_only and proto_only:
fail("A mutualy exclusive configuration is specified: grpc_only = True and proto_only = True")
extra_deps = []
proto_targets = []
if not grpc_only:
proto_target = "_" + name + "_only" proto_target = "_" + name + "_only"
codegen_target = "_" + name + "_codegen" cc_proto_target = name if proto_only else "_" + name + "_cc_proto"
codegen_grpc_target = "_" + name + "_grpc_codegen"
proto_deps = ["_" + dep + "_only" for dep in deps if dep.find(':') == -1] proto_deps = ["_" + dep + "_only" for dep in deps if dep.find(":") == -1]
proto_deps += [dep.split(':')[0] + ':' + "_" + dep.split(':')[1] + "_only" for dep in deps if dep.find(':') != -1] proto_deps += [dep.split(":")[0] + ":" + "_" + dep.split(":")[1] + "_only" for dep in deps if dep.find(":") != -1]
if well_known_protos:
proto_deps += well_known_proto_libs()
native.proto_library( native.proto_library(
name = proto_target, name = proto_target,
@ -35,37 +71,35 @@ def cc_grpc_library(name, srcs, deps, proto_only, well_known_protos, generate_mo
**kwargs **kwargs
) )
generate_cc( native.cc_proto_library(
name = codegen_target, name = cc_proto_target,
srcs = [proto_target], deps = [":" + proto_target],
well_known_protos = well_known_protos,
**kwargs **kwargs
) )
extra_deps.append(":" + cc_proto_target)
proto_targets.append(proto_target)
else:
if not srcs:
fail("srcs cannot be empty", "srcs")
proto_targets += srcs
if not proto_only: if not proto_only:
plugin = "@com_github_grpc_grpc//:grpc_cpp_plugin" codegen_grpc_target = "_" + name + "_grpc_codegen"
generate_cc( generate_cc(
name = codegen_grpc_target, name = codegen_grpc_target,
srcs = [proto_target], srcs = proto_targets,
plugin = plugin, plugin = "@com_github_grpc_grpc//:grpc_cpp_plugin",
well_known_protos = well_known_protos, well_known_protos = well_known_protos,
generate_mocks = generate_mocks, generate_mocks = generate_mocks,
**kwargs **kwargs
) )
grpc_deps = ["@com_github_grpc_grpc//:grpc++_codegen_proto",
"//external:protobuf"]
native.cc_library(
name = name,
srcs = [":" + codegen_grpc_target, ":" + codegen_target],
hdrs = [":" + codegen_grpc_target, ":" + codegen_target],
deps = deps + grpc_deps,
**kwargs
)
else:
native.cc_library( native.cc_library(
name = name, name = name,
srcs = [":" + codegen_target], srcs = [":" + codegen_grpc_target],
hdrs = [":" + codegen_target], hdrs = [":" + codegen_grpc_target],
deps = deps + ["//external:protobuf"], deps = deps +
extra_deps +
["@com_github_grpc_grpc//:grpc++_codegen_proto"],
**kwargs **kwargs
) )

@ -18,12 +18,22 @@ _GRPC_PROTO_MOCK_HEADER_FMT = "{}_mock.grpc.pb.h"
_PROTO_HEADER_FMT = "{}.pb.h" _PROTO_HEADER_FMT = "{}.pb.h"
_PROTO_SRC_FMT = "{}.pb.cc" _PROTO_SRC_FMT = "{}.pb.cc"
def _strip_package_from_path(label_package, path): def _strip_package_from_path(label_package, file):
prefix_len = 0
if not file.is_source and file.path.startswith(file.root.path):
prefix_len = len(file.root.path) + 1
path = file.path
if len(label_package) == 0: if len(label_package) == 0:
return path return path
if not path.startswith(label_package + "/"): if not path.startswith(label_package + "/", prefix_len):
fail("'{}' does not lie within '{}'.".format(path, label_package)) fail("'{}' does not lie within '{}'.".format(path, label_package))
return path[len(label_package + "/"):] return path[prefix_len + len(label_package + "/"):]
def _get_srcs_file_path(file):
if not file.is_source and file.path.startswith(file.root.path):
return file.path[len(file.root.path) + 1:]
return file.path
def _join_directories(directories): def _join_directories(directories):
massaged_directories = [directory for directory in directories if len(directory) != 0] massaged_directories = [directory for directory in directories if len(directory) != 0]
@ -31,7 +41,7 @@ def _join_directories(directories):
def generate_cc_impl(ctx): def generate_cc_impl(ctx):
"""Implementation of the generate_cc rule.""" """Implementation of the generate_cc rule."""
protos = [f for src in ctx.attr.srcs for f in src.proto.direct_sources] protos = [f for src in ctx.attr.srcs for f in src.proto.check_deps_sources]
includes = [ includes = [
f f
for src in ctx.attr.srcs for src in ctx.attr.srcs
@ -46,14 +56,14 @@ def generate_cc_impl(ctx):
if ctx.executable.plugin: if ctx.executable.plugin:
outs += [ outs += [
proto_path_to_generated_filename( proto_path_to_generated_filename(
_strip_package_from_path(label_package, proto.path), _strip_package_from_path(label_package, proto),
_GRPC_PROTO_HEADER_FMT, _GRPC_PROTO_HEADER_FMT,
) )
for proto in protos for proto in protos
] ]
outs += [ outs += [
proto_path_to_generated_filename( proto_path_to_generated_filename(
_strip_package_from_path(label_package, proto.path), _strip_package_from_path(label_package, proto),
_GRPC_PROTO_SRC_FMT, _GRPC_PROTO_SRC_FMT,
) )
for proto in protos for proto in protos
@ -61,7 +71,7 @@ def generate_cc_impl(ctx):
if ctx.attr.generate_mocks: if ctx.attr.generate_mocks:
outs += [ outs += [
proto_path_to_generated_filename( proto_path_to_generated_filename(
_strip_package_from_path(label_package, proto.path), _strip_package_from_path(label_package, proto),
_GRPC_PROTO_MOCK_HEADER_FMT, _GRPC_PROTO_MOCK_HEADER_FMT,
) )
for proto in protos for proto in protos
@ -69,14 +79,14 @@ def generate_cc_impl(ctx):
else: else:
outs += [ outs += [
proto_path_to_generated_filename( proto_path_to_generated_filename(
_strip_package_from_path(label_package, proto.path), _strip_package_from_path(label_package, proto),
_PROTO_HEADER_FMT, _PROTO_HEADER_FMT,
) )
for proto in protos for proto in protos
] ]
outs += [ outs += [
proto_path_to_generated_filename( proto_path_to_generated_filename(
_strip_package_from_path(label_package, proto.path), _strip_package_from_path(label_package, proto),
_PROTO_SRC_FMT, _PROTO_SRC_FMT,
) )
for proto in protos for proto in protos
@ -102,7 +112,7 @@ def generate_cc_impl(ctx):
# Include the output directory so that protoc puts the generated code in the # Include the output directory so that protoc puts the generated code in the
# right directory. # right directory.
arguments += ["--proto_path={0}{1}".format(dir_out, proto_root)] arguments += ["--proto_path={0}{1}".format(dir_out, proto_root)]
arguments += [proto.path for proto in protos] arguments += [_get_srcs_file_path(proto) for proto in protos]
# create a list of well known proto files if the argument is non-None # create a list of well known proto files if the argument is non-None
well_known_proto_files = [] well_known_proto_files = []

@ -2,6 +2,22 @@
_PROTO_EXTENSION = ".proto" _PROTO_EXTENSION = ".proto"
def well_known_proto_libs():
return [
"@com_google_protobuf//:any_proto",
"@com_google_protobuf//:api_proto",
"@com_google_protobuf//:compiler_plugin_proto",
"@com_google_protobuf//:descriptor_proto",
"@com_google_protobuf//:duration_proto",
"@com_google_protobuf//:empty_proto",
"@com_google_protobuf//:field_mask_proto",
"@com_google_protobuf//:source_context_proto",
"@com_google_protobuf//:struct_proto",
"@com_google_protobuf//:timestamp_proto",
"@com_google_protobuf//:type_proto",
"@com_google_protobuf//:wrappers_proto",
]
def get_proto_root(workspace_root): def get_proto_root(workspace_root):
"""Gets the root protobuf directory. """Gets the root protobuf directory.
@ -42,12 +58,16 @@ def proto_path_to_generated_filename(proto_path, fmt_str):
def _get_include_directory(include): def _get_include_directory(include):
directory = include.path directory = include.path
if directory.startswith("external"): prefix_len = 0
external_separator = directory.find("/") if not include.is_source and directory.startswith(include.root.path):
prefix_len = len(include.root.path) + 1
if directory.startswith("external", prefix_len):
external_separator = directory.find("/", prefix_len)
repository_separator = directory.find("/", external_separator + 1) repository_separator = directory.find("/", external_separator + 1)
return directory[:repository_separator] return directory[:repository_separator]
else: else:
return "." return include.root.path if include.root.path else "."
def get_include_protoc_args(includes): def get_include_protoc_args(includes):
"""Returns protoc args that imports protos relative to their import root. """Returns protoc args that imports protos relative to their import root.

@ -130,21 +130,6 @@ def _generate_py(well_known_protos, **kwargs):
else: else:
__generate_py(**kwargs) __generate_py(**kwargs)
_WELL_KNOWN_PROTO_LIBS = [
"@com_google_protobuf//:any_proto",
"@com_google_protobuf//:api_proto",
"@com_google_protobuf//:compiler_plugin_proto",
"@com_google_protobuf//:descriptor_proto",
"@com_google_protobuf//:duration_proto",
"@com_google_protobuf//:empty_proto",
"@com_google_protobuf//:field_mask_proto",
"@com_google_protobuf//:source_context_proto",
"@com_google_protobuf//:struct_proto",
"@com_google_protobuf//:timestamp_proto",
"@com_google_protobuf//:type_proto",
"@com_google_protobuf//:wrappers_proto",
]
def py_proto_library( def py_proto_library(
name, name,
deps, deps,
@ -167,8 +152,6 @@ def py_proto_library(
codegen_target = "_{}_codegen".format(name) codegen_target = "_{}_codegen".format(name)
codegen_grpc_target = "_{}_grpc_codegen".format(name) codegen_grpc_target = "_{}_grpc_codegen".format(name)
well_known_proto_rules = _WELL_KNOWN_PROTO_LIBS if well_known_protos else []
_generate_py( _generate_py(
name = codegen_target, name = codegen_target,
deps = deps, deps = deps,

@ -20,7 +20,7 @@ statuses are defined as such:
| OUT_OF_RANGE | 11 | The operation was attempted past the valid range. E.g., seeking or reading past end-of-file. Unlike `INVALID_ARGUMENT`, this error indicates a problem that may be fixed if the system state changes. For example, a 32-bit file system will generate `INVALID_ARGUMENT` if asked to read at an offset that is not in the range [0,2^32-1], but it will generate `OUT_OF_RANGE` if asked to read from an offset past the current file size. There is a fair bit of overlap between `FAILED_PRECONDITION` and `OUT_OF_RANGE`. We recommend using `OUT_OF_RANGE` (the more specific error) when it applies so that callers who are iterating through a space can easily look for an `OUT_OF_RANGE` error to detect when they are done. | 400 Bad Request | | OUT_OF_RANGE | 11 | The operation was attempted past the valid range. E.g., seeking or reading past end-of-file. Unlike `INVALID_ARGUMENT`, this error indicates a problem that may be fixed if the system state changes. For example, a 32-bit file system will generate `INVALID_ARGUMENT` if asked to read at an offset that is not in the range [0,2^32-1], but it will generate `OUT_OF_RANGE` if asked to read from an offset past the current file size. There is a fair bit of overlap between `FAILED_PRECONDITION` and `OUT_OF_RANGE`. We recommend using `OUT_OF_RANGE` (the more specific error) when it applies so that callers who are iterating through a space can easily look for an `OUT_OF_RANGE` error to detect when they are done. | 400 Bad Request |
| UNIMPLEMENTED | 12 | The operation is not implemented or is not supported/enabled in this service. | 501 Not Implemented | | UNIMPLEMENTED | 12 | The operation is not implemented or is not supported/enabled in this service. | 501 Not Implemented |
| INTERNAL | 13 | Internal errors. This means that some invariants expected by the underlying system have been broken. This error code is reserved for serious errors. | 500 Internal Server Error | | INTERNAL | 13 | Internal errors. This means that some invariants expected by the underlying system have been broken. This error code is reserved for serious errors. | 500 Internal Server Error |
| UNAVAILABLE | 14 | The service is currently unavailable. This is most likely a transient condition, which can be corrected by retrying with a backoff. | 503 Service Unavailable | | UNAVAILABLE | 14 | The service is currently unavailable. This is most likely a transient condition, which can be corrected by retrying with a backoff. Note that it is not always safe to retry non-idempotent operations. | 503 Service Unavailable |
| DATA_LOSS | 15 | Unrecoverable data loss or corruption. | 500 Internal Server Error | | DATA_LOSS | 15 | Unrecoverable data loss or corruption. | 500 Internal Server Error |
All RPCs started at a client return a `status` object composed of an integer All RPCs started at a client return a `status` object composed of an integer

@ -17,6 +17,7 @@ licenses(["notice"]) # 3-clause BSD
package(default_visibility = ["//visibility:public"]) package(default_visibility = ["//visibility:public"])
load("//bazel:grpc_build_system.bzl", "grpc_proto_library") load("//bazel:grpc_build_system.bzl", "grpc_proto_library")
load("//bazel:cc_grpc_library.bzl", "cc_grpc_library")
load("//bazel:python_rules.bzl", "py_proto_library") load("//bazel:python_rules.bzl", "py_proto_library")
grpc_proto_library( grpc_proto_library(
@ -29,11 +30,25 @@ grpc_proto_library(
srcs = ["protos/hellostreamingworld.proto"], srcs = ["protos/hellostreamingworld.proto"],
) )
grpc_proto_library( # The following three rules demonstrate the usage of the cc_grpc_library rule in
name = "helloworld", # in a mode compatible with the native proto_library and cc_proto_library rules.
proto_library(
name = "helloworld_proto",
srcs = ["protos/helloworld.proto"], srcs = ["protos/helloworld.proto"],
) )
cc_proto_library(
name = "helloworld_cc_proto",
deps = [":helloworld_proto"],
)
cc_grpc_library(
name = "helloworld_cc_grpc",
srcs = [":helloworld_proto"],
grpc_only = True,
deps = [":helloworld_cc_proto"],
)
grpc_proto_library( grpc_proto_library(
name = "route_guide", name = "route_guide",
srcs = ["protos/route_guide.proto"], srcs = ["protos/route_guide.proto"],
@ -59,7 +74,7 @@ cc_binary(
srcs = ["cpp/helloworld/greeter_client.cc"], srcs = ["cpp/helloworld/greeter_client.cc"],
defines = ["BAZEL_BUILD"], defines = ["BAZEL_BUILD"],
deps = [ deps = [
":helloworld", ":helloworld_cc_grpc",
"//:grpc++", "//:grpc++",
], ],
) )
@ -69,7 +84,7 @@ cc_binary(
srcs = ["cpp/helloworld/greeter_async_client.cc"], srcs = ["cpp/helloworld/greeter_async_client.cc"],
defines = ["BAZEL_BUILD"], defines = ["BAZEL_BUILD"],
deps = [ deps = [
":helloworld", ":helloworld_cc_grpc",
"//:grpc++", "//:grpc++",
], ],
) )
@ -79,7 +94,7 @@ cc_binary(
srcs = ["cpp/helloworld/greeter_async_client2.cc"], srcs = ["cpp/helloworld/greeter_async_client2.cc"],
defines = ["BAZEL_BUILD"], defines = ["BAZEL_BUILD"],
deps = [ deps = [
":helloworld", ":helloworld_cc_grpc",
"//:grpc++", "//:grpc++",
], ],
) )
@ -89,7 +104,7 @@ cc_binary(
srcs = ["cpp/helloworld/greeter_server.cc"], srcs = ["cpp/helloworld/greeter_server.cc"],
defines = ["BAZEL_BUILD"], defines = ["BAZEL_BUILD"],
deps = [ deps = [
":helloworld", ":helloworld_cc_grpc",
"//:grpc++", "//:grpc++",
], ],
) )
@ -99,7 +114,7 @@ cc_binary(
srcs = ["cpp/helloworld/greeter_async_server.cc"], srcs = ["cpp/helloworld/greeter_async_server.cc"],
defines = ["BAZEL_BUILD"], defines = ["BAZEL_BUILD"],
deps = [ deps = [
":helloworld", ":helloworld_cc_grpc",
"//:grpc++", "//:grpc++",
], ],
) )
@ -109,7 +124,7 @@ cc_binary(
srcs = ["cpp/metadata/greeter_client.cc"], srcs = ["cpp/metadata/greeter_client.cc"],
defines = ["BAZEL_BUILD"], defines = ["BAZEL_BUILD"],
deps = [ deps = [
":helloworld", ":helloworld_cc_grpc",
"//:grpc++", "//:grpc++",
], ],
) )
@ -119,7 +134,7 @@ cc_binary(
srcs = ["cpp/metadata/greeter_server.cc"], srcs = ["cpp/metadata/greeter_server.cc"],
defines = ["BAZEL_BUILD"], defines = ["BAZEL_BUILD"],
deps = [ deps = [
":helloworld", ":helloworld_cc_grpc",
"//:grpc++", "//:grpc++",
], ],
) )
@ -129,7 +144,7 @@ cc_binary(
srcs = ["cpp/load_balancing/greeter_client.cc"], srcs = ["cpp/load_balancing/greeter_client.cc"],
defines = ["BAZEL_BUILD"], defines = ["BAZEL_BUILD"],
deps = [ deps = [
":helloworld", ":helloworld_cc_grpc",
"//:grpc++", "//:grpc++",
], ],
) )
@ -139,7 +154,7 @@ cc_binary(
srcs = ["cpp/load_balancing/greeter_server.cc"], srcs = ["cpp/load_balancing/greeter_server.cc"],
defines = ["BAZEL_BUILD"], defines = ["BAZEL_BUILD"],
deps = [ deps = [
":helloworld", ":helloworld_cc_grpc",
"//:grpc++", "//:grpc++",
], ],
) )
@ -149,7 +164,7 @@ cc_binary(
srcs = ["cpp/compression/greeter_client.cc"], srcs = ["cpp/compression/greeter_client.cc"],
defines = ["BAZEL_BUILD"], defines = ["BAZEL_BUILD"],
deps = [ deps = [
":helloworld", ":helloworld_cc_grpc",
"//:grpc++", "//:grpc++",
], ],
) )
@ -159,15 +174,17 @@ cc_binary(
srcs = ["cpp/compression/greeter_server.cc"], srcs = ["cpp/compression/greeter_server.cc"],
defines = ["BAZEL_BUILD"], defines = ["BAZEL_BUILD"],
deps = [ deps = [
":helloworld", ":helloworld_cc_grpc",
"//:grpc++", "//:grpc++",
], ],
) )
cc_binary( cc_binary(
name = "keyvaluestore_client", name = "keyvaluestore_client",
srcs = ["cpp/keyvaluestore/caching_interceptor.h", srcs = [
"cpp/keyvaluestore/client.cc"], "cpp/keyvaluestore/caching_interceptor.h",
"cpp/keyvaluestore/client.cc",
],
defines = ["BAZEL_BUILD"], defines = ["BAZEL_BUILD"],
deps = [ deps = [
":keyvaluestore", ":keyvaluestore",

@ -77,7 +77,6 @@
#define GPR_WINDOWS 1 #define GPR_WINDOWS 1
#define GPR_WINDOWS_SUBPROCESS 1 #define GPR_WINDOWS_SUBPROCESS 1
#define GPR_WINDOWS_ENV #define GPR_WINDOWS_ENV
#define GPR_HAS_ALIGNED_MALLOC 1
#ifdef __MSYS__ #ifdef __MSYS__
#define GPR_GETPID_IN_UNISTD_H 1 #define GPR_GETPID_IN_UNISTD_H 1
#define GPR_MSYS_TMPFILE #define GPR_MSYS_TMPFILE
@ -174,7 +173,6 @@
#define GPR_POSIX_SYNC 1 #define GPR_POSIX_SYNC 1
#define GPR_POSIX_TIME 1 #define GPR_POSIX_TIME 1
#define GPR_HAS_PTHREAD_H 1 #define GPR_HAS_PTHREAD_H 1
#define GPR_HAS_ALIGNED_ALLOC 1
#define GPR_GETPID_IN_UNISTD_H 1 #define GPR_GETPID_IN_UNISTD_H 1
#ifdef _LP64 #ifdef _LP64
#define GPR_ARCH_64 1 #define GPR_ARCH_64 1
@ -240,7 +238,6 @@
#define GPR_POSIX_SUBPROCESS 1 #define GPR_POSIX_SUBPROCESS 1
#define GPR_POSIX_SYNC 1 #define GPR_POSIX_SYNC 1
#define GPR_POSIX_TIME 1 #define GPR_POSIX_TIME 1
#define GPR_HAS_POSIX_MEMALIGN 1
#define GPR_HAS_PTHREAD_H 1 #define GPR_HAS_PTHREAD_H 1
#define GPR_GETPID_IN_UNISTD_H 1 #define GPR_GETPID_IN_UNISTD_H 1
#ifndef GRPC_CFSTREAM #ifndef GRPC_CFSTREAM

@ -128,7 +128,8 @@ typedef enum {
/** The service is currently unavailable. This is a most likely a /** The service is currently unavailable. This is a most likely a
transient condition and may be corrected by retrying with transient condition and may be corrected by retrying with
a backoff. a backoff. Note that it is not always safe to retry non-idempotent
operations.
WARNING: Although data MIGHT not have been transmitted when this WARNING: Although data MIGHT not have been transmitted when this
status occurs, there is NOT A GUARANTEE that the server has not seen status occurs, there is NOT A GUARANTEE that the server has not seen

@ -32,8 +32,6 @@ typedef struct gpr_allocation_functions {
void* (*zalloc_fn)(size_t size); /** if NULL, uses malloc_fn then memset */ void* (*zalloc_fn)(size_t size); /** if NULL, uses malloc_fn then memset */
void* (*realloc_fn)(void* ptr, size_t size); void* (*realloc_fn)(void* ptr, size_t size);
void (*free_fn)(void* ptr); void (*free_fn)(void* ptr);
void* (*aligned_alloc_fn)(size_t size, size_t alignment);
void (*aligned_free_fn)(void* ptr);
} gpr_allocation_functions; } gpr_allocation_functions;
/** malloc. /** malloc.

@ -119,7 +119,8 @@ enum StatusCode {
INTERNAL = 13, INTERNAL = 13,
/// The service is currently unavailable. This is a most likely a transient /// The service is currently unavailable. This is a most likely a transient
/// condition and may be corrected by retrying with a backoff. /// condition and may be corrected by retrying with a backoff. Note that it is
/// not always safe to retry non-idempotent operations.
/// ///
/// \warning Although data MIGHT not have been transmitted when this /// \warning Although data MIGHT not have been transmitted when this
/// status occurs, there is NOT A GUARANTEE that the server has not seen /// status occurs, there is NOT A GUARANTEE that the server has not seen

@ -18,137 +18,7 @@
// Generates cpp gRPC service interface out of Protobuf IDL. // Generates cpp gRPC service interface out of Protobuf IDL.
// //
#include "src/compiler/cpp_plugin.h"
#include <memory>
#include <sstream>
#include "src/compiler/config.h"
#include "src/compiler/cpp_generator.h"
#include "src/compiler/generator_helpers.h"
#include "src/compiler/protobuf_plugin.h"
class CppGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator {
public:
CppGrpcGenerator() {}
virtual ~CppGrpcGenerator() {}
virtual bool Generate(const grpc::protobuf::FileDescriptor* file,
const grpc::string& parameter,
grpc::protobuf::compiler::GeneratorContext* context,
grpc::string* error) const {
if (file->options().cc_generic_services()) {
*error =
"cpp grpc proto compiler plugin does not work with generic "
"services. To generate cpp grpc APIs, please set \""
"cc_generic_service = false\".";
return false;
}
grpc_cpp_generator::Parameters generator_parameters;
generator_parameters.use_system_headers = true;
generator_parameters.generate_mock_code = false;
generator_parameters.include_import_headers = false;
ProtoBufFile pbfile(file);
if (!parameter.empty()) {
std::vector<grpc::string> parameters_list =
grpc_generator::tokenize(parameter, ",");
for (auto parameter_string = parameters_list.begin();
parameter_string != parameters_list.end(); parameter_string++) {
std::vector<grpc::string> param =
grpc_generator::tokenize(*parameter_string, "=");
if (param[0] == "services_namespace") {
generator_parameters.services_namespace = param[1];
} else if (param[0] == "use_system_headers") {
if (param[1] == "true") {
generator_parameters.use_system_headers = true;
} else if (param[1] == "false") {
generator_parameters.use_system_headers = false;
} else {
*error = grpc::string("Invalid parameter: ") + *parameter_string;
return false;
}
} else if (param[0] == "grpc_search_path") {
generator_parameters.grpc_search_path = param[1];
} else if (param[0] == "generate_mock_code") {
if (param[1] == "true") {
generator_parameters.generate_mock_code = true;
} else if (param[1] != "false") {
*error = grpc::string("Invalid parameter: ") + *parameter_string;
return false;
}
} else if (param[0] == "gmock_search_path") {
generator_parameters.gmock_search_path = param[1];
} else if (param[0] == "additional_header_includes") {
generator_parameters.additional_header_includes =
grpc_generator::tokenize(param[1], ":");
} else if (param[0] == "message_header_extension") {
generator_parameters.message_header_extension = param[1];
} else if (param[0] == "include_import_headers") {
if (param[1] == "true") {
generator_parameters.include_import_headers = true;
} else if (param[1] != "false") {
*error = grpc::string("Invalid parameter: ") + *parameter_string;
return false;
}
} else {
*error = grpc::string("Unknown parameter: ") + *parameter_string;
return false;
}
}
}
grpc::string file_name = grpc_generator::StripProto(file->name());
grpc::string header_code =
grpc_cpp_generator::GetHeaderPrologue(&pbfile, generator_parameters) +
grpc_cpp_generator::GetHeaderIncludes(&pbfile, generator_parameters) +
grpc_cpp_generator::GetHeaderServices(&pbfile, generator_parameters) +
grpc_cpp_generator::GetHeaderEpilogue(&pbfile, generator_parameters);
std::unique_ptr<grpc::protobuf::io::ZeroCopyOutputStream> header_output(
context->Open(file_name + ".grpc.pb.h"));
grpc::protobuf::io::CodedOutputStream header_coded_out(header_output.get());
header_coded_out.WriteRaw(header_code.data(), header_code.size());
grpc::string source_code =
grpc_cpp_generator::GetSourcePrologue(&pbfile, generator_parameters) +
grpc_cpp_generator::GetSourceIncludes(&pbfile, generator_parameters) +
grpc_cpp_generator::GetSourceServices(&pbfile, generator_parameters) +
grpc_cpp_generator::GetSourceEpilogue(&pbfile, generator_parameters);
std::unique_ptr<grpc::protobuf::io::ZeroCopyOutputStream> source_output(
context->Open(file_name + ".grpc.pb.cc"));
grpc::protobuf::io::CodedOutputStream source_coded_out(source_output.get());
source_coded_out.WriteRaw(source_code.data(), source_code.size());
if (!generator_parameters.generate_mock_code) {
return true;
}
grpc::string mock_code =
grpc_cpp_generator::GetMockPrologue(&pbfile, generator_parameters) +
grpc_cpp_generator::GetMockIncludes(&pbfile, generator_parameters) +
grpc_cpp_generator::GetMockServices(&pbfile, generator_parameters) +
grpc_cpp_generator::GetMockEpilogue(&pbfile, generator_parameters);
std::unique_ptr<grpc::protobuf::io::ZeroCopyOutputStream> mock_output(
context->Open(file_name + "_mock.grpc.pb.h"));
grpc::protobuf::io::CodedOutputStream mock_coded_out(mock_output.get());
mock_coded_out.WriteRaw(mock_code.data(), mock_code.size());
return true;
}
private:
// Insert the given code into the given file at the given insertion point.
void Insert(grpc::protobuf::compiler::GeneratorContext* context,
const grpc::string& filename, const grpc::string& insertion_point,
const grpc::string& code) const {
std::unique_ptr<grpc::protobuf::io::ZeroCopyOutputStream> output(
context->OpenForInsert(filename, insertion_point));
grpc::protobuf::io::CodedOutputStream coded_out(output.get());
coded_out.WriteRaw(code.data(), code.size());
}
};
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
CppGrpcGenerator generator; CppGrpcGenerator generator;

@ -0,0 +1,154 @@
/*
*
* Copyright 2019 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_INTERNAL_COMPILER_CPP_PLUGIN_H
#define GRPC_INTERNAL_COMPILER_CPP_PLUGIN_H
#include <memory>
#include <sstream>
#include "src/compiler/config.h"
#include "src/compiler/cpp_generator.h"
#include "src/compiler/generator_helpers.h"
#include "src/compiler/protobuf_plugin.h"
// Cpp Generator for Protobug IDL
class CppGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator {
public:
CppGrpcGenerator() {}
virtual ~CppGrpcGenerator() {}
virtual bool Generate(const grpc::protobuf::FileDescriptor* file,
const grpc::string& parameter,
grpc::protobuf::compiler::GeneratorContext* context,
grpc::string* error) const {
if (file->options().cc_generic_services()) {
*error =
"cpp grpc proto compiler plugin does not work with generic "
"services. To generate cpp grpc APIs, please set \""
"cc_generic_service = false\".";
return false;
}
grpc_cpp_generator::Parameters generator_parameters;
generator_parameters.use_system_headers = true;
generator_parameters.generate_mock_code = false;
generator_parameters.include_import_headers = false;
ProtoBufFile pbfile(file);
if (!parameter.empty()) {
std::vector<grpc::string> parameters_list =
grpc_generator::tokenize(parameter, ",");
for (auto parameter_string = parameters_list.begin();
parameter_string != parameters_list.end(); parameter_string++) {
std::vector<grpc::string> param =
grpc_generator::tokenize(*parameter_string, "=");
if (param[0] == "services_namespace") {
generator_parameters.services_namespace = param[1];
} else if (param[0] == "use_system_headers") {
if (param[1] == "true") {
generator_parameters.use_system_headers = true;
} else if (param[1] == "false") {
generator_parameters.use_system_headers = false;
} else {
*error = grpc::string("Invalid parameter: ") + *parameter_string;
return false;
}
} else if (param[0] == "grpc_search_path") {
generator_parameters.grpc_search_path = param[1];
} else if (param[0] == "generate_mock_code") {
if (param[1] == "true") {
generator_parameters.generate_mock_code = true;
} else if (param[1] != "false") {
*error = grpc::string("Invalid parameter: ") + *parameter_string;
return false;
}
} else if (param[0] == "gmock_search_path") {
generator_parameters.gmock_search_path = param[1];
} else if (param[0] == "additional_header_includes") {
generator_parameters.additional_header_includes =
grpc_generator::tokenize(param[1], ":");
} else if (param[0] == "message_header_extension") {
generator_parameters.message_header_extension = param[1];
} else if (param[0] == "include_import_headers") {
if (param[1] == "true") {
generator_parameters.include_import_headers = true;
} else if (param[1] != "false") {
*error = grpc::string("Invalid parameter: ") + *parameter_string;
return false;
}
} else {
*error = grpc::string("Unknown parameter: ") + *parameter_string;
return false;
}
}
}
grpc::string file_name = grpc_generator::StripProto(file->name());
grpc::string header_code =
grpc_cpp_generator::GetHeaderPrologue(&pbfile, generator_parameters) +
grpc_cpp_generator::GetHeaderIncludes(&pbfile, generator_parameters) +
grpc_cpp_generator::GetHeaderServices(&pbfile, generator_parameters) +
grpc_cpp_generator::GetHeaderEpilogue(&pbfile, generator_parameters);
std::unique_ptr<grpc::protobuf::io::ZeroCopyOutputStream> header_output(
context->Open(file_name + ".grpc.pb.h"));
grpc::protobuf::io::CodedOutputStream header_coded_out(header_output.get());
header_coded_out.WriteRaw(header_code.data(), header_code.size());
grpc::string source_code =
grpc_cpp_generator::GetSourcePrologue(&pbfile, generator_parameters) +
grpc_cpp_generator::GetSourceIncludes(&pbfile, generator_parameters) +
grpc_cpp_generator::GetSourceServices(&pbfile, generator_parameters) +
grpc_cpp_generator::GetSourceEpilogue(&pbfile, generator_parameters);
std::unique_ptr<grpc::protobuf::io::ZeroCopyOutputStream> source_output(
context->Open(file_name + ".grpc.pb.cc"));
grpc::protobuf::io::CodedOutputStream source_coded_out(source_output.get());
source_coded_out.WriteRaw(source_code.data(), source_code.size());
if (!generator_parameters.generate_mock_code) {
return true;
}
grpc::string mock_code =
grpc_cpp_generator::GetMockPrologue(&pbfile, generator_parameters) +
grpc_cpp_generator::GetMockIncludes(&pbfile, generator_parameters) +
grpc_cpp_generator::GetMockServices(&pbfile, generator_parameters) +
grpc_cpp_generator::GetMockEpilogue(&pbfile, generator_parameters);
std::unique_ptr<grpc::protobuf::io::ZeroCopyOutputStream> mock_output(
context->Open(file_name + "_mock.grpc.pb.h"));
grpc::protobuf::io::CodedOutputStream mock_coded_out(mock_output.get());
mock_coded_out.WriteRaw(mock_code.data(), mock_code.size());
return true;
}
private:
// Insert the given code into the given file at the given insertion point.
void Insert(grpc::protobuf::compiler::GeneratorContext* context,
const grpc::string& filename, const grpc::string& insertion_point,
const grpc::string& code) const {
std::unique_ptr<grpc::protobuf::io::ZeroCopyOutputStream> output(
context->OpenForInsert(filename, insertion_point));
grpc::protobuf::io::CodedOutputStream coded_out(output.get());
coded_out.WriteRaw(code.data(), code.size());
}
};
#endif // GRPC_INTERNAL_COMPILER_CPP_PLUGIN_H

@ -67,12 +67,11 @@
// Conversion between subchannel call and call stack. // Conversion between subchannel call and call stack.
#define SUBCHANNEL_CALL_TO_CALL_STACK(call) \ #define SUBCHANNEL_CALL_TO_CALL_STACK(call) \
(grpc_call_stack*)((char*)(call) + GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE( \ (grpc_call_stack*)((char*)(call) + \
sizeof(SubchannelCall))) GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(SubchannelCall)))
#define CALL_STACK_TO_SUBCHANNEL_CALL(callstack) \ #define CALL_STACK_TO_SUBCHANNEL_CALL(callstack) \
(SubchannelCall*)(((char*)(call_stack)) - \ (SubchannelCall*)(((char*)(call_stack)) - \
GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE( \ GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(SubchannelCall)))
sizeof(SubchannelCall)))
namespace grpc_core { namespace grpc_core {
@ -152,10 +151,10 @@ RefCountedPtr<SubchannelCall> ConnectedSubchannel::CreateCall(
size_t ConnectedSubchannel::GetInitialCallSizeEstimate( size_t ConnectedSubchannel::GetInitialCallSizeEstimate(
size_t parent_data_size) const { size_t parent_data_size) const {
size_t allocation_size = size_t allocation_size =
GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE(sizeof(SubchannelCall)); GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(SubchannelCall));
if (parent_data_size > 0) { if (parent_data_size > 0) {
allocation_size += allocation_size +=
GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE(channel_stack_->call_stack_size) + GPR_ROUND_UP_TO_ALIGNMENT_SIZE(channel_stack_->call_stack_size) +
parent_data_size; parent_data_size;
} else { } else {
allocation_size += channel_stack_->call_stack_size; allocation_size += channel_stack_->call_stack_size;
@ -179,9 +178,8 @@ void SubchannelCall::StartTransportStreamOpBatch(
void* SubchannelCall::GetParentData() { void* SubchannelCall::GetParentData() {
grpc_channel_stack* chanstk = connected_subchannel_->channel_stack(); grpc_channel_stack* chanstk = connected_subchannel_->channel_stack();
return (char*)this + return (char*)this + GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(SubchannelCall)) +
GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE(sizeof(SubchannelCall)) + GPR_ROUND_UP_TO_ALIGNMENT_SIZE(chanstk->call_stack_size);
GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE(chanstk->call_stack_size);
} }
grpc_call_stack* SubchannelCall::GetCallStack() { grpc_call_stack* SubchannelCall::GetCallStack() {

@ -679,8 +679,6 @@ grpc_chttp2_stream::grpc_chttp2_stream(grpc_chttp2_transport* t,
grpc_slice_buffer_init(&frame_storage); grpc_slice_buffer_init(&frame_storage);
grpc_slice_buffer_init(&unprocessed_incoming_frames_buffer); grpc_slice_buffer_init(&unprocessed_incoming_frames_buffer);
grpc_slice_buffer_init(&flow_controlled_buffer); grpc_slice_buffer_init(&flow_controlled_buffer);
grpc_slice_buffer_init(&compressed_data_buffer);
grpc_slice_buffer_init(&decompressed_data_buffer);
GRPC_CLOSURE_INIT(&complete_fetch_locked, ::complete_fetch_locked, this, GRPC_CLOSURE_INIT(&complete_fetch_locked, ::complete_fetch_locked, this,
grpc_combiner_scheduler(t->combiner)); grpc_combiner_scheduler(t->combiner));
@ -704,8 +702,13 @@ grpc_chttp2_stream::~grpc_chttp2_stream() {
grpc_slice_buffer_destroy_internal(&unprocessed_incoming_frames_buffer); grpc_slice_buffer_destroy_internal(&unprocessed_incoming_frames_buffer);
grpc_slice_buffer_destroy_internal(&frame_storage); grpc_slice_buffer_destroy_internal(&frame_storage);
if (stream_compression_method != GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS) {
grpc_slice_buffer_destroy_internal(&compressed_data_buffer); grpc_slice_buffer_destroy_internal(&compressed_data_buffer);
}
if (stream_decompression_method !=
GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS) {
grpc_slice_buffer_destroy_internal(&decompressed_data_buffer); grpc_slice_buffer_destroy_internal(&decompressed_data_buffer);
}
grpc_chttp2_list_remove_stalled_by_transport(t, this); grpc_chttp2_list_remove_stalled_by_transport(t, this);
grpc_chttp2_list_remove_stalled_by_stream(t, this); grpc_chttp2_list_remove_stalled_by_stream(t, this);
@ -759,12 +762,15 @@ static void destroy_stream(grpc_transport* gt, grpc_stream* gs,
GPR_TIMER_SCOPE("destroy_stream", 0); GPR_TIMER_SCOPE("destroy_stream", 0);
grpc_chttp2_transport* t = reinterpret_cast<grpc_chttp2_transport*>(gt); grpc_chttp2_transport* t = reinterpret_cast<grpc_chttp2_transport*>(gt);
grpc_chttp2_stream* s = reinterpret_cast<grpc_chttp2_stream*>(gs); grpc_chttp2_stream* s = reinterpret_cast<grpc_chttp2_stream*>(gs);
if (s->stream_compression_method !=
if (s->stream_compression_ctx != nullptr) { GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS &&
s->stream_compression_ctx != nullptr) {
grpc_stream_compression_context_destroy(s->stream_compression_ctx); grpc_stream_compression_context_destroy(s->stream_compression_ctx);
s->stream_compression_ctx = nullptr; s->stream_compression_ctx = nullptr;
} }
if (s->stream_decompression_ctx != nullptr) { if (s->stream_decompression_method !=
GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS &&
s->stream_decompression_ctx != nullptr) {
grpc_stream_compression_context_destroy(s->stream_decompression_ctx); grpc_stream_compression_context_destroy(s->stream_decompression_ctx);
s->stream_decompression_ctx = nullptr; s->stream_decompression_ctx = nullptr;
} }
@ -1442,7 +1448,12 @@ static void perform_stream_op_locked(void* stream_op,
true, &s->stream_compression_method) == 0) { true, &s->stream_compression_method) == 0) {
s->stream_compression_method = GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS; s->stream_compression_method = GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS;
} }
if (s->stream_compression_method !=
GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS) {
s->uncompressed_data_size = 0;
s->stream_compression_ctx = nullptr;
grpc_slice_buffer_init(&s->compressed_data_buffer);
}
s->send_initial_metadata_finished = add_closure_barrier(on_complete); s->send_initial_metadata_finished = add_closure_barrier(on_complete);
s->send_initial_metadata = s->send_initial_metadata =
op_payload->send_initial_metadata.send_initial_metadata; op_payload->send_initial_metadata.send_initial_metadata;
@ -1998,6 +2009,16 @@ void grpc_chttp2_maybe_complete_recv_trailing_metadata(grpc_chttp2_transport* t,
!s->seen_error && s->recv_trailing_metadata_finished != nullptr) { !s->seen_error && s->recv_trailing_metadata_finished != nullptr) {
/* Maybe some SYNC_FLUSH data is left in frame_storage. Consume them and /* Maybe some SYNC_FLUSH data is left in frame_storage. Consume them and
* maybe decompress the next 5 bytes in the stream. */ * maybe decompress the next 5 bytes in the stream. */
if (s->stream_decompression_method ==
GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS) {
grpc_slice_buffer_move_first(&s->frame_storage,
GRPC_HEADER_SIZE_IN_BYTES,
&s->unprocessed_incoming_frames_buffer);
if (s->unprocessed_incoming_frames_buffer.length > 0) {
s->unprocessed_incoming_frames_decompressed = true;
pending_data = true;
}
} else {
bool end_of_context; bool end_of_context;
if (!s->stream_decompression_ctx) { if (!s->stream_decompression_ctx) {
s->stream_decompression_ctx = grpc_stream_compression_context_create( s->stream_decompression_ctx = grpc_stream_compression_context_create(
@ -2017,11 +2038,13 @@ void grpc_chttp2_maybe_complete_recv_trailing_metadata(grpc_chttp2_transport* t,
pending_data = true; pending_data = true;
} }
if (end_of_context) { if (end_of_context) {
grpc_stream_compression_context_destroy(s->stream_decompression_ctx); grpc_stream_compression_context_destroy(
s->stream_decompression_ctx);
s->stream_decompression_ctx = nullptr; s->stream_decompression_ctx = nullptr;
} }
} }
} }
}
if (s->read_closed && s->frame_storage.length == 0 && !pending_data && if (s->read_closed && s->frame_storage.length == 0 && !pending_data &&
s->recv_trailing_metadata_finished != nullptr) { s->recv_trailing_metadata_finished != nullptr) {
grpc_transport_move_stats(&s->stats, s->collecting_stats); grpc_transport_move_stats(&s->stats, s->collecting_stats);
@ -2941,6 +2964,8 @@ bool Chttp2IncomingByteStream::Next(size_t max_size_hint,
} }
void Chttp2IncomingByteStream::MaybeCreateStreamDecompressionCtx() { void Chttp2IncomingByteStream::MaybeCreateStreamDecompressionCtx() {
GPR_DEBUG_ASSERT(stream_->stream_decompression_method !=
GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS);
if (!stream_->stream_decompression_ctx) { if (!stream_->stream_decompression_ctx) {
stream_->stream_decompression_ctx = grpc_stream_compression_context_create( stream_->stream_decompression_ctx = grpc_stream_compression_context_create(
stream_->stream_decompression_method); stream_->stream_decompression_method);
@ -2951,7 +2976,9 @@ grpc_error* Chttp2IncomingByteStream::Pull(grpc_slice* slice) {
GPR_TIMER_SCOPE("incoming_byte_stream_pull", 0); GPR_TIMER_SCOPE("incoming_byte_stream_pull", 0);
grpc_error* error; grpc_error* error;
if (stream_->unprocessed_incoming_frames_buffer.length > 0) { if (stream_->unprocessed_incoming_frames_buffer.length > 0) {
if (!stream_->unprocessed_incoming_frames_decompressed) { if (!stream_->unprocessed_incoming_frames_decompressed &&
stream_->stream_decompression_method !=
GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS) {
bool end_of_context; bool end_of_context;
MaybeCreateStreamDecompressionCtx(); MaybeCreateStreamDecompressionCtx();
if (!grpc_stream_decompress(stream_->stream_decompression_ctx, if (!grpc_stream_decompress(stream_->stream_decompression_ctx,

@ -1616,6 +1616,12 @@ static void parse_stream_compression_md(grpc_chttp2_transport* t,
s->stream_decompression_method = s->stream_decompression_method =
GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS; GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS;
} }
if (s->stream_decompression_method !=
GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS) {
s->stream_decompression_ctx = nullptr;
grpc_slice_buffer_init(&s->decompressed_data_buffer);
}
} }
grpc_error* grpc_chttp2_header_parser_parse(void* hpack_parser, grpc_error* grpc_chttp2_header_parser_parse(void* hpack_parser,

@ -238,7 +238,7 @@ class Chttp2IncomingByteStream : public ByteStream {
// switch to std::shared_ptr<>. // switch to std::shared_ptr<>.
void Ref() { refs_.Ref(); } void Ref() { refs_.Ref(); }
void Unref() { void Unref() {
if (refs_.Unref()) { if (GPR_UNLIKELY(refs_.Unref())) {
grpc_core::Delete(this); grpc_core::Delete(this);
} }
} }
@ -583,10 +583,6 @@ struct grpc_chttp2_stream {
grpc_slice_buffer frame_storage; /* protected by t combiner */ grpc_slice_buffer frame_storage; /* protected by t combiner */
/* Accessed only by transport thread when stream->pending_byte_stream == false
* Accessed only by application thread when stream->pending_byte_stream ==
* true */
grpc_slice_buffer unprocessed_incoming_frames_buffer;
grpc_closure* on_next = nullptr; /* protected by t combiner */ grpc_closure* on_next = nullptr; /* protected by t combiner */
bool pending_byte_stream = false; /* protected by t combiner */ bool pending_byte_stream = false; /* protected by t combiner */
// cached length of buffer to be used by the transport thread in cases where // cached length of buffer to be used by the transport thread in cases where
@ -594,6 +590,10 @@ struct grpc_chttp2_stream {
// application threads are allowed to modify // application threads are allowed to modify
// unprocessed_incoming_frames_buffer // unprocessed_incoming_frames_buffer
size_t unprocessed_incoming_frames_buffer_cached_length = 0; size_t unprocessed_incoming_frames_buffer_cached_length = 0;
/* Accessed only by transport thread when stream->pending_byte_stream == false
* Accessed only by application thread when stream->pending_byte_stream ==
* true */
grpc_slice_buffer unprocessed_incoming_frames_buffer;
grpc_closure reset_byte_stream; grpc_closure reset_byte_stream;
grpc_error* byte_stream_error = GRPC_ERROR_NONE; /* protected by t combiner */ grpc_error* byte_stream_error = GRPC_ERROR_NONE; /* protected by t combiner */
bool received_last_frame = false; /* protected by t combiner */ bool received_last_frame = false; /* protected by t combiner */
@ -634,18 +634,7 @@ struct grpc_chttp2_stream {
/* Stream decompression method to be used. */ /* Stream decompression method to be used. */
grpc_stream_compression_method stream_decompression_method = grpc_stream_compression_method stream_decompression_method =
GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS; GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS;
/** Stream compression decompress context */
grpc_stream_compression_context* stream_decompression_ctx = nullptr;
/** Stream compression compress context */
grpc_stream_compression_context* stream_compression_ctx = nullptr;
/** Buffer storing data that is compressed but not sent */
grpc_slice_buffer compressed_data_buffer;
/** Amount of uncompressed bytes sent out when compressed_data_buffer is
* emptied */
size_t uncompressed_data_size = 0;
/** Temporary buffer storing decompressed data */
grpc_slice_buffer decompressed_data_buffer;
/** Whether bytes stored in unprocessed_incoming_byte_stream is decompressed /** Whether bytes stored in unprocessed_incoming_byte_stream is decompressed
*/ */
bool unprocessed_incoming_frames_decompressed = false; bool unprocessed_incoming_frames_decompressed = false;
@ -655,6 +644,22 @@ struct grpc_chttp2_stream {
size_t decompressed_header_bytes = 0; size_t decompressed_header_bytes = 0;
/** Byte counter for number of bytes written */ /** Byte counter for number of bytes written */
size_t byte_counter = 0; size_t byte_counter = 0;
/** Amount of uncompressed bytes sent out when compressed_data_buffer is
* emptied */
size_t uncompressed_data_size;
/** Stream compression compress context */
grpc_stream_compression_context* stream_compression_ctx;
/** Buffer storing data that is compressed but not sent */
grpc_slice_buffer compressed_data_buffer;
/** Stream compression decompress context */
grpc_stream_compression_context* stream_decompression_ctx;
/** Temporary buffer storing decompressed data.
* Initialized, used, and destroyed only when stream uses (non-identity)
* compression.
*/
grpc_slice_buffer decompressed_data_buffer;
}; };
/** Transport writing call flow: /** Transport writing call flow:

@ -25,6 +25,7 @@
#include <grpc/support/log.h> #include <grpc/support/log.h>
#include "src/core/lib/compression/stream_compression.h"
#include "src/core/lib/debug/stats.h" #include "src/core/lib/debug/stats.h"
#include "src/core/lib/profiling/timers.h" #include "src/core/lib/profiling/timers.h"
#include "src/core/lib/slice/slice_internal.h" #include "src/core/lib/slice/slice_internal.h"
@ -150,7 +151,11 @@ static void report_stall(grpc_chttp2_transport* t, grpc_chttp2_stream* s,
":flowed=%" PRId64 ":peer_initwin=%d:t_win=%" PRId64 ":flowed=%" PRId64 ":peer_initwin=%d:t_win=%" PRId64
":s_win=%d:s_delta=%" PRId64 "]", ":s_win=%d:s_delta=%" PRId64 "]",
t->peer_string, t, s->id, staller, s->flow_controlled_buffer.length, t->peer_string, t, s->id, staller, s->flow_controlled_buffer.length,
s->compressed_data_buffer.length, s->flow_controlled_bytes_flowed, s->stream_compression_method ==
GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS
? 0
: s->compressed_data_buffer.length,
s->flow_controlled_bytes_flowed,
t->settings[GRPC_ACKED_SETTINGS] t->settings[GRPC_ACKED_SETTINGS]
[GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE], [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE],
t->flow_control->remote_window(), t->flow_control->remote_window(),
@ -325,7 +330,23 @@ class DataSendContext {
bool AnyOutgoing() const { return max_outgoing() > 0; } bool AnyOutgoing() const { return max_outgoing() > 0; }
void FlushUncompressedBytes() {
uint32_t send_bytes = static_cast<uint32_t> GPR_MIN(
max_outgoing(), s_->flow_controlled_buffer.length);
is_last_frame_ = send_bytes == s_->flow_controlled_buffer.length &&
s_->fetching_send_message == nullptr &&
s_->send_trailing_metadata != nullptr &&
grpc_metadata_batch_is_empty(s_->send_trailing_metadata);
grpc_chttp2_encode_data(s_->id, &s_->flow_controlled_buffer, send_bytes,
is_last_frame_, &s_->stats.outgoing, &t_->outbuf);
s_->flow_control->SentData(send_bytes);
s_->sending_bytes += send_bytes;
}
void FlushCompressedBytes() { void FlushCompressedBytes() {
GPR_DEBUG_ASSERT(s_->stream_compression_method !=
GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS);
uint32_t send_bytes = static_cast<uint32_t> GPR_MIN( uint32_t send_bytes = static_cast<uint32_t> GPR_MIN(
max_outgoing(), s_->compressed_data_buffer.length); max_outgoing(), s_->compressed_data_buffer.length);
bool is_last_data_frame = bool is_last_data_frame =
@ -360,6 +381,9 @@ class DataSendContext {
} }
void CompressMoreBytes() { void CompressMoreBytes() {
GPR_DEBUG_ASSERT(s_->stream_compression_method !=
GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS);
if (s_->stream_compression_ctx == nullptr) { if (s_->stream_compression_ctx == nullptr) {
s_->stream_compression_ctx = s_->stream_compression_ctx =
grpc_stream_compression_context_create(s_->stream_compression_method); grpc_stream_compression_context_create(s_->stream_compression_method);
@ -417,7 +441,7 @@ class StreamWriteContext {
// https://github.com/grpc/proposal/blob/master/A6-client-retries.md#when-retries-are-valid // https://github.com/grpc/proposal/blob/master/A6-client-retries.md#when-retries-are-valid
if (!t_->is_client && s_->fetching_send_message == nullptr && if (!t_->is_client && s_->fetching_send_message == nullptr &&
s_->flow_controlled_buffer.length == 0 && s_->flow_controlled_buffer.length == 0 &&
s_->compressed_data_buffer.length == 0 && compressed_data_buffer_len() == 0 &&
s_->send_trailing_metadata != nullptr && s_->send_trailing_metadata != nullptr &&
is_default_initial_metadata(s_->send_initial_metadata)) { is_default_initial_metadata(s_->send_initial_metadata)) {
ConvertInitialMetadataToTrailingMetadata(); ConvertInitialMetadataToTrailingMetadata();
@ -446,6 +470,13 @@ class StreamWriteContext {
"send_initial_metadata_finished"); "send_initial_metadata_finished");
} }
bool compressed_data_buffer_len() {
return s_->stream_compression_method ==
GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS
? 0
: s_->compressed_data_buffer.length;
}
void FlushWindowUpdates() { void FlushWindowUpdates() {
/* send any window updates */ /* send any window updates */
const uint32_t stream_announce = s_->flow_control->MaybeSendUpdate(); const uint32_t stream_announce = s_->flow_control->MaybeSendUpdate();
@ -462,7 +493,7 @@ class StreamWriteContext {
if (!s_->sent_initial_metadata) return; if (!s_->sent_initial_metadata) return;
if (s_->flow_controlled_buffer.length == 0 && if (s_->flow_controlled_buffer.length == 0 &&
s_->compressed_data_buffer.length == 0) { compressed_data_buffer_len() == 0) {
return; // early out: nothing to do return; // early out: nothing to do
} }
@ -479,6 +510,13 @@ class StreamWriteContext {
return; // early out: nothing to do return; // early out: nothing to do
} }
if (s_->stream_compression_method ==
GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS) {
while (s_->flow_controlled_buffer.length > 0 &&
data_send_context.max_outgoing() > 0) {
data_send_context.FlushUncompressedBytes();
}
} else {
while ((s_->flow_controlled_buffer.length > 0 || while ((s_->flow_controlled_buffer.length > 0 ||
s_->compressed_data_buffer.length > 0) && s_->compressed_data_buffer.length > 0) &&
data_send_context.max_outgoing() > 0) { data_send_context.max_outgoing() > 0) {
@ -488,6 +526,7 @@ class StreamWriteContext {
data_send_context.CompressMoreBytes(); data_send_context.CompressMoreBytes();
} }
} }
}
write_context_->ResetPingClock(); write_context_->ResetPingClock();
if (data_send_context.is_last_frame()) { if (data_send_context.is_last_frame()) {
SentLastFrame(); SentLastFrame();
@ -495,7 +534,7 @@ class StreamWriteContext {
data_send_context.CallCallbacks(); data_send_context.CallCallbacks();
stream_became_writable_ = true; stream_became_writable_ = true;
if (s_->flow_controlled_buffer.length > 0 || if (s_->flow_controlled_buffer.length > 0 ||
s_->compressed_data_buffer.length > 0) { compressed_data_buffer_len() > 0) {
GRPC_CHTTP2_STREAM_REF(s_, "chttp2_writing:fork"); GRPC_CHTTP2_STREAM_REF(s_, "chttp2_writing:fork");
grpc_chttp2_list_add_writable_stream(t_, s_); grpc_chttp2_list_add_writable_stream(t_, s_);
} }
@ -508,7 +547,7 @@ class StreamWriteContext {
if (s_->send_trailing_metadata == nullptr) return; if (s_->send_trailing_metadata == nullptr) return;
if (s_->fetching_send_message != nullptr) return; if (s_->fetching_send_message != nullptr) return;
if (s_->flow_controlled_buffer.length != 0) return; if (s_->flow_controlled_buffer.length != 0) return;
if (s_->compressed_data_buffer.length != 0) return; if (compressed_data_buffer_len() != 0) return;
GRPC_CHTTP2_IF_TRACING(gpr_log(GPR_INFO, "sending trailing_metadata")); GRPC_CHTTP2_IF_TRACING(gpr_log(GPR_INFO, "sending trailing_metadata"));
if (grpc_metadata_batch_is_empty(s_->send_trailing_metadata)) { if (grpc_metadata_batch_is_empty(s_->send_trailing_metadata)) {

@ -47,9 +47,9 @@ grpc_core::TraceFlag grpc_trace_channel(false, "channel");
size_t grpc_channel_stack_size(const grpc_channel_filter** filters, size_t grpc_channel_stack_size(const grpc_channel_filter** filters,
size_t filter_count) { size_t filter_count) {
/* always need the header, and size for the channel elements */ /* always need the header, and size for the channel elements */
size_t size = GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE(sizeof(grpc_channel_stack)) + size_t size = GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_channel_stack)) +
GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE( GPR_ROUND_UP_TO_ALIGNMENT_SIZE(filter_count *
filter_count * sizeof(grpc_channel_element)); sizeof(grpc_channel_element));
size_t i; size_t i;
GPR_ASSERT((GPR_MAX_ALIGNMENT & (GPR_MAX_ALIGNMENT - 1)) == 0 && GPR_ASSERT((GPR_MAX_ALIGNMENT & (GPR_MAX_ALIGNMENT - 1)) == 0 &&
@ -57,18 +57,18 @@ size_t grpc_channel_stack_size(const grpc_channel_filter** filters,
/* add the size for each filter */ /* add the size for each filter */
for (i = 0; i < filter_count; i++) { for (i = 0; i < filter_count; i++) {
size += GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE(filters[i]->sizeof_channel_data); size += GPR_ROUND_UP_TO_ALIGNMENT_SIZE(filters[i]->sizeof_channel_data);
} }
return size; return size;
} }
#define CHANNEL_ELEMS_FROM_STACK(stk) \ #define CHANNEL_ELEMS_FROM_STACK(stk) \
((grpc_channel_element*)((char*)(stk) + GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE( \ ((grpc_channel_element*)((char*)(stk) + GPR_ROUND_UP_TO_ALIGNMENT_SIZE( \
sizeof(grpc_channel_stack)))) sizeof(grpc_channel_stack))))
#define CALL_ELEMS_FROM_STACK(stk) \ #define CALL_ELEMS_FROM_STACK(stk) \
((grpc_call_element*)((char*)(stk) + GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE( \ ((grpc_call_element*)((char*)(stk) + GPR_ROUND_UP_TO_ALIGNMENT_SIZE( \
sizeof(grpc_call_stack)))) sizeof(grpc_call_stack))))
grpc_channel_element* grpc_channel_stack_element( grpc_channel_element* grpc_channel_stack_element(
@ -92,9 +92,8 @@ grpc_error* grpc_channel_stack_init(
const grpc_channel_args* channel_args, grpc_transport* optional_transport, const grpc_channel_args* channel_args, grpc_transport* optional_transport,
const char* name, grpc_channel_stack* stack) { const char* name, grpc_channel_stack* stack) {
size_t call_size = size_t call_size =
GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE(sizeof(grpc_call_stack)) + GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_call_stack)) +
GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE(filter_count * GPR_ROUND_UP_TO_ALIGNMENT_SIZE(filter_count * sizeof(grpc_call_element));
sizeof(grpc_call_element));
grpc_channel_element* elems; grpc_channel_element* elems;
grpc_channel_element_args args; grpc_channel_element_args args;
char* user_data; char* user_data;
@ -105,7 +104,7 @@ grpc_error* grpc_channel_stack_init(
name); name);
elems = CHANNEL_ELEMS_FROM_STACK(stack); elems = CHANNEL_ELEMS_FROM_STACK(stack);
user_data = (reinterpret_cast<char*>(elems)) + user_data = (reinterpret_cast<char*>(elems)) +
GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE(filter_count * GPR_ROUND_UP_TO_ALIGNMENT_SIZE(filter_count *
sizeof(grpc_channel_element)); sizeof(grpc_channel_element));
/* init per-filter data */ /* init per-filter data */
@ -127,9 +126,8 @@ grpc_error* grpc_channel_stack_init(
} }
} }
user_data += user_data +=
GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE(filters[i]->sizeof_channel_data); GPR_ROUND_UP_TO_ALIGNMENT_SIZE(filters[i]->sizeof_channel_data);
call_size += call_size += GPR_ROUND_UP_TO_ALIGNMENT_SIZE(filters[i]->sizeof_call_data);
GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE(filters[i]->sizeof_call_data);
} }
GPR_ASSERT(user_data > (char*)stack); GPR_ASSERT(user_data > (char*)stack);
@ -164,9 +162,8 @@ grpc_error* grpc_call_stack_init(grpc_channel_stack* channel_stack,
GRPC_STREAM_REF_INIT(&elem_args->call_stack->refcount, initial_refs, destroy, GRPC_STREAM_REF_INIT(&elem_args->call_stack->refcount, initial_refs, destroy,
destroy_arg, "CALL_STACK"); destroy_arg, "CALL_STACK");
call_elems = CALL_ELEMS_FROM_STACK(elem_args->call_stack); call_elems = CALL_ELEMS_FROM_STACK(elem_args->call_stack);
user_data = user_data = (reinterpret_cast<char*>(call_elems)) +
(reinterpret_cast<char*>(call_elems)) + GPR_ROUND_UP_TO_ALIGNMENT_SIZE(count * sizeof(grpc_call_element));
GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE(count * sizeof(grpc_call_element));
/* init per-filter data */ /* init per-filter data */
grpc_error* first_error = GRPC_ERROR_NONE; grpc_error* first_error = GRPC_ERROR_NONE;
@ -174,8 +171,8 @@ grpc_error* grpc_call_stack_init(grpc_channel_stack* channel_stack,
call_elems[i].filter = channel_elems[i].filter; call_elems[i].filter = channel_elems[i].filter;
call_elems[i].channel_data = channel_elems[i].channel_data; call_elems[i].channel_data = channel_elems[i].channel_data;
call_elems[i].call_data = user_data; call_elems[i].call_data = user_data;
user_data += GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE( user_data +=
call_elems[i].filter->sizeof_call_data); GPR_ROUND_UP_TO_ALIGNMENT_SIZE(call_elems[i].filter->sizeof_call_data);
} }
for (size_t i = 0; i < count; i++) { for (size_t i = 0; i < count; i++) {
grpc_error* error = grpc_error* error =
@ -245,11 +242,11 @@ grpc_channel_stack* grpc_channel_stack_from_top_element(
grpc_channel_element* elem) { grpc_channel_element* elem) {
return reinterpret_cast<grpc_channel_stack*>( return reinterpret_cast<grpc_channel_stack*>(
reinterpret_cast<char*>(elem) - reinterpret_cast<char*>(elem) -
GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE(sizeof(grpc_channel_stack))); GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_channel_stack)));
} }
grpc_call_stack* grpc_call_stack_from_top_element(grpc_call_element* elem) { grpc_call_stack* grpc_call_stack_from_top_element(grpc_call_element* elem) {
return reinterpret_cast<grpc_call_stack*>( return reinterpret_cast<grpc_call_stack*>(
reinterpret_cast<char*>(elem) - reinterpret_cast<char*>(elem) -
GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE(sizeof(grpc_call_stack))); GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_call_stack)));
} }

@ -23,7 +23,6 @@
#include <grpc/support/log.h> #include <grpc/support/log.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "src/core/lib/gpr/alloc.h"
#include "src/core/lib/profiling/timers.h" #include "src/core/lib/profiling/timers.h"
static void* zalloc_with_calloc(size_t sz) { return calloc(sz, 1); } static void* zalloc_with_calloc(size_t sz) { return calloc(sz, 1); }
@ -34,56 +33,8 @@ static void* zalloc_with_gpr_malloc(size_t sz) {
return p; return p;
} }
#ifndef NDEBUG static gpr_allocation_functions g_alloc_functions = {malloc, zalloc_with_calloc,
static constexpr bool is_power_of_two(size_t value) { realloc, free};
// 2^N = 100000...000
// 2^N - 1 = 011111...111
// (2^N) && ((2^N)-1)) = 0
return (value & (value - 1)) == 0;
}
#endif
static void* platform_malloc_aligned(size_t size, size_t alignment) {
#if defined(GPR_HAS_ALIGNED_ALLOC)
GPR_DEBUG_ASSERT(is_power_of_two(alignment));
size = GPR_ROUND_UP_TO_ALIGNMENT_SIZE(size, alignment);
void* ret = aligned_alloc(alignment, size);
GPR_ASSERT(ret != nullptr);
return ret;
#elif defined(GPR_HAS_ALIGNED_MALLOC)
GPR_DEBUG_ASSERT(is_power_of_two(alignment));
void* ret = _aligned_malloc(size, alignment);
GPR_ASSERT(ret != nullptr);
return ret;
#elif defined(GPR_HAS_POSIX_MEMALIGN)
GPR_DEBUG_ASSERT(is_power_of_two(alignment));
GPR_DEBUG_ASSERT(alignment % sizeof(void*) == 0);
void* ret = nullptr;
GPR_ASSERT(posix_memalign(&ret, alignment, size) == 0);
return ret;
#else
GPR_DEBUG_ASSERT(is_power_of_two(alignment));
size_t extra = alignment - 1 + sizeof(void*);
void* p = gpr_malloc(size + extra);
void** ret = (void**)(((uintptr_t)p + extra) & ~(alignment - 1));
ret[-1] = p;
return (void*)ret;
#endif
}
static void platform_free_aligned(void* ptr) {
#if defined(GPR_HAS_ALIGNED_ALLOC) || defined(GPR_HAS_POSIX_MEMALIGN)
free(ptr);
#elif defined(GPR_HAS_ALIGNED_MALLOC)
_aligned_free(ptr);
#else
gpr_free((static_cast<void**>(ptr))[-1]);
#endif
}
static gpr_allocation_functions g_alloc_functions = {
malloc, zalloc_with_calloc, realloc,
free, platform_malloc_aligned, platform_free_aligned};
gpr_allocation_functions gpr_get_allocation_functions() { gpr_allocation_functions gpr_get_allocation_functions() {
return g_alloc_functions; return g_alloc_functions;
@ -96,12 +47,6 @@ void gpr_set_allocation_functions(gpr_allocation_functions functions) {
if (functions.zalloc_fn == nullptr) { if (functions.zalloc_fn == nullptr) {
functions.zalloc_fn = zalloc_with_gpr_malloc; functions.zalloc_fn = zalloc_with_gpr_malloc;
} }
GPR_ASSERT((functions.aligned_alloc_fn == nullptr) ==
(functions.aligned_free_fn == nullptr));
if (functions.aligned_alloc_fn == nullptr) {
functions.aligned_alloc_fn = platform_malloc_aligned;
functions.aligned_free_fn = platform_free_aligned;
}
g_alloc_functions = functions; g_alloc_functions = functions;
} }
@ -143,12 +88,12 @@ void* gpr_realloc(void* p, size_t size) {
} }
void* gpr_malloc_aligned(size_t size, size_t alignment) { void* gpr_malloc_aligned(size_t size, size_t alignment) {
GPR_TIMER_SCOPE("gpr_malloc_aligned", 0); GPR_ASSERT(((alignment - 1) & alignment) == 0); // Must be power of 2.
if (size == 0) return nullptr; size_t extra = alignment - 1 + sizeof(void*);
return g_alloc_functions.aligned_alloc_fn(size, alignment); void* p = gpr_malloc(size + extra);
void** ret = (void**)(((uintptr_t)p + extra) & ~(alignment - 1));
ret[-1] = p;
return (void*)ret;
} }
void gpr_free_aligned(void* ptr) { void gpr_free_aligned(void* ptr) { gpr_free((static_cast<void**>(ptr))[-1]); }
GPR_TIMER_SCOPE("gpr_free_aligned", 0);
g_alloc_functions.aligned_free_fn(ptr);
}

@ -22,13 +22,7 @@
#include <grpc/support/port_platform.h> #include <grpc/support/port_platform.h>
/// Given a size, round up to the next multiple of sizeof(void*). /// Given a size, round up to the next multiple of sizeof(void*).
#define GPR_ROUND_UP_TO_ALIGNMENT_SIZE(x, align) \ #define GPR_ROUND_UP_TO_ALIGNMENT_SIZE(x) \
(((x) + (align)-1u) & ~((align)-1u)) (((x) + GPR_MAX_ALIGNMENT - 1u) & ~(GPR_MAX_ALIGNMENT - 1u))
#define GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE(x) \
GPR_ROUND_UP_TO_ALIGNMENT_SIZE((x), GPR_MAX_ALIGNMENT)
#define GPR_ROUND_UP_TO_CACHELINE_SIZE(x) \
GPR_ROUND_UP_TO_ALIGNMENT_SIZE((x), GPR_CACHELINE_SIZE)
#endif /* GRPC_CORE_LIB_GPR_ALLOC_H */ #endif /* GRPC_CORE_LIB_GPR_ALLOC_H */

@ -35,8 +35,8 @@ namespace {
void* ArenaStorage(size_t initial_size) { void* ArenaStorage(size_t initial_size) {
static constexpr size_t base_size = static constexpr size_t base_size =
GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE(sizeof(grpc_core::Arena)); GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_core::Arena));
initial_size = GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE(initial_size); initial_size = GPR_ROUND_UP_TO_ALIGNMENT_SIZE(initial_size);
size_t alloc_size = base_size + initial_size; size_t alloc_size = base_size + initial_size;
static constexpr size_t alignment = static constexpr size_t alignment =
(GPR_CACHELINE_SIZE > GPR_MAX_ALIGNMENT && (GPR_CACHELINE_SIZE > GPR_MAX_ALIGNMENT &&
@ -67,7 +67,7 @@ Arena* Arena::Create(size_t initial_size) {
Pair<Arena*, void*> Arena::CreateWithAlloc(size_t initial_size, Pair<Arena*, void*> Arena::CreateWithAlloc(size_t initial_size,
size_t alloc_size) { size_t alloc_size) {
static constexpr size_t base_size = static constexpr size_t base_size =
GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE(sizeof(Arena)); GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(Arena));
auto* new_arena = auto* new_arena =
new (ArenaStorage(initial_size)) Arena(initial_size, alloc_size); new (ArenaStorage(initial_size)) Arena(initial_size, alloc_size);
void* first_alloc = reinterpret_cast<char*>(new_arena) + base_size; void* first_alloc = reinterpret_cast<char*>(new_arena) + base_size;
@ -88,7 +88,7 @@ void* Arena::AllocZone(size_t size) {
// sizing hysteresis (that is, most calls should have a large enough initial // sizing hysteresis (that is, most calls should have a large enough initial
// zone and will not need to grow the arena). // zone and will not need to grow the arena).
static constexpr size_t zone_base_size = static constexpr size_t zone_base_size =
GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE(sizeof(Zone)); GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(Zone));
size_t alloc_size = zone_base_size + size; size_t alloc_size = zone_base_size + size;
Zone* z = new (gpr_malloc_aligned(alloc_size, GPR_MAX_ALIGNMENT)) Zone(); Zone* z = new (gpr_malloc_aligned(alloc_size, GPR_MAX_ALIGNMENT)) Zone();
{ {

@ -58,10 +58,10 @@ class Arena {
// Allocate \a size bytes from the arena. // Allocate \a size bytes from the arena.
void* Alloc(size_t size) { void* Alloc(size_t size) {
static constexpr size_t base_size = static constexpr size_t base_size =
GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE(sizeof(Arena)); GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(Arena));
size = GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE(size); size = GPR_ROUND_UP_TO_ALIGNMENT_SIZE(size);
size_t begin = total_used_.FetchAdd(size, MemoryOrder::RELAXED); size_t begin = total_used_.FetchAdd(size, MemoryOrder::RELAXED);
if (GPR_LIKELY(begin + size <= initial_zone_size_)) { if (begin + size <= initial_zone_size_) {
return reinterpret_cast<char*>(this) + base_size + begin; return reinterpret_cast<char*>(this) + base_size + begin;
} else { } else {
return AllocZone(size); return AllocZone(size);

@ -110,12 +110,12 @@ class InternallyRefCounted : public Orphanable {
} }
void Unref() { void Unref() {
if (refs_.Unref()) { if (GPR_UNLIKELY(refs_.Unref())) {
Delete(static_cast<Child*>(this)); Delete(static_cast<Child*>(this));
} }
} }
void Unref(const DebugLocation& location, const char* reason) { void Unref(const DebugLocation& location, const char* reason) {
if (refs_.Unref(location, reason)) { if (GPR_UNLIKELY(refs_.Unref(location, reason))) {
Delete(static_cast<Child*>(this)); Delete(static_cast<Child*>(this));
} }
} }

@ -211,12 +211,12 @@ class RefCounted : public Impl {
// private, since it will only be used by RefCountedPtr<>, which is a // private, since it will only be used by RefCountedPtr<>, which is a
// friend of this class. // friend of this class.
void Unref() { void Unref() {
if (refs_.Unref()) { if (GPR_UNLIKELY(refs_.Unref())) {
Delete(static_cast<Child*>(this)); Delete(static_cast<Child*>(this));
} }
} }
void Unref(const DebugLocation& location, const char* reason) { void Unref(const DebugLocation& location, const char* reason) {
if (refs_.Unref(location, reason)) { if (GPR_UNLIKELY(refs_.Unref(location, reason))) {
Delete(static_cast<Child*>(this)); Delete(static_cast<Child*>(this));
} }
} }

@ -383,6 +383,13 @@ static void fd_shutdown_internal(grpc_fd* fd, grpc_error* why,
if (fd->read_closure->SetShutdown(GRPC_ERROR_REF(why))) { if (fd->read_closure->SetShutdown(GRPC_ERROR_REF(why))) {
if (!releasing_fd) { if (!releasing_fd) {
shutdown(fd->fd, SHUT_RDWR); shutdown(fd->fd, SHUT_RDWR);
} else {
/* we need a dummy event for earlier linux versions. */
epoll_event dummy_event;
if (epoll_ctl(g_epoll_set.epfd, EPOLL_CTL_DEL, fd->fd, &dummy_event) !=
0) {
gpr_log(GPR_ERROR, "epoll_ctl failed: %s", strerror(errno));
}
} }
fd->write_closure->SetShutdown(GRPC_ERROR_REF(why)); fd->write_closure->SetShutdown(GRPC_ERROR_REF(why));
fd->error_closure->SetShutdown(GRPC_ERROR_REF(why)); fd->error_closure->SetShutdown(GRPC_ERROR_REF(why));

@ -33,7 +33,7 @@
#define GROW(x) (3 * (x) / 2) #define GROW(x) (3 * (x) / 2)
static void maybe_embiggen(grpc_slice_buffer* sb) { static void maybe_embiggen(grpc_slice_buffer* sb) {
if (sb->length == 0) { if (sb->count == 0) {
sb->slices = sb->base_slices; sb->slices = sb->base_slices;
} }

@ -222,7 +222,7 @@ inline uint32_t grpc_slice_refcount::Hash(const grpc_slice& slice) {
g_hash_seed); g_hash_seed);
} }
inline grpc_slice grpc_slice_ref_internal(const grpc_slice& slice) { inline const grpc_slice& grpc_slice_ref_internal(const grpc_slice& slice) {
if (slice.refcount) { if (slice.refcount) {
slice.refcount->Ref(); slice.refcount->Ref();
} }

@ -260,10 +260,10 @@ grpc_core::TraceFlag grpc_compression_trace(false, "compression");
#define CALL_STACK_FROM_CALL(call) \ #define CALL_STACK_FROM_CALL(call) \
(grpc_call_stack*)((char*)(call) + \ (grpc_call_stack*)((char*)(call) + \
GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE(sizeof(grpc_call))) GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_call)))
#define CALL_FROM_CALL_STACK(call_stack) \ #define CALL_FROM_CALL_STACK(call_stack) \
(grpc_call*)(((char*)(call_stack)) - \ (grpc_call*)(((char*)(call_stack)) - \
GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE(sizeof(grpc_call))) GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_call)))
#define CALL_ELEM_FROM_CALL(call, idx) \ #define CALL_ELEM_FROM_CALL(call, idx) \
grpc_call_stack_element(CALL_STACK_FROM_CALL(call), idx) grpc_call_stack_element(CALL_STACK_FROM_CALL(call), idx)
@ -329,7 +329,7 @@ grpc_error* grpc_call_create(const grpc_call_create_args* args,
size_t initial_size = grpc_channel_get_call_size_estimate(args->channel); size_t initial_size = grpc_channel_get_call_size_estimate(args->channel);
GRPC_STATS_INC_CALL_INITIAL_SIZE(initial_size); GRPC_STATS_INC_CALL_INITIAL_SIZE(initial_size);
size_t call_and_stack_size = size_t call_and_stack_size =
GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE(sizeof(grpc_call)) + GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_call)) +
channel_stack->call_stack_size; channel_stack->call_stack_size;
size_t call_alloc_size = size_t call_alloc_size =
call_and_stack_size + (args->parent ? sizeof(child_call) : 0); call_and_stack_size + (args->parent ? sizeof(child_call) : 0);

@ -466,7 +466,7 @@ void grpc_mdelem_do_unref(grpc_mdelem gmd DEBUG_ARGS) {
case GRPC_MDELEM_STORAGE_INTERNED: { case GRPC_MDELEM_STORAGE_INTERNED: {
auto* md = reinterpret_cast<InternedMetadata*> GRPC_MDELEM_DATA(gmd); auto* md = reinterpret_cast<InternedMetadata*> GRPC_MDELEM_DATA(gmd);
uint32_t hash = md->hash(); uint32_t hash = md->hash();
if (md->Unref(FWD_DEBUG_ARGS)) { if (GPR_UNLIKELY(md->Unref(FWD_DEBUG_ARGS))) {
/* once the refcount hits zero, some other thread can come along and /* once the refcount hits zero, some other thread can come along and
free md at any time: it's unsafe from this point on to access it */ free md at any time: it's unsafe from this point on to access it */
note_disposed_interned_metadata(hash); note_disposed_interned_metadata(hash);
@ -475,7 +475,7 @@ void grpc_mdelem_do_unref(grpc_mdelem gmd DEBUG_ARGS) {
} }
case GRPC_MDELEM_STORAGE_ALLOCATED: { case GRPC_MDELEM_STORAGE_ALLOCATED: {
auto* md = reinterpret_cast<AllocatedMetadata*> GRPC_MDELEM_DATA(gmd); auto* md = reinterpret_cast<AllocatedMetadata*> GRPC_MDELEM_DATA(gmd);
if (md->Unref(FWD_DEBUG_ARGS)) { if (GPR_UNLIKELY(md->Unref(FWD_DEBUG_ARGS))) {
grpc_core::Delete(md); grpc_core::Delete(md);
} }
break; break;

@ -115,7 +115,7 @@ void grpc_transport_move_stats(grpc_transport_stream_stats* from,
} }
size_t grpc_transport_stream_size(grpc_transport* transport) { size_t grpc_transport_stream_size(grpc_transport* transport) {
return GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE(transport->vtable->sizeof_stream); return GPR_ROUND_UP_TO_ALIGNMENT_SIZE(transport->vtable->sizeof_stream);
} }
void grpc_transport_destroy(grpc_transport* transport) { void grpc_transport_destroy(grpc_transport* transport) {

@ -98,7 +98,7 @@ inline void grpc_stream_unref(grpc_stream_refcount* refcount,
#else #else
inline void grpc_stream_unref(grpc_stream_refcount* refcount) { inline void grpc_stream_unref(grpc_stream_refcount* refcount) {
#endif #endif
if (refcount->refs.Unref()) { if (GPR_UNLIKELY(refcount->refs.Unref())) {
grpc_stream_destroy(refcount); grpc_stream_destroy(refcount);
} }
} }

@ -39,7 +39,7 @@ namespace Grpc.Core
/// Also, allocating a new buffer each time can put excessive pressure on GC, especially if /// Also, allocating a new buffer each time can put excessive pressure on GC, especially if
/// the payload is more than 86700 bytes large (which means the newly allocated buffer will be placed in LOH, /// the payload is more than 86700 bytes large (which means the newly allocated buffer will be placed in LOH,
/// and LOH object can only be garbage collected via a full ("stop the world") GC run). /// and LOH object can only be garbage collected via a full ("stop the world") GC run).
/// NOTE: Deserializers are expected not to call this method more than once per received message /// NOTE: Deserializers are expected not to call this method (or other payload accessor methods) more than once per received message
/// (as there is no practical reason for doing so) and <c>DeserializationContext</c> implementations are free to assume so. /// (as there is no practical reason for doing so) and <c>DeserializationContext</c> implementations are free to assume so.
/// </summary> /// </summary>
/// <returns>byte array containing the entire payload.</returns> /// <returns>byte array containing the entire payload.</returns>
@ -47,5 +47,22 @@ namespace Grpc.Core
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
/// <summary>
/// Gets the entire payload as a ReadOnlySequence.
/// The ReadOnlySequence is only valid for the duration of the deserializer routine and the caller must not access it after the deserializer returns.
/// Using the read only sequence is the most efficient way to access the message payload. Where possible it allows directly
/// accessing the received payload without needing to perform any buffer copying or buffer allocations.
/// NOTE: This method is only available in the netstandard2.0 build of the library.
/// NOTE: Deserializers are expected not to call this method (or other payload accessor methods) more than once per received message
/// (as there is no practical reason for doing so) and <c>DeserializationContext</c> implementations are free to assume so.
/// </summary>
/// <returns>read only sequence containing the entire payload.</returns>
public virtual System.Buffers.ReadOnlySequence<byte> PayloadAsReadOnlySequence()
{
throw new NotImplementedException();
}
#endif
} }
} }

@ -19,12 +19,20 @@
<TreatWarningsAsErrors>true</TreatWarningsAsErrors> <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<DefineConstants>$(DefineConstants);GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY</DefineConstants>
</PropertyGroup>
<Import Project="..\Grpc.Core\SourceLink.csproj.include" /> <Import Project="..\Grpc.Core\SourceLink.csproj.include" />
<ItemGroup> <ItemGroup>
<PackageReference Include="System.Interactive.Async" Version="3.2.0" /> <PackageReference Include="System.Interactive.Async" Version="3.2.0" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<PackageReference Include="System.Memory" Version="4.5.2" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' "> <ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />

@ -114,7 +114,8 @@ namespace Grpc.Core
/// <summary> /// <summary>
/// The service is currently unavailable. This is a most likely a /// The service is currently unavailable. This is a most likely a
/// transient condition and may be corrected by retrying with /// transient condition and may be corrected by retrying with
/// a backoff. /// a backoff. Note that it is not always safe to retry
/// non-idempotent operations.
/// </summary> /// </summary>
Unavailable = 14, Unavailable = 14,

@ -8,6 +8,10 @@
<TreatWarningsAsErrors>true</TreatWarningsAsErrors> <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netcoreapp2.1' ">
<DefineConstants>$(DefineConstants);GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY</DefineConstants>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="../Grpc.Core/Grpc.Core.csproj" /> <ProjectReference Include="../Grpc.Core/Grpc.Core.csproj" />
</ItemGroup> </ItemGroup>

@ -35,6 +35,7 @@ namespace Grpc.Core.Internal.Tests
Server server; Server server;
FakeNativeCall fakeCall; FakeNativeCall fakeCall;
AsyncCallServer<string, string> asyncCallServer; AsyncCallServer<string, string> asyncCallServer;
FakeBufferReaderManager fakeBufferReaderManager;
[SetUp] [SetUp]
public void Init() public void Init()
@ -52,11 +53,13 @@ namespace Grpc.Core.Internal.Tests
Marshallers.StringMarshaller.ContextualSerializer, Marshallers.StringMarshaller.ContextualDeserializer, Marshallers.StringMarshaller.ContextualSerializer, Marshallers.StringMarshaller.ContextualDeserializer,
server); server);
asyncCallServer.InitializeForTesting(fakeCall); asyncCallServer.InitializeForTesting(fakeCall);
fakeBufferReaderManager = new FakeBufferReaderManager();
} }
[TearDown] [TearDown]
public void Cleanup() public void Cleanup()
{ {
fakeBufferReaderManager.Dispose();
server.ShutdownAsync().Wait(); server.ShutdownAsync().Wait();
} }
@ -77,7 +80,7 @@ namespace Grpc.Core.Internal.Tests
var moveNextTask = requestStream.MoveNext(); var moveNextTask = requestStream.MoveNext();
fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true); fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true);
fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null); fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
Assert.IsFalse(moveNextTask.Result); Assert.IsFalse(moveNextTask.Result);
AssertFinished(asyncCallServer, fakeCall, finishedTask); AssertFinished(asyncCallServer, fakeCall, finishedTask);
@ -107,7 +110,7 @@ namespace Grpc.Core.Internal.Tests
// if a read completion's success==false, the request stream will silently finish // if a read completion's success==false, the request stream will silently finish
// and we rely on C core cancelling the call. // and we rely on C core cancelling the call.
var moveNextTask = requestStream.MoveNext(); var moveNextTask = requestStream.MoveNext();
fakeCall.ReceivedMessageCallback.OnReceivedMessage(false, null); fakeCall.ReceivedMessageCallback.OnReceivedMessage(false, CreateNullResponse());
Assert.IsFalse(moveNextTask.Result); Assert.IsFalse(moveNextTask.Result);
fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true); fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true);
@ -182,5 +185,10 @@ namespace Grpc.Core.Internal.Tests
Assert.IsTrue(finishedTask.IsCompleted); Assert.IsTrue(finishedTask.IsCompleted);
Assert.DoesNotThrow(() => finishedTask.Wait()); Assert.DoesNotThrow(() => finishedTask.Wait());
} }
IBufferReader CreateNullResponse()
{
return fakeBufferReaderManager.CreateNullPayloadBufferReader();
}
} }
} }

@ -21,6 +21,7 @@ using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Grpc.Core.Internal; using Grpc.Core.Internal;
using Grpc.Core.Utils;
using NUnit.Framework; using NUnit.Framework;
namespace Grpc.Core.Internal.Tests namespace Grpc.Core.Internal.Tests
@ -33,6 +34,7 @@ namespace Grpc.Core.Internal.Tests
Channel channel; Channel channel;
FakeNativeCall fakeCall; FakeNativeCall fakeCall;
AsyncCall<string, string> asyncCall; AsyncCall<string, string> asyncCall;
FakeBufferReaderManager fakeBufferReaderManager;
[SetUp] [SetUp]
public void Init() public void Init()
@ -43,12 +45,14 @@ namespace Grpc.Core.Internal.Tests
var callDetails = new CallInvocationDetails<string, string>(channel, "someMethod", null, Marshallers.StringMarshaller, Marshallers.StringMarshaller, new CallOptions()); var callDetails = new CallInvocationDetails<string, string>(channel, "someMethod", null, Marshallers.StringMarshaller, Marshallers.StringMarshaller, new CallOptions());
asyncCall = new AsyncCall<string, string>(callDetails, fakeCall); asyncCall = new AsyncCall<string, string>(callDetails, fakeCall);
fakeBufferReaderManager = new FakeBufferReaderManager();
} }
[TearDown] [TearDown]
public void Cleanup() public void Cleanup()
{ {
channel.ShutdownAsync().Wait(); channel.ShutdownAsync().Wait();
fakeBufferReaderManager.Dispose();
} }
[Test] [Test]
@ -87,7 +91,7 @@ namespace Grpc.Core.Internal.Tests
var resultTask = asyncCall.UnaryCallAsync("request1"); var resultTask = asyncCall.UnaryCallAsync("request1");
fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true, fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true,
CreateClientSideStatus(StatusCode.InvalidArgument), CreateClientSideStatus(StatusCode.InvalidArgument),
null, CreateNullResponse(),
new Metadata()); new Metadata());
AssertUnaryResponseError(asyncCall, fakeCall, resultTask, StatusCode.InvalidArgument); AssertUnaryResponseError(asyncCall, fakeCall, resultTask, StatusCode.InvalidArgument);
@ -168,7 +172,7 @@ namespace Grpc.Core.Internal.Tests
var resultTask = asyncCall.ClientStreamingCallAsync(); var resultTask = asyncCall.ClientStreamingCallAsync();
fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true, fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true,
CreateClientSideStatus(StatusCode.InvalidArgument), CreateClientSideStatus(StatusCode.InvalidArgument),
null, CreateNullResponse(),
new Metadata()); new Metadata());
AssertUnaryResponseError(asyncCall, fakeCall, resultTask, StatusCode.InvalidArgument); AssertUnaryResponseError(asyncCall, fakeCall, resultTask, StatusCode.InvalidArgument);
@ -214,7 +218,7 @@ namespace Grpc.Core.Internal.Tests
fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true, fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true,
CreateClientSideStatus(StatusCode.Internal), CreateClientSideStatus(StatusCode.Internal),
null, CreateNullResponse(),
new Metadata()); new Metadata());
var ex = Assert.ThrowsAsync<RpcException>(async () => await writeTask); var ex = Assert.ThrowsAsync<RpcException>(async () => await writeTask);
@ -233,7 +237,7 @@ namespace Grpc.Core.Internal.Tests
fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true, fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true,
CreateClientSideStatus(StatusCode.Internal), CreateClientSideStatus(StatusCode.Internal),
null, CreateNullResponse(),
new Metadata()); new Metadata());
fakeCall.SendCompletionCallback.OnSendCompletion(false); fakeCall.SendCompletionCallback.OnSendCompletion(false);
@ -259,7 +263,7 @@ namespace Grpc.Core.Internal.Tests
fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true, fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true,
CreateClientSideStatus(StatusCode.Internal), CreateClientSideStatus(StatusCode.Internal),
null, CreateNullResponse(),
new Metadata()); new Metadata());
var ex = Assert.ThrowsAsync<RpcException>(async () => await writeTask); var ex = Assert.ThrowsAsync<RpcException>(async () => await writeTask);
@ -357,7 +361,7 @@ namespace Grpc.Core.Internal.Tests
fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true, fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true,
CreateClientSideStatus(StatusCode.Cancelled), CreateClientSideStatus(StatusCode.Cancelled),
null, CreateNullResponse(),
new Metadata()); new Metadata());
AssertUnaryResponseError(asyncCall, fakeCall, resultTask, StatusCode.Cancelled); AssertUnaryResponseError(asyncCall, fakeCall, resultTask, StatusCode.Cancelled);
@ -390,7 +394,7 @@ namespace Grpc.Core.Internal.Tests
fakeCall.ReceivedResponseHeadersCallback.OnReceivedResponseHeaders(true, new Metadata()); fakeCall.ReceivedResponseHeadersCallback.OnReceivedResponseHeaders(true, new Metadata());
Assert.AreEqual(0, asyncCall.ResponseHeadersAsync.Result.Count); Assert.AreEqual(0, asyncCall.ResponseHeadersAsync.Result.Count);
fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null); fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata())); fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata()));
AssertStreamingResponseSuccess(asyncCall, fakeCall, readTask); AssertStreamingResponseSuccess(asyncCall, fakeCall, readTask);
@ -405,7 +409,7 @@ namespace Grpc.Core.Internal.Tests
// try alternative order of completions // try alternative order of completions
fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata())); fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata()));
fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null); fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
AssertStreamingResponseSuccess(asyncCall, fakeCall, readTask); AssertStreamingResponseSuccess(asyncCall, fakeCall, readTask);
} }
@ -417,7 +421,7 @@ namespace Grpc.Core.Internal.Tests
var responseStream = new ClientResponseStream<string, string>(asyncCall); var responseStream = new ClientResponseStream<string, string>(asyncCall);
var readTask = responseStream.MoveNext(); var readTask = responseStream.MoveNext();
fakeCall.ReceivedMessageCallback.OnReceivedMessage(false, null); // after a failed read, we rely on C core to deliver appropriate status code. fakeCall.ReceivedMessageCallback.OnReceivedMessage(false, CreateNullResponse()); // after a failed read, we rely on C core to deliver appropriate status code.
fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, CreateClientSideStatus(StatusCode.Internal)); fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, CreateClientSideStatus(StatusCode.Internal));
AssertStreamingResponseError(asyncCall, fakeCall, readTask, StatusCode.Internal); AssertStreamingResponseError(asyncCall, fakeCall, readTask, StatusCode.Internal);
@ -441,7 +445,7 @@ namespace Grpc.Core.Internal.Tests
var readTask3 = responseStream.MoveNext(); var readTask3 = responseStream.MoveNext();
fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata())); fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata()));
fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null); fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
AssertStreamingResponseSuccess(asyncCall, fakeCall, readTask3); AssertStreamingResponseSuccess(asyncCall, fakeCall, readTask3);
} }
@ -479,7 +483,7 @@ namespace Grpc.Core.Internal.Tests
Assert.DoesNotThrowAsync(async () => await writeTask1); Assert.DoesNotThrowAsync(async () => await writeTask1);
var readTask = responseStream.MoveNext(); var readTask = responseStream.MoveNext();
fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null); fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata())); fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata()));
AssertStreamingResponseSuccess(asyncCall, fakeCall, readTask); AssertStreamingResponseSuccess(asyncCall, fakeCall, readTask);
@ -493,7 +497,7 @@ namespace Grpc.Core.Internal.Tests
var responseStream = new ClientResponseStream<string, string>(asyncCall); var responseStream = new ClientResponseStream<string, string>(asyncCall);
var readTask = responseStream.MoveNext(); var readTask = responseStream.MoveNext();
fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null); fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata())); fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata()));
AssertStreamingResponseSuccess(asyncCall, fakeCall, readTask); AssertStreamingResponseSuccess(asyncCall, fakeCall, readTask);
@ -511,7 +515,7 @@ namespace Grpc.Core.Internal.Tests
var responseStream = new ClientResponseStream<string, string>(asyncCall); var responseStream = new ClientResponseStream<string, string>(asyncCall);
var readTask = responseStream.MoveNext(); var readTask = responseStream.MoveNext();
fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null); fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata())); fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata()));
AssertStreamingResponseSuccess(asyncCall, fakeCall, readTask); AssertStreamingResponseSuccess(asyncCall, fakeCall, readTask);
@ -533,7 +537,7 @@ namespace Grpc.Core.Internal.Tests
Assert.IsFalse(writeTask.IsCompleted); Assert.IsFalse(writeTask.IsCompleted);
var readTask = responseStream.MoveNext(); var readTask = responseStream.MoveNext();
fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null); fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, CreateClientSideStatus(StatusCode.PermissionDenied)); fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, CreateClientSideStatus(StatusCode.PermissionDenied));
var ex = Assert.ThrowsAsync<RpcException>(async () => await writeTask); var ex = Assert.ThrowsAsync<RpcException>(async () => await writeTask);
@ -552,7 +556,7 @@ namespace Grpc.Core.Internal.Tests
var writeTask = requestStream.WriteAsync("request1"); var writeTask = requestStream.WriteAsync("request1");
var readTask = responseStream.MoveNext(); var readTask = responseStream.MoveNext();
fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null); fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, CreateClientSideStatus(StatusCode.PermissionDenied)); fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, CreateClientSideStatus(StatusCode.PermissionDenied));
fakeCall.SendCompletionCallback.OnSendCompletion(false); fakeCall.SendCompletionCallback.OnSendCompletion(false);
@ -576,7 +580,7 @@ namespace Grpc.Core.Internal.Tests
Assert.ThrowsAsync(typeof(TaskCanceledException), async () => await writeTask); Assert.ThrowsAsync(typeof(TaskCanceledException), async () => await writeTask);
var readTask = responseStream.MoveNext(); var readTask = responseStream.MoveNext();
fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null); fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, CreateClientSideStatus(StatusCode.Cancelled)); fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, CreateClientSideStatus(StatusCode.Cancelled));
AssertStreamingResponseError(asyncCall, fakeCall, readTask, StatusCode.Cancelled); AssertStreamingResponseError(asyncCall, fakeCall, readTask, StatusCode.Cancelled);
@ -597,7 +601,7 @@ namespace Grpc.Core.Internal.Tests
Assert.AreEqual("response1", responseStream.Current); Assert.AreEqual("response1", responseStream.Current);
var readTask2 = responseStream.MoveNext(); var readTask2 = responseStream.MoveNext();
fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null); fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, CreateClientSideStatus(StatusCode.Cancelled)); fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, CreateClientSideStatus(StatusCode.Cancelled));
AssertStreamingResponseError(asyncCall, fakeCall, readTask2, StatusCode.Cancelled); AssertStreamingResponseError(asyncCall, fakeCall, readTask2, StatusCode.Cancelled);
@ -618,7 +622,7 @@ namespace Grpc.Core.Internal.Tests
Assert.AreEqual("response1", responseStream.Current); Assert.AreEqual("response1", responseStream.Current);
var readTask2 = responseStream.MoveNext(); var readTask2 = responseStream.MoveNext();
fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null); fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, CreateClientSideStatus(StatusCode.Cancelled)); fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, CreateClientSideStatus(StatusCode.Cancelled));
AssertStreamingResponseError(asyncCall, fakeCall, readTask2, StatusCode.Cancelled); AssertStreamingResponseError(asyncCall, fakeCall, readTask2, StatusCode.Cancelled);
@ -638,9 +642,14 @@ namespace Grpc.Core.Internal.Tests
return new ClientSideStatus(new Status(statusCode, ""), new Metadata()); return new ClientSideStatus(new Status(statusCode, ""), new Metadata());
} }
byte[] CreateResponsePayload() IBufferReader CreateResponsePayload()
{
return fakeBufferReaderManager.CreateSingleSegmentBufferReader(Marshallers.StringMarshaller.Serializer("response1"));
}
IBufferReader CreateNullResponse()
{ {
return Marshallers.StringMarshaller.Serializer("response1"); return fakeBufferReaderManager.CreateNullPayloadBufferReader();
} }
static void AssertUnaryResponseSuccess(AsyncCall<string, string> asyncCall, FakeNativeCall fakeCall, Task<string> resultTask) static void AssertUnaryResponseSuccess(AsyncCall<string, string> asyncCall, FakeNativeCall fakeCall, Task<string> resultTask)

@ -0,0 +1,240 @@
#region Copyright notice and license
// Copyright 2019 The 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.Collections.Generic;
using Grpc.Core;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
using NUnit.Framework;
using System.Runtime.InteropServices;
#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
using System.Buffers;
#endif
namespace Grpc.Core.Internal.Tests
{
public class DefaultDeserializationContextTest
{
FakeBufferReaderManager fakeBufferReaderManager;
[SetUp]
public void Init()
{
fakeBufferReaderManager = new FakeBufferReaderManager();
}
[TearDown]
public void Cleanup()
{
fakeBufferReaderManager.Dispose();
}
#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
[TestCase]
public void PayloadAsReadOnlySequence_ZeroSegmentPayload()
{
var context = new DefaultDeserializationContext();
context.Initialize(fakeBufferReaderManager.CreateMultiSegmentBufferReader(new List<byte[]> {}));
Assert.AreEqual(0, context.PayloadLength);
var sequence = context.PayloadAsReadOnlySequence();
Assert.AreEqual(ReadOnlySequence<byte>.Empty, sequence);
Assert.IsTrue(sequence.IsEmpty);
Assert.IsTrue(sequence.IsSingleSegment);
}
[TestCase(0)]
[TestCase(1)]
[TestCase(10)]
[TestCase(100)]
[TestCase(1000)]
public void PayloadAsReadOnlySequence_SingleSegmentPayload(int segmentLength)
{
var origBuffer = GetTestBuffer(segmentLength);
var context = new DefaultDeserializationContext();
context.Initialize(fakeBufferReaderManager.CreateSingleSegmentBufferReader(origBuffer));
Assert.AreEqual(origBuffer.Length, context.PayloadLength);
var sequence = context.PayloadAsReadOnlySequence();
Assert.AreEqual(origBuffer.Length, sequence.Length);
Assert.AreEqual(origBuffer.Length, sequence.First.Length);
Assert.IsTrue(sequence.IsSingleSegment);
CollectionAssert.AreEqual(origBuffer, sequence.First.ToArray());
}
[TestCase(0, 5, 10)]
[TestCase(1, 1, 1)]
[TestCase(10, 100, 1000)]
[TestCase(100, 100, 10)]
[TestCase(1000, 1000, 1000)]
public void PayloadAsReadOnlySequence_MultiSegmentPayload(int segmentLen1, int segmentLen2, int segmentLen3)
{
var origBuffer1 = GetTestBuffer(segmentLen1);
var origBuffer2 = GetTestBuffer(segmentLen2);
var origBuffer3 = GetTestBuffer(segmentLen3);
int totalLen = origBuffer1.Length + origBuffer2.Length + origBuffer3.Length;
var context = new DefaultDeserializationContext();
context.Initialize(fakeBufferReaderManager.CreateMultiSegmentBufferReader(new List<byte[]> { origBuffer1, origBuffer2, origBuffer3 }));
Assert.AreEqual(totalLen, context.PayloadLength);
var sequence = context.PayloadAsReadOnlySequence();
Assert.AreEqual(totalLen, sequence.Length);
var segmentEnumerator = sequence.GetEnumerator();
Assert.IsTrue(segmentEnumerator.MoveNext());
CollectionAssert.AreEqual(origBuffer1, segmentEnumerator.Current.ToArray());
Assert.IsTrue(segmentEnumerator.MoveNext());
CollectionAssert.AreEqual(origBuffer2, segmentEnumerator.Current.ToArray());
Assert.IsTrue(segmentEnumerator.MoveNext());
CollectionAssert.AreEqual(origBuffer3, segmentEnumerator.Current.ToArray());
Assert.IsFalse(segmentEnumerator.MoveNext());
}
#endif
[TestCase]
public void NullPayloadNotAllowed()
{
var context = new DefaultDeserializationContext();
Assert.Throws(typeof(InvalidOperationException), () => context.Initialize(fakeBufferReaderManager.CreateNullPayloadBufferReader()));
}
[TestCase]
public void PayloadAsNewByteBuffer_ZeroSegmentPayload()
{
var context = new DefaultDeserializationContext();
context.Initialize(fakeBufferReaderManager.CreateMultiSegmentBufferReader(new List<byte[]> {}));
Assert.AreEqual(0, context.PayloadLength);
var payload = context.PayloadAsNewBuffer();
Assert.AreEqual(0, payload.Length);
}
[TestCase(0)]
[TestCase(1)]
[TestCase(10)]
[TestCase(100)]
[TestCase(1000)]
public void PayloadAsNewByteBuffer_SingleSegmentPayload(int segmentLength)
{
var origBuffer = GetTestBuffer(segmentLength);
var context = new DefaultDeserializationContext();
context.Initialize(fakeBufferReaderManager.CreateSingleSegmentBufferReader(origBuffer));
Assert.AreEqual(origBuffer.Length, context.PayloadLength);
var payload = context.PayloadAsNewBuffer();
CollectionAssert.AreEqual(origBuffer, payload);
}
[TestCase(0, 5, 10)]
[TestCase(1, 1, 1)]
[TestCase(10, 100, 1000)]
[TestCase(100, 100, 10)]
[TestCase(1000, 1000, 1000)]
public void PayloadAsNewByteBuffer_MultiSegmentPayload(int segmentLen1, int segmentLen2, int segmentLen3)
{
var origBuffer1 = GetTestBuffer(segmentLen1);
var origBuffer2 = GetTestBuffer(segmentLen2);
var origBuffer3 = GetTestBuffer(segmentLen3);
var context = new DefaultDeserializationContext();
context.Initialize(fakeBufferReaderManager.CreateMultiSegmentBufferReader(new List<byte[]> { origBuffer1, origBuffer2, origBuffer3 }));
var payload = context.PayloadAsNewBuffer();
var concatenatedOrigBuffers = new List<byte>();
concatenatedOrigBuffers.AddRange(origBuffer1);
concatenatedOrigBuffers.AddRange(origBuffer2);
concatenatedOrigBuffers.AddRange(origBuffer3);
Assert.AreEqual(concatenatedOrigBuffers.Count, context.PayloadLength);
Assert.AreEqual(concatenatedOrigBuffers.Count, payload.Length);
CollectionAssert.AreEqual(concatenatedOrigBuffers, payload);
}
[TestCase]
public void GetPayloadMultipleTimesIsIllegal()
{
var origBuffer = GetTestBuffer(100);
var context = new DefaultDeserializationContext();
context.Initialize(fakeBufferReaderManager.CreateSingleSegmentBufferReader(origBuffer));
Assert.AreEqual(origBuffer.Length, context.PayloadLength);
var payload = context.PayloadAsNewBuffer();
CollectionAssert.AreEqual(origBuffer, payload);
// Getting payload multiple times is illegal
Assert.Throws(typeof(InvalidOperationException), () => context.PayloadAsNewBuffer());
#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
Assert.Throws(typeof(InvalidOperationException), () => context.PayloadAsReadOnlySequence());
#endif
}
[TestCase]
public void ResetContextAndReinitialize()
{
var origBuffer = GetTestBuffer(100);
var context = new DefaultDeserializationContext();
context.Initialize(fakeBufferReaderManager.CreateSingleSegmentBufferReader(origBuffer));
Assert.AreEqual(origBuffer.Length, context.PayloadLength);
// Reset invalidates context
context.Reset();
Assert.AreEqual(0, context.PayloadLength);
Assert.Throws(typeof(NullReferenceException), () => context.PayloadAsNewBuffer());
#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
Assert.Throws(typeof(NullReferenceException), () => context.PayloadAsReadOnlySequence());
#endif
// Previously reset context can be initialized again
var origBuffer2 = GetTestBuffer(50);
context.Initialize(fakeBufferReaderManager.CreateSingleSegmentBufferReader(origBuffer2));
Assert.AreEqual(origBuffer2.Length, context.PayloadLength);
CollectionAssert.AreEqual(origBuffer2, context.PayloadAsNewBuffer());
}
private byte[] GetTestBuffer(int length)
{
var testBuffer = new byte[length];
for (int i = 0; i < testBuffer.Length; i++)
{
testBuffer[i] = (byte) i;
}
return testBuffer;
}
}
}

@ -0,0 +1,118 @@
#region Copyright notice and license
// Copyright 2018 The 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.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
namespace Grpc.Core.Internal.Tests
{
// Creates instances of fake IBufferReader. All created instances will become invalid once Dispose is called.
internal class FakeBufferReaderManager : IDisposable
{
List<GCHandle> pinnedHandles = new List<GCHandle>();
bool disposed = false;
public IBufferReader CreateSingleSegmentBufferReader(byte[] data)
{
return CreateMultiSegmentBufferReader(new List<byte[]> { data });
}
public IBufferReader CreateMultiSegmentBufferReader(IEnumerable<byte[]> dataSegments)
{
GrpcPreconditions.CheckState(!disposed);
GrpcPreconditions.CheckNotNull(dataSegments);
var segments = new List<GCHandle>();
foreach (var data in dataSegments)
{
GrpcPreconditions.CheckNotNull(data);
segments.Add(GCHandle.Alloc(data, GCHandleType.Pinned));
}
pinnedHandles.AddRange(segments); // all the allocated GCHandles will be freed on Dispose()
return new FakeBufferReader(segments);
}
public IBufferReader CreateNullPayloadBufferReader()
{
GrpcPreconditions.CheckState(!disposed);
return new FakeBufferReader(null);
}
public void Dispose()
{
if (!disposed)
{
disposed = true;
for (int i = 0; i < pinnedHandles.Count; i++)
{
pinnedHandles[i].Free();
}
}
}
private class FakeBufferReader : IBufferReader
{
readonly List<GCHandle> bufferSegments;
readonly int? totalLength;
readonly IEnumerator<GCHandle> segmentEnumerator;
public FakeBufferReader(List<GCHandle> bufferSegments)
{
this.bufferSegments = bufferSegments;
this.totalLength = ComputeTotalLength(bufferSegments);
this.segmentEnumerator = bufferSegments?.GetEnumerator();
}
public int? TotalLength => totalLength;
public bool TryGetNextSlice(out Slice slice)
{
GrpcPreconditions.CheckNotNull(bufferSegments);
if (!segmentEnumerator.MoveNext())
{
slice = default(Slice);
return false;
}
var segment = segmentEnumerator.Current;
int sliceLen = ((byte[]) segment.Target).Length;
slice = new Slice(segment.AddrOfPinnedObject(), sliceLen);
return true;
}
static int? ComputeTotalLength(List<GCHandle> bufferSegments)
{
if (bufferSegments == null)
{
return null;
}
int sum = 0;
foreach (var segment in bufferSegments)
{
var data = (byte[]) segment.Target;
sum += data.Length;
}
return sum;
}
}
}
}

@ -0,0 +1,121 @@
#region Copyright notice and license
// Copyright 2018 The 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.Collections.Generic;
using Grpc.Core;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
using NUnit.Framework;
namespace Grpc.Core.Internal.Tests
{
public class FakeBufferReaderManagerTest
{
FakeBufferReaderManager fakeBufferReaderManager;
[SetUp]
public void Init()
{
fakeBufferReaderManager = new FakeBufferReaderManager();
}
[TearDown]
public void Cleanup()
{
fakeBufferReaderManager.Dispose();
}
[TestCase]
public void NullPayload()
{
var fakeBufferReader = fakeBufferReaderManager.CreateNullPayloadBufferReader();
Assert.IsFalse(fakeBufferReader.TotalLength.HasValue);
Assert.Throws(typeof(ArgumentNullException), () => fakeBufferReader.TryGetNextSlice(out Slice slice));
}
[TestCase]
public void ZeroSegmentPayload()
{
var fakeBufferReader = fakeBufferReaderManager.CreateMultiSegmentBufferReader(new List<byte[]> {});
Assert.AreEqual(0, fakeBufferReader.TotalLength.Value);
Assert.IsFalse(fakeBufferReader.TryGetNextSlice(out Slice slice));
}
[TestCase(0)]
[TestCase(1)]
[TestCase(10)]
[TestCase(30)]
[TestCase(100)]
[TestCase(1000)]
public void SingleSegmentPayload(int bufferLen)
{
var origBuffer = GetTestBuffer(bufferLen);
var fakeBufferReader = fakeBufferReaderManager.CreateSingleSegmentBufferReader(origBuffer);
Assert.AreEqual(origBuffer.Length, fakeBufferReader.TotalLength.Value);
Assert.IsTrue(fakeBufferReader.TryGetNextSlice(out Slice slice));
AssertSliceDataEqual(origBuffer, slice);
Assert.IsFalse(fakeBufferReader.TryGetNextSlice(out Slice slice2));
}
[TestCase(0, 5, 10)]
[TestCase(1, 1, 1)]
[TestCase(10, 100, 1000)]
[TestCase(100, 100, 10)]
[TestCase(1000, 1000, 1000)]
public void MultiSegmentPayload(int segmentLen1, int segmentLen2, int segmentLen3)
{
var origBuffer1 = GetTestBuffer(segmentLen1);
var origBuffer2 = GetTestBuffer(segmentLen2);
var origBuffer3 = GetTestBuffer(segmentLen3);
var fakeBufferReader = fakeBufferReaderManager.CreateMultiSegmentBufferReader(new List<byte[]> { origBuffer1, origBuffer2, origBuffer3 });
Assert.AreEqual(origBuffer1.Length + origBuffer2.Length + origBuffer3.Length, fakeBufferReader.TotalLength.Value);
Assert.IsTrue(fakeBufferReader.TryGetNextSlice(out Slice slice1));
AssertSliceDataEqual(origBuffer1, slice1);
Assert.IsTrue(fakeBufferReader.TryGetNextSlice(out Slice slice2));
AssertSliceDataEqual(origBuffer2, slice2);
Assert.IsTrue(fakeBufferReader.TryGetNextSlice(out Slice slice3));
AssertSliceDataEqual(origBuffer3, slice3);
Assert.IsFalse(fakeBufferReader.TryGetNextSlice(out Slice slice4));
}
private void AssertSliceDataEqual(byte[] expected, Slice actual)
{
var actualSliceData = new byte[actual.Length];
actual.CopyTo(new ArraySegment<byte>(actualSliceData));
CollectionAssert.AreEqual(expected, actualSliceData);
}
// create a buffer of given size and fill it with some data
private byte[] GetTestBuffer(int length)
{
var testBuffer = new byte[length];
for (int i = 0; i < testBuffer.Length; i++)
{
testBuffer[i] = (byte) i;
}
return testBuffer;
}
}
}

@ -0,0 +1,151 @@
#region Copyright notice and license
// Copyright 2018 The 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.Collections.Generic;
using System.Linq;
using Grpc.Core;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
using NUnit.Framework;
using System.Runtime.InteropServices;
#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
using System.Buffers;
#endif
namespace Grpc.Core.Internal.Tests
{
// Converts IBufferReader into instances of ReadOnlySequence<byte>
// Objects representing the sequence segments are cached to decrease GC load.
public class ReusableSliceBufferTest
{
FakeBufferReaderManager fakeBufferReaderManager;
[SetUp]
public void Init()
{
fakeBufferReaderManager = new FakeBufferReaderManager();
}
[TearDown]
public void Cleanup()
{
fakeBufferReaderManager.Dispose();
}
#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
[TestCase]
public void NullPayload()
{
var sliceBuffer = new ReusableSliceBuffer();
Assert.Throws(typeof(ArgumentNullException), () => sliceBuffer.PopulateFrom(fakeBufferReaderManager.CreateNullPayloadBufferReader()));
}
[TestCase]
public void ZeroSegmentPayload()
{
var sliceBuffer = new ReusableSliceBuffer();
var sequence = sliceBuffer.PopulateFrom(fakeBufferReaderManager.CreateMultiSegmentBufferReader(new List<byte[]> {}));
Assert.AreEqual(ReadOnlySequence<byte>.Empty, sequence);
Assert.IsTrue(sequence.IsEmpty);
Assert.IsTrue(sequence.IsSingleSegment);
}
[TestCase]
public void SegmentsAreCached()
{
var bufferSegments1 = Enumerable.Range(0, 100).Select((_) => GetTestBuffer(50)).ToList();
var bufferSegments2 = Enumerable.Range(0, 100).Select((_) => GetTestBuffer(50)).ToList();
var sliceBuffer = new ReusableSliceBuffer();
var sequence1 = sliceBuffer.PopulateFrom(fakeBufferReaderManager.CreateMultiSegmentBufferReader(bufferSegments1));
var memoryManagers1 = GetMemoryManagersForSequenceSegments(sequence1);
sliceBuffer.Invalidate();
var sequence2 = sliceBuffer.PopulateFrom(fakeBufferReaderManager.CreateMultiSegmentBufferReader(bufferSegments2));
var memoryManagers2 = GetMemoryManagersForSequenceSegments(sequence2);
// check memory managers are identical objects (i.e. they've been reused)
CollectionAssert.AreEquivalent(memoryManagers1, memoryManagers2);
}
[TestCase]
public void MultiSegmentPayload_LotsOfSegments()
{
var bufferSegments = Enumerable.Range(0, ReusableSliceBuffer.MaxCachedSegments + 100).Select((_) => GetTestBuffer(10)).ToList();
var sliceBuffer = new ReusableSliceBuffer();
var sequence = sliceBuffer.PopulateFrom(fakeBufferReaderManager.CreateMultiSegmentBufferReader(bufferSegments));
int index = 0;
foreach (var memory in sequence)
{
CollectionAssert.AreEqual(bufferSegments[index], memory.ToArray());
index ++;
}
}
[TestCase]
public void InvalidateMakesSequenceUnusable()
{
var origBuffer = GetTestBuffer(100);
var sliceBuffer = new ReusableSliceBuffer();
var sequence = sliceBuffer.PopulateFrom(fakeBufferReaderManager.CreateMultiSegmentBufferReader(new List<byte[]> { origBuffer }));
Assert.AreEqual(origBuffer.Length, sequence.Length);
sliceBuffer.Invalidate();
// Invalidate with make the returned sequence completely unusable and broken, users must not use it beyond the deserializer functions.
Assert.Throws(typeof(ArgumentOutOfRangeException), () => { var first = sequence.First; });
}
private List<MemoryManager<byte>> GetMemoryManagersForSequenceSegments(ReadOnlySequence<byte> sequence)
{
var result = new List<MemoryManager<byte>>();
foreach (var memory in sequence)
{
Assert.IsTrue(MemoryMarshal.TryGetMemoryManager(memory, out MemoryManager<byte> memoryManager));
result.Add(memoryManager);
}
return result;
}
#else
[TestCase]
public void OnlySupportedOnNetCore()
{
// Test case needs to exist to make C# sanity test happy.
}
#endif
private byte[] GetTestBuffer(int length)
{
var testBuffer = new byte[length];
for (int i = 0; i < testBuffer.Length; i++)
{
testBuffer[i] = (byte) i;
}
return testBuffer;
}
}
}

@ -0,0 +1,83 @@
#region Copyright notice and license
// Copyright 2018 The 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;
using Grpc.Core.Utils;
using NUnit.Framework;
using System.Runtime.InteropServices;
namespace Grpc.Core.Internal.Tests
{
public class SliceTest
{
[TestCase(0)]
[TestCase(1)]
[TestCase(10)]
[TestCase(100)]
[TestCase(1000)]
public void SliceFromNativePtr_CopyToArraySegment(int bufferLength)
{
var origBuffer = GetTestBuffer(bufferLength);
var gcHandle = GCHandle.Alloc(origBuffer, GCHandleType.Pinned);
try
{
var slice = new Slice(gcHandle.AddrOfPinnedObject(), origBuffer.Length);
Assert.AreEqual(bufferLength, slice.Length);
var newBuffer = new byte[bufferLength];
slice.CopyTo(new ArraySegment<byte>(newBuffer));
CollectionAssert.AreEqual(origBuffer, newBuffer);
}
finally
{
gcHandle.Free();
}
}
[TestCase]
public void SliceFromNativePtr_CopyToArraySegmentTooSmall()
{
var origBuffer = GetTestBuffer(100);
var gcHandle = GCHandle.Alloc(origBuffer, GCHandleType.Pinned);
try
{
var slice = new Slice(gcHandle.AddrOfPinnedObject(), origBuffer.Length);
var tooSmall = new byte[origBuffer.Length - 1];
Assert.Catch(typeof(ArgumentException), () => slice.CopyTo(new ArraySegment<byte>(tooSmall)));
}
finally
{
gcHandle.Free();
}
}
// create a buffer of given size and fill it with some data
private byte[] GetTestBuffer(int length)
{
var testBuffer = new byte[length];
for (int i = 0; i < testBuffer.Length; i++)
{
testBuffer[i] = (byte) i;
}
return testBuffer;
}
}
}

@ -19,6 +19,15 @@
<TreatWarningsAsErrors>true</TreatWarningsAsErrors> <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup> </PropertyGroup>
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<LangVersion>7.2</LangVersion>
<DefineConstants>$(DefineConstants);GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY</DefineConstants>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\Grpc.Core.Api\Version.cs" /> <Compile Include="..\Grpc.Core.Api\Version.cs" />
</ItemGroup> </ItemGroup>

@ -111,7 +111,7 @@ namespace Grpc.Core.Internal
{ {
using (profiler.NewScope("AsyncCall.UnaryCall.HandleBatch")) using (profiler.NewScope("AsyncCall.UnaryCall.HandleBatch"))
{ {
HandleUnaryResponse(success, ctx.GetReceivedStatusOnClient(), ctx.GetReceivedMessage(), ctx.GetReceivedInitialMetadata()); HandleUnaryResponse(success, ctx.GetReceivedStatusOnClient(), ctx.GetReceivedMessageReader(), ctx.GetReceivedInitialMetadata());
} }
} }
catch (Exception e) catch (Exception e)
@ -537,14 +537,14 @@ namespace Grpc.Core.Internal
/// <summary> /// <summary>
/// Handler for unary response completion. /// Handler for unary response completion.
/// </summary> /// </summary>
private void HandleUnaryResponse(bool success, ClientSideStatus receivedStatus, byte[] receivedMessage, Metadata responseHeaders) private void HandleUnaryResponse(bool success, ClientSideStatus receivedStatus, IBufferReader receivedMessageReader, Metadata responseHeaders)
{ {
// NOTE: because this event is a result of batch containing GRPC_OP_RECV_STATUS_ON_CLIENT, // NOTE: because this event is a result of batch containing GRPC_OP_RECV_STATUS_ON_CLIENT,
// success will be always set to true. // success will be always set to true.
TaskCompletionSource<object> delayedStreamingWriteTcs = null; TaskCompletionSource<object> delayedStreamingWriteTcs = null;
TResponse msg = default(TResponse); TResponse msg = default(TResponse);
var deserializeException = TryDeserialize(receivedMessage, out msg); var deserializeException = TryDeserialize(receivedMessageReader, out msg);
bool releasedResources; bool releasedResources;
lock (myLock) lock (myLock)
@ -634,9 +634,9 @@ namespace Grpc.Core.Internal
IUnaryResponseClientCallback UnaryResponseClientCallback => this; IUnaryResponseClientCallback UnaryResponseClientCallback => this;
void IUnaryResponseClientCallback.OnUnaryResponseClient(bool success, ClientSideStatus receivedStatus, byte[] receivedMessage, Metadata responseHeaders) void IUnaryResponseClientCallback.OnUnaryResponseClient(bool success, ClientSideStatus receivedStatus, IBufferReader receivedMessageReader, Metadata responseHeaders)
{ {
HandleUnaryResponse(success, receivedStatus, receivedMessage, responseHeaders); HandleUnaryResponse(success, receivedStatus, receivedMessageReader, responseHeaders);
} }
IReceivedStatusOnClientCallback ReceivedStatusOnClientCallback => this; IReceivedStatusOnClientCallback ReceivedStatusOnClientCallback => this;

@ -228,12 +228,12 @@ namespace Grpc.Core.Internal
} }
} }
protected Exception TryDeserialize(byte[] payload, out TRead msg) protected Exception TryDeserialize(IBufferReader reader, out TRead msg)
{ {
DefaultDeserializationContext context = null; DefaultDeserializationContext context = null;
try try
{ {
context = DefaultDeserializationContext.GetInitializedThreadLocal(payload); context = DefaultDeserializationContext.GetInitializedThreadLocal(reader);
msg = deserializer(context); msg = deserializer(context);
return null; return null;
} }
@ -245,7 +245,6 @@ namespace Grpc.Core.Internal
finally finally
{ {
context?.Reset(); context?.Reset();
} }
} }
@ -333,21 +332,21 @@ namespace Grpc.Core.Internal
/// <summary> /// <summary>
/// Handles streaming read completion. /// Handles streaming read completion.
/// </summary> /// </summary>
protected void HandleReadFinished(bool success, byte[] receivedMessage) protected void HandleReadFinished(bool success, IBufferReader receivedMessageReader)
{ {
// if success == false, received message will be null. It that case we will // if success == false, the message reader will report null payload. It that case we will
// treat this completion as the last read an rely on C core to handle the failed // treat this completion as the last read an rely on C core to handle the failed
// read (e.g. deliver approriate statusCode on the clientside). // read (e.g. deliver approriate statusCode on the clientside).
TRead msg = default(TRead); TRead msg = default(TRead);
var deserializeException = (success && receivedMessage != null) ? TryDeserialize(receivedMessage, out msg) : null; var deserializeException = (success && receivedMessageReader.TotalLength.HasValue) ? TryDeserialize(receivedMessageReader, out msg) : null;
TaskCompletionSource<TRead> origTcs = null; TaskCompletionSource<TRead> origTcs = null;
bool releasedResources; bool releasedResources;
lock (myLock) lock (myLock)
{ {
origTcs = streamingReadTcs; origTcs = streamingReadTcs;
if (receivedMessage == null) if (!receivedMessageReader.TotalLength.HasValue)
{ {
// This was the last read. // This was the last read.
readingDone = true; readingDone = true;
@ -391,9 +390,9 @@ namespace Grpc.Core.Internal
IReceivedMessageCallback ReceivedMessageCallback => this; IReceivedMessageCallback ReceivedMessageCallback => this;
void IReceivedMessageCallback.OnReceivedMessage(bool success, byte[] receivedMessage) void IReceivedMessageCallback.OnReceivedMessage(bool success, IBufferReader receivedMessageReader)
{ {
HandleReadFinished(success, receivedMessage); HandleReadFinished(success, receivedMessageReader);
} }
} }
} }

@ -30,10 +30,17 @@ namespace Grpc.Core.Internal
void OnComplete(bool success); void OnComplete(bool success);
} }
internal interface IBufferReader
{
int? TotalLength { get; }
bool TryGetNextSlice(out Slice slice);
}
/// <summary> /// <summary>
/// grpcsharp_batch_context /// grpcsharp_batch_context
/// </summary> /// </summary>
internal class BatchContextSafeHandle : SafeHandleZeroIsInvalid, IOpCompletionCallback, IPooledObject<BatchContextSafeHandle> internal class BatchContextSafeHandle : SafeHandleZeroIsInvalid, IOpCompletionCallback, IPooledObject<BatchContextSafeHandle>, IBufferReader
{ {
static readonly NativeMethods Native = NativeMethods.Get(); static readonly NativeMethods Native = NativeMethods.Get();
static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<BatchContextSafeHandle>(); static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<BatchContextSafeHandle>();
@ -93,17 +100,9 @@ namespace Grpc.Core.Internal
return new ClientSideStatus(status, metadata); return new ClientSideStatus(status, metadata);
} }
// Gets data of recv_message completion. public IBufferReader GetReceivedMessageReader()
public byte[] GetReceivedMessage()
{
IntPtr len = Native.grpcsharp_batch_context_recv_message_length(this);
if (len == new IntPtr(-1))
{ {
return null; return this;
}
byte[] data = new byte[(int)len];
Native.grpcsharp_batch_context_recv_message_to_buffer(this, data, new UIntPtr((ulong)data.Length));
return data;
} }
// Gets data of receive_close_on_server completion. // Gets data of receive_close_on_server completion.
@ -153,6 +152,29 @@ namespace Grpc.Core.Internal
} }
} }
int? IBufferReader.TotalLength
{
get
{
var len = Native.grpcsharp_batch_context_recv_message_length(this);
return len != new IntPtr(-1) ? (int?) len : null;
}
}
bool IBufferReader.TryGetNextSlice(out Slice slice)
{
UIntPtr sliceLen;
IntPtr sliceDataPtr;
if (0 == Native.grpcsharp_batch_context_recv_message_next_slice_peek(this, out sliceLen, out sliceDataPtr))
{
slice = default(Slice);
return false;
}
slice = new Slice(sliceDataPtr, (int) sliceLen);
return true;
}
struct CompletionCallbackData struct CompletionCallbackData
{ {
public CompletionCallbackData(BatchCompletionDelegate callback, object state) public CompletionCallbackData(BatchCompletionDelegate callback, object state)

@ -35,11 +35,11 @@ namespace Grpc.Core.Internal
// Completion handlers are pre-allocated to avoid unneccessary delegate allocations. // Completion handlers are pre-allocated to avoid unneccessary delegate allocations.
// The "state" field is used to store the actual callback to invoke. // The "state" field is used to store the actual callback to invoke.
static readonly BatchCompletionDelegate CompletionHandler_IUnaryResponseClientCallback = static readonly BatchCompletionDelegate CompletionHandler_IUnaryResponseClientCallback =
(success, context, state) => ((IUnaryResponseClientCallback)state).OnUnaryResponseClient(success, context.GetReceivedStatusOnClient(), context.GetReceivedMessage(), context.GetReceivedInitialMetadata()); (success, context, state) => ((IUnaryResponseClientCallback)state).OnUnaryResponseClient(success, context.GetReceivedStatusOnClient(), context.GetReceivedMessageReader(), context.GetReceivedInitialMetadata());
static readonly BatchCompletionDelegate CompletionHandler_IReceivedStatusOnClientCallback = static readonly BatchCompletionDelegate CompletionHandler_IReceivedStatusOnClientCallback =
(success, context, state) => ((IReceivedStatusOnClientCallback)state).OnReceivedStatusOnClient(success, context.GetReceivedStatusOnClient()); (success, context, state) => ((IReceivedStatusOnClientCallback)state).OnReceivedStatusOnClient(success, context.GetReceivedStatusOnClient());
static readonly BatchCompletionDelegate CompletionHandler_IReceivedMessageCallback = static readonly BatchCompletionDelegate CompletionHandler_IReceivedMessageCallback =
(success, context, state) => ((IReceivedMessageCallback)state).OnReceivedMessage(success, context.GetReceivedMessage()); (success, context, state) => ((IReceivedMessageCallback)state).OnReceivedMessage(success, context.GetReceivedMessageReader());
static readonly BatchCompletionDelegate CompletionHandler_IReceivedResponseHeadersCallback = static readonly BatchCompletionDelegate CompletionHandler_IReceivedResponseHeadersCallback =
(success, context, state) => ((IReceivedResponseHeadersCallback)state).OnReceivedResponseHeaders(success, context.GetReceivedInitialMetadata()); (success, context, state) => ((IReceivedResponseHeadersCallback)state).OnReceivedResponseHeaders(success, context.GetReceivedInitialMetadata());
static readonly BatchCompletionDelegate CompletionHandler_ISendCompletionCallback = static readonly BatchCompletionDelegate CompletionHandler_ISendCompletionCallback =

@ -20,6 +20,10 @@ using Grpc.Core.Utils;
using System; using System;
using System.Threading; using System.Threading;
#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
using System.Buffers;
#endif
namespace Grpc.Core.Internal namespace Grpc.Core.Internal
{ {
internal class DefaultDeserializationContext : DeserializationContext internal class DefaultDeserializationContext : DeserializationContext
@ -27,40 +31,71 @@ namespace Grpc.Core.Internal
static readonly ThreadLocal<DefaultDeserializationContext> threadLocalInstance = static readonly ThreadLocal<DefaultDeserializationContext> threadLocalInstance =
new ThreadLocal<DefaultDeserializationContext>(() => new DefaultDeserializationContext(), false); new ThreadLocal<DefaultDeserializationContext>(() => new DefaultDeserializationContext(), false);
byte[] payload; IBufferReader bufferReader;
bool alreadyCalledPayloadAsNewBuffer; int payloadLength;
#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
ReusableSliceBuffer cachedSliceBuffer = new ReusableSliceBuffer();
#endif
public DefaultDeserializationContext() public DefaultDeserializationContext()
{ {
Reset(); Reset();
} }
public override int PayloadLength => payload.Length; public override int PayloadLength => payloadLength;
public override byte[] PayloadAsNewBuffer() public override byte[] PayloadAsNewBuffer()
{ {
GrpcPreconditions.CheckState(!alreadyCalledPayloadAsNewBuffer); var buffer = new byte[payloadLength];
alreadyCalledPayloadAsNewBuffer = true; FillContinguousBuffer(bufferReader, buffer);
return payload; return buffer;
} }
public void Initialize(byte[] payload) #if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
public override ReadOnlySequence<byte> PayloadAsReadOnlySequence()
{ {
this.payload = GrpcPreconditions.CheckNotNull(payload); var sequence = cachedSliceBuffer.PopulateFrom(bufferReader);
this.alreadyCalledPayloadAsNewBuffer = false; GrpcPreconditions.CheckState(sequence.Length == payloadLength);
return sequence;
}
#endif
public void Initialize(IBufferReader bufferReader)
{
this.bufferReader = GrpcPreconditions.CheckNotNull(bufferReader);
this.payloadLength = bufferReader.TotalLength.Value; // payload must not be null
} }
public void Reset() public void Reset()
{ {
this.payload = null; this.bufferReader = null;
this.alreadyCalledPayloadAsNewBuffer = true; // mark payload as read this.payloadLength = 0;
#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
this.cachedSliceBuffer.Invalidate();
#endif
} }
public static DefaultDeserializationContext GetInitializedThreadLocal(byte[] payload) public static DefaultDeserializationContext GetInitializedThreadLocal(IBufferReader bufferReader)
{ {
var instance = threadLocalInstance.Value; var instance = threadLocalInstance.Value;
instance.Initialize(payload); instance.Initialize(bufferReader);
return instance; return instance;
} }
private void FillContinguousBuffer(IBufferReader reader, byte[] destination)
{
#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
PayloadAsReadOnlySequence().CopyTo(new Span<byte>(destination));
#else
int offset = 0;
while (reader.TryGetNextSlice(out Slice slice))
{
slice.CopyTo(new ArraySegment<byte>(destination, offset, (int)slice.Length));
offset += (int)slice.Length;
}
// check that we filled the entire destination
GrpcPreconditions.CheckState(offset == payloadLength);
#endif
}
} }
} }

@ -22,7 +22,7 @@ namespace Grpc.Core.Internal
{ {
internal interface IUnaryResponseClientCallback internal interface IUnaryResponseClientCallback
{ {
void OnUnaryResponseClient(bool success, ClientSideStatus receivedStatus, byte[] receivedMessage, Metadata responseHeaders); void OnUnaryResponseClient(bool success, ClientSideStatus receivedStatus, IBufferReader receivedMessageReader, Metadata responseHeaders);
} }
// Received status for streaming response calls. // Received status for streaming response calls.
@ -33,7 +33,7 @@ namespace Grpc.Core.Internal
internal interface IReceivedMessageCallback internal interface IReceivedMessageCallback
{ {
void OnReceivedMessage(bool success, byte[] receivedMessage); void OnReceivedMessage(bool success, IBufferReader receivedMessageReader);
} }
internal interface IReceivedResponseHeadersCallback internal interface IReceivedResponseHeadersCallback

@ -40,7 +40,7 @@ namespace Grpc.Core.Internal
public readonly Delegates.grpcsharp_batch_context_create_delegate grpcsharp_batch_context_create; public readonly Delegates.grpcsharp_batch_context_create_delegate grpcsharp_batch_context_create;
public readonly Delegates.grpcsharp_batch_context_recv_initial_metadata_delegate grpcsharp_batch_context_recv_initial_metadata; public readonly Delegates.grpcsharp_batch_context_recv_initial_metadata_delegate grpcsharp_batch_context_recv_initial_metadata;
public readonly Delegates.grpcsharp_batch_context_recv_message_length_delegate grpcsharp_batch_context_recv_message_length; public readonly Delegates.grpcsharp_batch_context_recv_message_length_delegate grpcsharp_batch_context_recv_message_length;
public readonly Delegates.grpcsharp_batch_context_recv_message_to_buffer_delegate grpcsharp_batch_context_recv_message_to_buffer; public readonly Delegates.grpcsharp_batch_context_recv_message_next_slice_peek_delegate grpcsharp_batch_context_recv_message_next_slice_peek;
public readonly Delegates.grpcsharp_batch_context_recv_status_on_client_status_delegate grpcsharp_batch_context_recv_status_on_client_status; public readonly Delegates.grpcsharp_batch_context_recv_status_on_client_status_delegate grpcsharp_batch_context_recv_status_on_client_status;
public readonly Delegates.grpcsharp_batch_context_recv_status_on_client_details_delegate grpcsharp_batch_context_recv_status_on_client_details; public readonly Delegates.grpcsharp_batch_context_recv_status_on_client_details_delegate grpcsharp_batch_context_recv_status_on_client_details;
public readonly Delegates.grpcsharp_batch_context_recv_status_on_client_trailing_metadata_delegate grpcsharp_batch_context_recv_status_on_client_trailing_metadata; public readonly Delegates.grpcsharp_batch_context_recv_status_on_client_trailing_metadata_delegate grpcsharp_batch_context_recv_status_on_client_trailing_metadata;
@ -141,7 +141,7 @@ namespace Grpc.Core.Internal
this.grpcsharp_batch_context_create = GetMethodDelegate<Delegates.grpcsharp_batch_context_create_delegate>(library); this.grpcsharp_batch_context_create = GetMethodDelegate<Delegates.grpcsharp_batch_context_create_delegate>(library);
this.grpcsharp_batch_context_recv_initial_metadata = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_initial_metadata_delegate>(library); this.grpcsharp_batch_context_recv_initial_metadata = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_initial_metadata_delegate>(library);
this.grpcsharp_batch_context_recv_message_length = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_message_length_delegate>(library); this.grpcsharp_batch_context_recv_message_length = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_message_length_delegate>(library);
this.grpcsharp_batch_context_recv_message_to_buffer = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_message_to_buffer_delegate>(library); this.grpcsharp_batch_context_recv_message_next_slice_peek = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_message_next_slice_peek_delegate>(library);
this.grpcsharp_batch_context_recv_status_on_client_status = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_status_on_client_status_delegate>(library); this.grpcsharp_batch_context_recv_status_on_client_status = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_status_on_client_status_delegate>(library);
this.grpcsharp_batch_context_recv_status_on_client_details = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_status_on_client_details_delegate>(library); this.grpcsharp_batch_context_recv_status_on_client_details = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_status_on_client_details_delegate>(library);
this.grpcsharp_batch_context_recv_status_on_client_trailing_metadata = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_status_on_client_trailing_metadata_delegate>(library); this.grpcsharp_batch_context_recv_status_on_client_trailing_metadata = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_status_on_client_trailing_metadata_delegate>(library);
@ -241,7 +241,7 @@ namespace Grpc.Core.Internal
this.grpcsharp_batch_context_create = DllImportsFromStaticLib.grpcsharp_batch_context_create; this.grpcsharp_batch_context_create = DllImportsFromStaticLib.grpcsharp_batch_context_create;
this.grpcsharp_batch_context_recv_initial_metadata = DllImportsFromStaticLib.grpcsharp_batch_context_recv_initial_metadata; this.grpcsharp_batch_context_recv_initial_metadata = DllImportsFromStaticLib.grpcsharp_batch_context_recv_initial_metadata;
this.grpcsharp_batch_context_recv_message_length = DllImportsFromStaticLib.grpcsharp_batch_context_recv_message_length; this.grpcsharp_batch_context_recv_message_length = DllImportsFromStaticLib.grpcsharp_batch_context_recv_message_length;
this.grpcsharp_batch_context_recv_message_to_buffer = DllImportsFromStaticLib.grpcsharp_batch_context_recv_message_to_buffer; this.grpcsharp_batch_context_recv_message_next_slice_peek = DllImportsFromStaticLib.grpcsharp_batch_context_recv_message_next_slice_peek;
this.grpcsharp_batch_context_recv_status_on_client_status = DllImportsFromStaticLib.grpcsharp_batch_context_recv_status_on_client_status; this.grpcsharp_batch_context_recv_status_on_client_status = DllImportsFromStaticLib.grpcsharp_batch_context_recv_status_on_client_status;
this.grpcsharp_batch_context_recv_status_on_client_details = DllImportsFromStaticLib.grpcsharp_batch_context_recv_status_on_client_details; this.grpcsharp_batch_context_recv_status_on_client_details = DllImportsFromStaticLib.grpcsharp_batch_context_recv_status_on_client_details;
this.grpcsharp_batch_context_recv_status_on_client_trailing_metadata = DllImportsFromStaticLib.grpcsharp_batch_context_recv_status_on_client_trailing_metadata; this.grpcsharp_batch_context_recv_status_on_client_trailing_metadata = DllImportsFromStaticLib.grpcsharp_batch_context_recv_status_on_client_trailing_metadata;
@ -341,7 +341,7 @@ namespace Grpc.Core.Internal
this.grpcsharp_batch_context_create = DllImportsFromSharedLib.grpcsharp_batch_context_create; this.grpcsharp_batch_context_create = DllImportsFromSharedLib.grpcsharp_batch_context_create;
this.grpcsharp_batch_context_recv_initial_metadata = DllImportsFromSharedLib.grpcsharp_batch_context_recv_initial_metadata; this.grpcsharp_batch_context_recv_initial_metadata = DllImportsFromSharedLib.grpcsharp_batch_context_recv_initial_metadata;
this.grpcsharp_batch_context_recv_message_length = DllImportsFromSharedLib.grpcsharp_batch_context_recv_message_length; this.grpcsharp_batch_context_recv_message_length = DllImportsFromSharedLib.grpcsharp_batch_context_recv_message_length;
this.grpcsharp_batch_context_recv_message_to_buffer = DllImportsFromSharedLib.grpcsharp_batch_context_recv_message_to_buffer; this.grpcsharp_batch_context_recv_message_next_slice_peek = DllImportsFromSharedLib.grpcsharp_batch_context_recv_message_next_slice_peek;
this.grpcsharp_batch_context_recv_status_on_client_status = DllImportsFromSharedLib.grpcsharp_batch_context_recv_status_on_client_status; this.grpcsharp_batch_context_recv_status_on_client_status = DllImportsFromSharedLib.grpcsharp_batch_context_recv_status_on_client_status;
this.grpcsharp_batch_context_recv_status_on_client_details = DllImportsFromSharedLib.grpcsharp_batch_context_recv_status_on_client_details; this.grpcsharp_batch_context_recv_status_on_client_details = DllImportsFromSharedLib.grpcsharp_batch_context_recv_status_on_client_details;
this.grpcsharp_batch_context_recv_status_on_client_trailing_metadata = DllImportsFromSharedLib.grpcsharp_batch_context_recv_status_on_client_trailing_metadata; this.grpcsharp_batch_context_recv_status_on_client_trailing_metadata = DllImportsFromSharedLib.grpcsharp_batch_context_recv_status_on_client_trailing_metadata;
@ -444,7 +444,7 @@ namespace Grpc.Core.Internal
public delegate BatchContextSafeHandle grpcsharp_batch_context_create_delegate(); public delegate BatchContextSafeHandle grpcsharp_batch_context_create_delegate();
public delegate IntPtr grpcsharp_batch_context_recv_initial_metadata_delegate(BatchContextSafeHandle ctx); public delegate IntPtr grpcsharp_batch_context_recv_initial_metadata_delegate(BatchContextSafeHandle ctx);
public delegate IntPtr grpcsharp_batch_context_recv_message_length_delegate(BatchContextSafeHandle ctx); public delegate IntPtr grpcsharp_batch_context_recv_message_length_delegate(BatchContextSafeHandle ctx);
public delegate void grpcsharp_batch_context_recv_message_to_buffer_delegate(BatchContextSafeHandle ctx, byte[] buffer, UIntPtr bufferLen); public delegate int grpcsharp_batch_context_recv_message_next_slice_peek_delegate(BatchContextSafeHandle ctx, out UIntPtr sliceLen, out IntPtr sliceDataPtr);
public delegate StatusCode grpcsharp_batch_context_recv_status_on_client_status_delegate(BatchContextSafeHandle ctx); public delegate StatusCode grpcsharp_batch_context_recv_status_on_client_status_delegate(BatchContextSafeHandle ctx);
public delegate IntPtr grpcsharp_batch_context_recv_status_on_client_details_delegate(BatchContextSafeHandle ctx, out UIntPtr detailsLength); public delegate IntPtr grpcsharp_batch_context_recv_status_on_client_details_delegate(BatchContextSafeHandle ctx, out UIntPtr detailsLength);
public delegate IntPtr grpcsharp_batch_context_recv_status_on_client_trailing_metadata_delegate(BatchContextSafeHandle ctx); public delegate IntPtr grpcsharp_batch_context_recv_status_on_client_trailing_metadata_delegate(BatchContextSafeHandle ctx);
@ -562,7 +562,7 @@ namespace Grpc.Core.Internal
public static extern IntPtr grpcsharp_batch_context_recv_message_length(BatchContextSafeHandle ctx); public static extern IntPtr grpcsharp_batch_context_recv_message_length(BatchContextSafeHandle ctx);
[DllImport(ImportName)] [DllImport(ImportName)]
public static extern void grpcsharp_batch_context_recv_message_to_buffer(BatchContextSafeHandle ctx, byte[] buffer, UIntPtr bufferLen); public static extern int grpcsharp_batch_context_recv_message_next_slice_peek(BatchContextSafeHandle ctx, out UIntPtr sliceLen, out IntPtr sliceDataPtr);
[DllImport(ImportName)] [DllImport(ImportName)]
public static extern StatusCode grpcsharp_batch_context_recv_status_on_client_status(BatchContextSafeHandle ctx); public static extern StatusCode grpcsharp_batch_context_recv_status_on_client_status(BatchContextSafeHandle ctx);
@ -858,7 +858,7 @@ namespace Grpc.Core.Internal
public static extern IntPtr grpcsharp_batch_context_recv_message_length(BatchContextSafeHandle ctx); public static extern IntPtr grpcsharp_batch_context_recv_message_length(BatchContextSafeHandle ctx);
[DllImport(ImportName)] [DllImport(ImportName)]
public static extern void grpcsharp_batch_context_recv_message_to_buffer(BatchContextSafeHandle ctx, byte[] buffer, UIntPtr bufferLen); public static extern int grpcsharp_batch_context_recv_message_next_slice_peek(BatchContextSafeHandle ctx, out UIntPtr sliceLen, out IntPtr sliceDataPtr);
[DllImport(ImportName)] [DllImport(ImportName)]
public static extern StatusCode grpcsharp_batch_context_recv_status_on_client_status(BatchContextSafeHandle ctx); public static extern StatusCode grpcsharp_batch_context_recv_status_on_client_status(BatchContextSafeHandle ctx);

@ -0,0 +1,148 @@
#region Copyright notice and license
// Copyright 2019 The 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
#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
using Grpc.Core.Utils;
using System;
using System.Threading;
using System.Buffers;
namespace Grpc.Core.Internal
{
internal class ReusableSliceBuffer
{
public const int MaxCachedSegments = 1024; // ~4MB payload for 4K slices
readonly SliceSegment[] cachedSegments = new SliceSegment[MaxCachedSegments];
int populatedSegmentCount;
public ReadOnlySequence<byte> PopulateFrom(IBufferReader bufferReader)
{
populatedSegmentCount = 0;
long offset = 0;
SliceSegment prevSegment = null;
while (bufferReader.TryGetNextSlice(out Slice slice))
{
// Initialize cached segment if still null or just allocate a new segment if we already reached MaxCachedSegments
var current = populatedSegmentCount < cachedSegments.Length ? cachedSegments[populatedSegmentCount] : new SliceSegment();
if (current == null)
{
current = cachedSegments[populatedSegmentCount] = new SliceSegment();
}
current.Reset(slice, offset);
prevSegment?.SetNext(current);
populatedSegmentCount ++;
offset += slice.Length;
prevSegment = current;
}
// Not necessary for ending the ReadOnlySequence, but for making sure we
// don't keep more than MaxCachedSegments alive.
prevSegment?.SetNext(null);
if (populatedSegmentCount == 0)
{
return ReadOnlySequence<byte>.Empty;
}
var firstSegment = cachedSegments[0];
var lastSegment = prevSegment;
return new ReadOnlySequence<byte>(firstSegment, 0, lastSegment, lastSegment.Memory.Length);
}
public void Invalidate()
{
if (populatedSegmentCount == 0)
{
return;
}
var segment = cachedSegments[0];
while (segment != null)
{
segment.Reset(new Slice(IntPtr.Zero, 0), 0);
var nextSegment = (SliceSegment) segment.Next;
segment.SetNext(null);
segment = nextSegment;
}
populatedSegmentCount = 0;
}
// Represents a segment in ReadOnlySequence
// Segment is backed by Slice and the instances are reusable.
private class SliceSegment : ReadOnlySequenceSegment<byte>
{
readonly SliceMemoryManager pointerMemoryManager = new SliceMemoryManager();
public void Reset(Slice slice, long runningIndex)
{
pointerMemoryManager.Reset(slice);
Memory = pointerMemoryManager.Memory; // maybe not always necessary
RunningIndex = runningIndex;
}
public void SetNext(ReadOnlySequenceSegment<byte> next)
{
Next = next;
}
}
// Allow creating instances of Memory<byte> from Slice.
// Represents a chunk of native memory, but doesn't manage its lifetime.
// Instances of this class are reuseable - they can be reset to point to a different memory chunk.
// That is important to make the instances cacheable (rather then creating new instances
// the old ones will be reused to reduce GC pressure).
private class SliceMemoryManager : MemoryManager<byte>
{
private Slice slice;
public void Reset(Slice slice)
{
this.slice = slice;
}
public void Reset()
{
Reset(new Slice(IntPtr.Zero, 0));
}
public override Span<byte> GetSpan()
{
return slice.ToSpanUnsafe();
}
public override MemoryHandle Pin(int elementIndex = 0)
{
throw new NotSupportedException();
}
public override void Unpin()
{
}
protected override void Dispose(bool disposing)
{
// NOP
}
}
}
}
#endif

@ -0,0 +1,68 @@
#region Copyright notice and license
// Copyright 2019 The 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.Utils;
namespace Grpc.Core.Internal
{
/// <summary>
/// Slice of native memory.
/// Rough equivalent of grpc_slice (but doesn't support inlined slices, just a pointer to data and length)
/// </summary>
internal struct Slice
{
private readonly IntPtr dataPtr;
private readonly int length;
public Slice(IntPtr dataPtr, int length)
{
this.dataPtr = dataPtr;
this.length = length;
}
public int Length => length;
// copies data of the slice to given span.
// there needs to be enough space in the destination buffer
public void CopyTo(ArraySegment<byte> destination)
{
Marshal.Copy(dataPtr, destination.Array, destination.Offset, length);
}
#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
public Span<byte> ToSpanUnsafe()
{
unsafe
{
return new Span<byte>((byte*) dataPtr, length);
}
}
#endif
/// <summary>
/// Returns a <see cref="System.String"/> that represents the current <see cref="Grpc.Core.Internal.Slice"/>.
/// </summary>
public override string ToString()
{
return string.Format("[Slice: dataPtr={0}, length={1}]", dataPtr, length);
}
}
}

@ -28,6 +28,7 @@
<ProtoRoot Condition="'%(Protobuf.ProtoRoot)' == '' " /> <ProtoRoot Condition="'%(Protobuf.ProtoRoot)' == '' " />
<CompileOutputs Condition="'%(Protobuf.CompileOutputs)' == ''">True</CompileOutputs> <CompileOutputs Condition="'%(Protobuf.CompileOutputs)' == ''">True</CompileOutputs>
<OutputDir Condition="'%(Protobuf.OutputDir)' == '' ">$(Protobuf_OutputPath)</OutputDir> <OutputDir Condition="'%(Protobuf.OutputDir)' == '' ">$(Protobuf_OutputPath)</OutputDir>
<Generator Condition="'%(Protobuf.Generator)' == '' and '$(DisableProtobufDesignTimeBuild)' != 'true' " >MSBuild:Compile</Generator>
</Protobuf> </Protobuf>
</ItemDefinitionGroup> </ItemDefinitionGroup>

@ -59,12 +59,16 @@ typedef struct grpcsharp_batch_context {
} send_status_from_server; } send_status_from_server;
grpc_metadata_array recv_initial_metadata; grpc_metadata_array recv_initial_metadata;
grpc_byte_buffer* recv_message; grpc_byte_buffer* recv_message;
grpc_byte_buffer_reader* recv_message_reader;
struct { struct {
grpc_metadata_array trailing_metadata; grpc_metadata_array trailing_metadata;
grpc_status_code status; grpc_status_code status;
grpc_slice status_details; grpc_slice status_details;
} recv_status_on_client; } recv_status_on_client;
int recv_close_on_server_cancelled; int recv_close_on_server_cancelled;
/* reserve space for byte_buffer_reader */
grpc_byte_buffer_reader reserved_recv_message_reader;
} grpcsharp_batch_context; } grpcsharp_batch_context;
GPR_EXPORT grpcsharp_batch_context* GPR_CALLTYPE GPR_EXPORT grpcsharp_batch_context* GPR_CALLTYPE
@ -206,6 +210,9 @@ grpcsharp_batch_context_reset(grpcsharp_batch_context* ctx) {
grpcsharp_metadata_array_destroy_metadata_only(&(ctx->recv_initial_metadata)); grpcsharp_metadata_array_destroy_metadata_only(&(ctx->recv_initial_metadata));
if (ctx->recv_message_reader) {
grpc_byte_buffer_reader_destroy(ctx->recv_message_reader);
}
grpc_byte_buffer_destroy(ctx->recv_message); grpc_byte_buffer_destroy(ctx->recv_message);
grpcsharp_metadata_array_destroy_metadata_only( grpcsharp_metadata_array_destroy_metadata_only(
@ -264,27 +271,42 @@ GPR_EXPORT intptr_t GPR_CALLTYPE grpcsharp_batch_context_recv_message_length(
} }
/* /*
* Copies data from recv_message to a buffer. Fatal error occurs if * Gets the next slice from recv_message byte buffer.
* buffer is too small. * Returns 1 if a slice was get successfully, 0 if there are no more slices to
* read. Set slice_len to the length of the slice and the slice_data_ptr to
* point to slice's data. Caller must ensure that the byte buffer being read
* from stays alive as long as the data of the slice are being accessed
* (grpc_byte_buffer_reader_peek method is used internally)
*
* Remarks:
* Slices can only be iterated once.
* Initializes recv_message_buffer_reader if it was not initialized yet.
*/ */
GPR_EXPORT void GPR_CALLTYPE grpcsharp_batch_context_recv_message_to_buffer( GPR_EXPORT int GPR_CALLTYPE
const grpcsharp_batch_context* ctx, char* buffer, size_t buffer_len) { grpcsharp_batch_context_recv_message_next_slice_peek(
grpc_byte_buffer_reader reader; grpcsharp_batch_context* ctx, size_t* slice_len, uint8_t** slice_data_ptr) {
grpc_slice slice; *slice_len = 0;
size_t offset = 0; *slice_data_ptr = NULL;
GPR_ASSERT(grpc_byte_buffer_reader_init(&reader, ctx->recv_message)); if (!ctx->recv_message) {
return 0;
}
while (grpc_byte_buffer_reader_next(&reader, &slice)) { if (!ctx->recv_message_reader) {
size_t len = GRPC_SLICE_LENGTH(slice); ctx->recv_message_reader = &ctx->reserved_recv_message_reader;
GPR_ASSERT(offset + len <= buffer_len); GPR_ASSERT(grpc_byte_buffer_reader_init(ctx->recv_message_reader,
memcpy(buffer + offset, GRPC_SLICE_START_PTR(slice), ctx->recv_message));
GRPC_SLICE_LENGTH(slice));
offset += len;
grpc_slice_unref(slice);
} }
grpc_byte_buffer_reader_destroy(&reader); grpc_slice* slice_ptr;
if (!grpc_byte_buffer_reader_peek(ctx->recv_message_reader, &slice_ptr)) {
return 0;
}
/* recv_message buffer must not be deleted before all the data is read */
*slice_len = GRPC_SLICE_LENGTH(*slice_ptr);
*slice_data_ptr = GRPC_SLICE_START_PTR(*slice_ptr);
return 1;
} }
GPR_EXPORT grpc_status_code GPR_CALLTYPE GPR_EXPORT grpc_status_code GPR_CALLTYPE

@ -7,8 +7,12 @@
"Grpc.Core.Internal.Tests.ChannelArgsSafeHandleTest", "Grpc.Core.Internal.Tests.ChannelArgsSafeHandleTest",
"Grpc.Core.Internal.Tests.CompletionQueueEventTest", "Grpc.Core.Internal.Tests.CompletionQueueEventTest",
"Grpc.Core.Internal.Tests.CompletionQueueSafeHandleTest", "Grpc.Core.Internal.Tests.CompletionQueueSafeHandleTest",
"Grpc.Core.Internal.Tests.DefaultDeserializationContextTest",
"Grpc.Core.Internal.Tests.DefaultObjectPoolTest", "Grpc.Core.Internal.Tests.DefaultObjectPoolTest",
"Grpc.Core.Internal.Tests.FakeBufferReaderManagerTest",
"Grpc.Core.Internal.Tests.MetadataArraySafeHandleTest", "Grpc.Core.Internal.Tests.MetadataArraySafeHandleTest",
"Grpc.Core.Internal.Tests.ReusableSliceBufferTest",
"Grpc.Core.Internal.Tests.SliceTest",
"Grpc.Core.Internal.Tests.TimespecTest", "Grpc.Core.Internal.Tests.TimespecTest",
"Grpc.Core.Tests.AppDomainUnloadTest", "Grpc.Core.Tests.AppDomainUnloadTest",
"Grpc.Core.Tests.AuthContextTest", "Grpc.Core.Tests.AuthContextTest",

@ -46,7 +46,7 @@ void grpcsharp_batch_context_recv_message_length() {
fprintf(stderr, "Should never reach here"); fprintf(stderr, "Should never reach here");
abort(); abort();
} }
void grpcsharp_batch_context_recv_message_to_buffer() { void grpcsharp_batch_context_recv_message_next_slice_peek() {
fprintf(stderr, "Should never reach here"); fprintf(stderr, "Should never reach here");
abort(); abort();
} }

@ -135,7 +135,8 @@ typedef NS_ENUM(NSUInteger, GRPCErrorCode) {
/** /**
* The server is currently unavailable. This is most likely a transient condition and may be * The server is currently unavailable. This is most likely a transient condition and may be
* corrected by retrying with a backoff. * corrected by retrying with a backoff. Note that it is not always safe to retry
* non-idempotent operations.
*/ */
GRPCErrorCodeUnavailable = 14, GRPCErrorCodeUnavailable = 14,

@ -146,6 +146,37 @@ grpc_proto_library(
], ],
) )
# Test that grpc_proto_library/cc_grpc_library can consume generated files
genrule(
name = "messages_gen_proto_file",
srcs = ["messages.proto"],
outs = ["messages_gen.proto"],
cmd = "cp $< $@",
)
grpc_proto_library(
name = "messages_gen_proto",
srcs = ["messages_gen_proto_file"],
has_services = False,
)
genrule(
name = "test_gen_proto_file",
srcs = ["test.proto"],
outs = ["test_gen.proto"],
cmd = "sed 's/messages.proto/messages_gen.proto/' $< > $@",
)
# Consume generated files in srcs and in deps
grpc_proto_library(
name = "test_gen_proto",
srcs = ["test_gen_proto_file"],
deps = [
"empty_proto",
"messages_gen_proto",
],
)
proto_library( proto_library(
name = "test_proto_descriptor", name = "test_proto_descriptor",
srcs = ["test.proto"], srcs = ["test.proto"],

@ -13,7 +13,6 @@
# limitations under the License. # limitations under the License.
require_relative './grpc' require_relative './grpc'
require_relative './google_rpc_status_utils'
# GRPC contains the General RPC module. # GRPC contains the General RPC module.
module GRPC module GRPC
@ -58,10 +57,11 @@ module GRPC
# #
# @return [Google::Rpc::Status, nil] # @return [Google::Rpc::Status, nil]
def to_rpc_status def to_rpc_status
# Lazily require google_rpc_status_utils to scope
# loading protobuf_c.so to the users of this method.
require_relative './google_rpc_status_utils'
status = to_status status = to_status
return if status.nil? return if status.nil?
GoogleRpcStatusUtils.extract_google_rpc_status(status) GoogleRpcStatusUtils.extract_google_rpc_status(status)
rescue Google::Protobuf::ParseError => parse_error rescue Google::Protobuf::ParseError => parse_error
GRPC.logger.warn('parse error: to_rpc_status failed') GRPC.logger.warn('parse error: to_rpc_status failed')

@ -6,7 +6,7 @@ native_method_signatures = [
'BatchContextSafeHandle grpcsharp_batch_context_create()', 'BatchContextSafeHandle grpcsharp_batch_context_create()',
'IntPtr grpcsharp_batch_context_recv_initial_metadata(BatchContextSafeHandle ctx)', 'IntPtr grpcsharp_batch_context_recv_initial_metadata(BatchContextSafeHandle ctx)',
'IntPtr grpcsharp_batch_context_recv_message_length(BatchContextSafeHandle ctx)', 'IntPtr grpcsharp_batch_context_recv_message_length(BatchContextSafeHandle ctx)',
'void grpcsharp_batch_context_recv_message_to_buffer(BatchContextSafeHandle ctx, byte[] buffer, UIntPtr bufferLen)', 'int grpcsharp_batch_context_recv_message_next_slice_peek(BatchContextSafeHandle ctx, out UIntPtr sliceLen, out IntPtr sliceDataPtr)',
'StatusCode grpcsharp_batch_context_recv_status_on_client_status(BatchContextSafeHandle ctx)', 'StatusCode grpcsharp_batch_context_recv_status_on_client_status(BatchContextSafeHandle ctx)',
'IntPtr grpcsharp_batch_context_recv_status_on_client_details(BatchContextSafeHandle ctx, out UIntPtr detailsLength)', 'IntPtr grpcsharp_batch_context_recv_status_on_client_details(BatchContextSafeHandle ctx, out UIntPtr detailsLength)',
'IntPtr grpcsharp_batch_context_recv_status_on_client_trailing_metadata(BatchContextSafeHandle ctx)', 'IntPtr grpcsharp_batch_context_recv_status_on_client_trailing_metadata(BatchContextSafeHandle ctx)',

@ -31,21 +31,12 @@ static void fake_free(void* addr) {
*(static_cast<intptr_t*>(addr)) = static_cast<intptr_t>(0xdeadd00d); *(static_cast<intptr_t*>(addr)) = static_cast<intptr_t>(0xdeadd00d);
} }
static void* fake_aligned_malloc(size_t size, size_t alignment) {
return (void*)(size + alignment);
}
static void fake_aligned_free(void* addr) {
*(static_cast<intptr_t*>(addr)) = static_cast<intptr_t>(0xcafef00d);
}
static void test_custom_allocs() { static void test_custom_allocs() {
const gpr_allocation_functions default_fns = gpr_get_allocation_functions(); const gpr_allocation_functions default_fns = gpr_get_allocation_functions();
intptr_t addr_to_free = 0; intptr_t addr_to_free = 0;
char* i; char* i;
gpr_allocation_functions fns = {fake_malloc, nullptr, gpr_allocation_functions fns = {fake_malloc, nullptr, fake_realloc,
fake_realloc, fake_free, fake_free};
fake_aligned_malloc, fake_aligned_free};
gpr_set_allocation_functions(fns); gpr_set_allocation_functions(fns);
GPR_ASSERT((void*)(size_t)0xdeadbeef == gpr_malloc(0xdeadbeef)); GPR_ASSERT((void*)(size_t)0xdeadbeef == gpr_malloc(0xdeadbeef));
@ -54,11 +45,6 @@ static void test_custom_allocs() {
gpr_free(&addr_to_free); gpr_free(&addr_to_free);
GPR_ASSERT(addr_to_free == (intptr_t)0xdeadd00d); GPR_ASSERT(addr_to_free == (intptr_t)0xdeadd00d);
GPR_ASSERT((void*)(size_t)(0xdeadbeef + 64) ==
gpr_malloc_aligned(0xdeadbeef, 64));
gpr_free_aligned(&addr_to_free);
GPR_ASSERT(addr_to_free == (intptr_t)0xcafef00d);
/* Restore and check we don't get funky values and that we don't leak */ /* Restore and check we don't get funky values and that we don't leak */
gpr_set_allocation_functions(default_fns); gpr_set_allocation_functions(default_fns);
GPR_ASSERT((void*)sizeof(*i) != GPR_ASSERT((void*)sizeof(*i) !=

@ -54,10 +54,9 @@ static void* guard_malloc(size_t size) {
NO_BARRIER_FETCH_ADD(&g_memory_counters.total_allocs_absolute, (gpr_atm)1); NO_BARRIER_FETCH_ADD(&g_memory_counters.total_allocs_absolute, (gpr_atm)1);
NO_BARRIER_FETCH_ADD(&g_memory_counters.total_allocs_relative, (gpr_atm)1); NO_BARRIER_FETCH_ADD(&g_memory_counters.total_allocs_relative, (gpr_atm)1);
void* ptr = g_old_allocs.malloc_fn( void* ptr = g_old_allocs.malloc_fn(
GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE(sizeof(size)) + size); GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(size)) + size);
*static_cast<size_t*>(ptr) = size; *static_cast<size_t*>(ptr) = size;
return static_cast<char*>(ptr) + return static_cast<char*>(ptr) + GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(size));
GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE(sizeof(size));
} }
static void* guard_realloc(void* vptr, size_t size) { static void* guard_realloc(void* vptr, size_t size) {
@ -68,36 +67,31 @@ static void* guard_realloc(void* vptr, size_t size) {
guard_free(vptr); guard_free(vptr);
return nullptr; return nullptr;
} }
void* ptr = static_cast<char*>(vptr) - void* ptr =
GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE(sizeof(size)); static_cast<char*>(vptr) - GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(size));
NO_BARRIER_FETCH_ADD(&g_memory_counters.total_size_absolute, (gpr_atm)size); NO_BARRIER_FETCH_ADD(&g_memory_counters.total_size_absolute, (gpr_atm)size);
NO_BARRIER_FETCH_ADD(&g_memory_counters.total_size_relative, NO_BARRIER_FETCH_ADD(&g_memory_counters.total_size_relative,
-*static_cast<gpr_atm*>(ptr)); -*static_cast<gpr_atm*>(ptr));
NO_BARRIER_FETCH_ADD(&g_memory_counters.total_size_relative, (gpr_atm)size); NO_BARRIER_FETCH_ADD(&g_memory_counters.total_size_relative, (gpr_atm)size);
NO_BARRIER_FETCH_ADD(&g_memory_counters.total_allocs_absolute, (gpr_atm)1); NO_BARRIER_FETCH_ADD(&g_memory_counters.total_allocs_absolute, (gpr_atm)1);
ptr = g_old_allocs.realloc_fn( ptr = g_old_allocs.realloc_fn(
ptr, GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE(sizeof(size)) + size); ptr, GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(size)) + size);
*static_cast<size_t*>(ptr) = size; *static_cast<size_t*>(ptr) = size;
return static_cast<char*>(ptr) + return static_cast<char*>(ptr) + GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(size));
GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE(sizeof(size));
} }
static void guard_free(void* vptr) { static void guard_free(void* vptr) {
if (vptr == nullptr) return; if (vptr == nullptr) return;
void* ptr = static_cast<char*>(vptr) - void* ptr =
GPR_ROUND_UP_TO_MAX_ALIGNMENT_SIZE(sizeof(size_t)); static_cast<char*>(vptr) - GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(size_t));
NO_BARRIER_FETCH_ADD(&g_memory_counters.total_size_relative, NO_BARRIER_FETCH_ADD(&g_memory_counters.total_size_relative,
-*static_cast<gpr_atm*>(ptr)); -*static_cast<gpr_atm*>(ptr));
NO_BARRIER_FETCH_ADD(&g_memory_counters.total_allocs_relative, -(gpr_atm)1); NO_BARRIER_FETCH_ADD(&g_memory_counters.total_allocs_relative, -(gpr_atm)1);
g_old_allocs.free_fn(ptr); g_old_allocs.free_fn(ptr);
} }
// NB: We do not specify guard_malloc_aligned/guard_free_aligned methods. Since struct gpr_allocation_functions g_guard_allocs = {guard_malloc, nullptr,
// they are null, calls to gpr_malloc_aligned/gpr_free_aligned are executed as a guard_realloc, guard_free};
// wrapper over gpr_malloc/gpr_free, which do use guard_malloc/guard_free, and
// thus their allocations are tracked as well.
struct gpr_allocation_functions g_guard_allocs = {
guard_malloc, nullptr, guard_realloc, guard_free, nullptr, nullptr};
void grpc_memory_counters_init() { void grpc_memory_counters_init() {
memset(&g_memory_counters, 0, sizeof(g_memory_counters)); memset(&g_memory_counters, 0, sizeof(g_memory_counters));

@ -1,4 +1,4 @@
# Copyright 2017 gRPC authors. # Copyright 2019 The gRPC Authors
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@ -16,7 +16,7 @@
# Location of the continuous shell script in repository. # Location of the continuous shell script in repository.
build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh" build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh"
timeout_mins: 240 timeout_mins: 60
action { action {
define_artifacts { define_artifacts {
regex: "**/*sponge_log.*" regex: "**/*sponge_log.*"
@ -26,5 +26,5 @@ action {
env_vars { env_vars {
key: "RUN_TESTS_FLAGS" key: "RUN_TESTS_FLAGS"
value: "-f basictests linux corelang dbg --inner_jobs 16 -j 1 --internal_ci --max_time=3600" value: "-f basictests linux csharp --inner_jobs 16 -j 2 --internal_ci --max_time=3600"
} }

@ -1,4 +1,4 @@
# Copyright 2017 gRPC authors. # Copyright 2019 The gRPC Authors
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@ -16,7 +16,7 @@
# Location of the continuous shell script in repository. # Location of the continuous shell script in repository.
build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh" build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh"
timeout_mins: 240 timeout_mins: 60
action { action {
define_artifacts { define_artifacts {
regex: "**/*sponge_log.*" regex: "**/*sponge_log.*"
@ -26,5 +26,5 @@ action {
env_vars { env_vars {
key: "RUN_TESTS_FLAGS" key: "RUN_TESTS_FLAGS"
value: "-f basictests linux corelang opt --inner_jobs 16 -j 1 --internal_ci --max_time=3600" value: "-f basictests linux grpc-node --inner_jobs 16 -j 2 --internal_ci --max_time=3600"
} }

@ -0,0 +1,30 @@
# Copyright 2019 The 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.
# Config file for the internal CI (in protobuf text format)
# Location of the continuous shell script in repository.
build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh"
timeout_mins: 60
action {
define_artifacts {
regex: "**/*sponge_log.*"
regex: "github/grpc/reports/**"
}
}
env_vars {
key: "RUN_TESTS_FLAGS"
value: "-f basictests linux php --inner_jobs 16 -j 2 --internal_ci --max_time=3600"
}

@ -0,0 +1,30 @@
# Copyright 2019 The 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.
# Config file for the internal CI (in protobuf text format)
# Location of the continuous shell script in repository.
build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh"
timeout_mins: 60
action {
define_artifacts {
regex: "**/*sponge_log.*"
regex: "github/grpc/reports/**"
}
}
env_vars {
key: "RUN_TESTS_FLAGS"
value: "-f basictests linux python --inner_jobs 16 -j 2 --internal_ci --max_time=3600"
}

@ -0,0 +1,30 @@
# Copyright 2019 The 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.
# Config file for the internal CI (in protobuf text format)
# Location of the continuous shell script in repository.
build_file: "grpc/tools/internal_ci/linux/grpc_run_tests_matrix.sh"
timeout_mins: 60
action {
define_artifacts {
regex: "**/*sponge_log.*"
regex: "github/grpc/reports/**"
}
}
env_vars {
key: "RUN_TESTS_FLAGS"
value: "-f basictests linux ruby --inner_jobs 16 -j 2 --internal_ci --max_time=3600"
}

@ -17,7 +17,7 @@
# Location of the continuous shell script in repository. # Location of the continuous shell script in repository.
build_file: "grpc/tools/internal_ci/macos/grpc_run_tests_matrix.sh" build_file: "grpc/tools/internal_ci/macos/grpc_run_tests_matrix.sh"
gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0eeee2db331.json" gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0eeee2db331.json"
timeout_mins: 60 timeout_mins: 120
action { action {
define_artifacts { define_artifacts {
regex: "**/*sponge_log.*" regex: "**/*sponge_log.*"

@ -0,0 +1,31 @@
# Copyright 2019 The 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.
# Config file for the internal CI (in protobuf text format)
# Location of the continuous shell script in repository.
build_file: "grpc/tools/internal_ci/macos/grpc_run_tests_matrix.sh"
gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0eeee2db331.json"
timeout_mins: 120
action {
define_artifacts {
regex: "**/*sponge_log.*"
regex: "github/grpc/reports/**"
}
}
env_vars {
key: "RUN_TESTS_FLAGS"
value: "-f basictests macos corelang --internal_ci -j 1 --inner_jobs 4 --max_time=3600"
}

@ -0,0 +1,31 @@
# Copyright 2019 The 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.
# Config file for the internal CI (in protobuf text format)
# Location of the continuous shell script in repository.
build_file: "grpc/tools/internal_ci/macos/grpc_run_tests_matrix.sh"
gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0eeee2db331.json"
timeout_mins: 60
action {
define_artifacts {
regex: "**/*sponge_log.*"
regex: "github/grpc/reports/**"
}
}
env_vars {
key: "RUN_TESTS_FLAGS"
value: "-f basictests macos csharp --internal_ci -j 1 --inner_jobs 4 --max_time=3600"
}

@ -0,0 +1,31 @@
# Copyright 2019 The 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.
# Config file for the internal CI (in protobuf text format)
# Location of the continuous shell script in repository.
build_file: "grpc/tools/internal_ci/macos/grpc_run_tests_matrix.sh"
gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0eeee2db331.json"
timeout_mins: 60
action {
define_artifacts {
regex: "**/*sponge_log.*"
regex: "github/grpc/reports/**"
}
}
env_vars {
key: "RUN_TESTS_FLAGS"
value: "-f basictests macos grpc-node --internal_ci -j 1 --inner_jobs 4 --max_time=3600"
}

@ -0,0 +1,31 @@
# Copyright 2019 The 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.
# Config file for the internal CI (in protobuf text format)
# Location of the continuous shell script in repository.
build_file: "grpc/tools/internal_ci/macos/grpc_run_tests_matrix.sh"
gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0eeee2db331.json"
timeout_mins: 60
action {
define_artifacts {
regex: "**/*sponge_log.*"
regex: "github/grpc/reports/**"
}
}
env_vars {
key: "RUN_TESTS_FLAGS"
value: "-f basictests macos php --internal_ci -j 1 --inner_jobs 4 --max_time=3600"
}

@ -0,0 +1,31 @@
# Copyright 2019 The 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.
# Config file for the internal CI (in protobuf text format)
# Location of the continuous shell script in repository.
build_file: "grpc/tools/internal_ci/macos/grpc_run_tests_matrix.sh"
gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0eeee2db331.json"
timeout_mins: 60
action {
define_artifacts {
regex: "**/*sponge_log.*"
regex: "github/grpc/reports/**"
}
}
env_vars {
key: "RUN_TESTS_FLAGS"
value: "-f basictests macos python --internal_ci -j 1 --inner_jobs 4 --max_time=3600"
}

@ -0,0 +1,31 @@
# Copyright 2019 The 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.
# Config file for the internal CI (in protobuf text format)
# Location of the continuous shell script in repository.
build_file: "grpc/tools/internal_ci/macos/grpc_run_tests_matrix.sh"
gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0eeee2db331.json"
timeout_mins: 60
action {
define_artifacts {
regex: "**/*sponge_log.*"
regex: "github/grpc/reports/**"
}
}
env_vars {
key: "RUN_TESTS_FLAGS"
value: "-f basictests macos ruby --internal_ci -j 1 --inner_jobs 4 --max_time=3600"
}

@ -16,7 +16,7 @@
# Location of the continuous shell script in repository. # Location of the continuous shell script in repository.
build_file: "grpc/tools/internal_ci/windows/grpc_run_tests_matrix.bat" build_file: "grpc/tools/internal_ci/windows/grpc_run_tests_matrix.bat"
timeout_mins: 60 timeout_mins: 120
action { action {
define_artifacts { define_artifacts {
regex: "**/*sponge_log.*" regex: "**/*sponge_log.*"

@ -0,0 +1,30 @@
# Copyright 2019 The 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.
# Config file for the internal CI (in protobuf text format)
# Location of the continuous shell script in repository.
build_file: "grpc/tools/internal_ci/windows/grpc_run_tests_matrix.bat"
timeout_mins: 120
action {
define_artifacts {
regex: "**/*sponge_log.*"
regex: "github/grpc/reports/**"
}
}
env_vars {
key: "RUN_TESTS_FLAGS"
value: "-f basictests windows c -j 1 --inner_jobs 8 --internal_ci --max_time=3600"
}

@ -0,0 +1,30 @@
# Copyright 2019 The 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.
# Config file for the internal CI (in protobuf text format)
# Location of the continuous shell script in repository.
build_file: "grpc/tools/internal_ci/windows/grpc_run_tests_matrix.bat"
timeout_mins: 60
action {
define_artifacts {
regex: "**/*sponge_log.*"
regex: "github/grpc/reports/**"
}
}
env_vars {
key: "RUN_TESTS_FLAGS"
value: "-f basictests windows csharp -j 1 --inner_jobs 8 --internal_ci --max_time=3600"
}

@ -0,0 +1,30 @@
# Copyright 2019 The 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.
# Config file for the internal CI (in protobuf text format)
# Location of the continuous shell script in repository.
build_file: "grpc/tools/internal_ci/windows/grpc_run_tests_matrix.bat"
timeout_mins: 60
action {
define_artifacts {
regex: "**/*sponge_log.*"
regex: "github/grpc/reports/**"
}
}
env_vars {
key: "RUN_TESTS_FLAGS"
value: "-f basictests windows python -j 1 --inner_jobs 8 --internal_ci --max_time=3600"
}

@ -0,0 +1,32 @@
# Copyright 2019 The 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.
# Config file for the internal CI (in protobuf text format)
# Location of the continuous shell script in repository.
build_file: "grpc/tools/internal_ci/windows/bazel_rbe.bat"
timeout_mins: 60
gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/GrpcTesting-d0eeee2db331.json"
gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/resultstore_api_key"
gfile_resources: "/bigstore/grpc-testing-secrets/gcp_credentials/rbe-windows-credentials.json"
bazel_setting {
# In order for Kokoro to recognize this as a bazel build and publish the bazel resultstore link,
# the bazel_setting section needs to be present and "upsalite_frontend_address" needs to be
# set. The rest of configuration from bazel_setting is unused (we configure everything when bazel
# command is invoked).
upsalite_frontend_address: "https://source.cloud.google.com"
}
Loading…
Cancel
Save