Merge branch 'master' into callback_test_coverage_1

pull/17350/head
hcaseyal 6 years ago committed by GitHub
commit cf14bd13fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      CONTRIBUTING.md
  2. 44
      WORKSPACE
  3. 4
      bazel/grpc_build_system.bzl
  4. 22
      doc/grpc_release_schedule.md
  5. 4
      doc/naming.md
  6. 3
      doc/python/sphinx/conf.py
  7. 12
      doc/python/sphinx/grpc_channelz.rst
  8. 1
      doc/python/sphinx/index.rst
  9. 2
      examples/python/helloworld/greeter_client.py
  10. 2
      examples/python/helloworld/greeter_client_with_options.py
  11. 2
      examples/python/helloworld/greeter_server.py
  12. 2
      examples/python/helloworld/greeter_server_with_reflection.py
  13. 2
      examples/python/interceptors/default_value/greeter_client.py
  14. 2
      examples/python/interceptors/headers/greeter_client.py
  15. 2
      examples/python/interceptors/headers/greeter_server.py
  16. 2
      examples/python/multiplex/multiplex_client.py
  17. 2
      examples/python/multiplex/multiplex_server.py
  18. 2
      examples/python/route_guide/route_guide_client.py
  19. 2
      examples/python/route_guide/route_guide_server.py
  20. 7
      include/grpc/impl/codegen/grpc_types.h
  21. 5
      include/grpcpp/generic/generic_stub.h
  22. 8
      include/grpcpp/impl/codegen/byte_buffer.h
  23. 27
      include/grpcpp/impl/codegen/callback_common.h
  24. 12
      include/grpcpp/impl/codegen/channel_interface.h
  25. 641
      include/grpcpp/impl/codegen/client_callback.h
  26. 12
      include/grpcpp/impl/codegen/client_context.h
  27. 774
      include/grpcpp/impl/codegen/server_callback.h
  28. 21
      include/grpcpp/impl/codegen/server_context.h
  29. 18
      setup.py
  30. 147
      src/compiler/cpp_generator.cc
  31. 84
      src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc
  32. 10
      src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc
  33. 36
      src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc
  34. 1
      src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h
  35. 12
      src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc
  36. 4
      src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h
  37. 3
      src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc
  38. 1
      src/core/ext/transport/chttp2/transport/chttp2_transport.cc
  39. 1
      src/core/ext/transport/chttp2/transport/context_list.cc
  40. 2
      src/core/ext/transport/chttp2/transport/context_list.h
  41. 6
      src/core/ext/transport/chttp2/transport/internal.h
  42. 7
      src/core/ext/transport/chttp2/transport/writing.cc
  43. 44
      src/core/lib/gprpp/inlined_vector.h
  44. 3
      src/core/lib/iomgr/buffer_list.cc
  45. 4
      src/core/lib/iomgr/buffer_list.h
  46. 4
      src/core/lib/iomgr/ev_epoll1_linux.cc
  47. 4
      src/core/lib/iomgr/ev_epollex_linux.cc
  48. 4
      src/core/lib/iomgr/ev_poll_posix.cc
  49. 8
      src/core/lib/iomgr/ev_posix.cc
  50. 10
      src/core/lib/iomgr/ev_posix.h
  51. 2
      src/core/lib/iomgr/fork_posix.cc
  52. 4
      src/core/lib/iomgr/iomgr.cc
  53. 4
      src/core/lib/iomgr/iomgr.h
  54. 4
      src/core/lib/iomgr/iomgr_custom.cc
  55. 4
      src/core/lib/iomgr/iomgr_internal.cc
  56. 4
      src/core/lib/iomgr/iomgr_internal.h
  57. 7
      src/core/lib/iomgr/iomgr_posix.cc
  58. 7
      src/core/lib/iomgr/iomgr_posix_cfstream.cc
  59. 5
      src/core/lib/iomgr/iomgr_windows.cc
  60. 2
      src/core/lib/iomgr/resolve_address.h
  61. 36
      src/core/lib/iomgr/tcp_posix.cc
  62. 1
      src/core/lib/surface/init.cc
  63. 3
      src/core/lib/transport/transport.cc
  64. 6
      src/core/lib/transport/transport.h
  65. 11
      src/core/tsi/alts/handshaker/alts_handshaker_client.cc
  66. 9
      src/cpp/client/generic_stub.cc
  67. 7
      src/cpp/server/server_cc.cc
  68. 47
      src/cpp/server/server_context.cc
  69. 2
      src/csharp/Grpc.Core.Tests/AppDomainUnloadTest.cs
  70. 2
      src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj
  71. 2
      src/csharp/Grpc.Core.Tests/NUnitMain.cs
  72. 4
      src/csharp/Grpc.Core.Tests/SanityTest.cs
  73. 4
      src/csharp/Grpc.Core/Internal/NativeExtension.cs
  74. 2
      src/csharp/Grpc.Examples.MathClient/Grpc.Examples.MathClient.csproj
  75. 2
      src/csharp/Grpc.Examples.MathServer/Grpc.Examples.MathServer.csproj
  76. 2
      src/csharp/Grpc.Examples.Tests/Grpc.Examples.Tests.csproj
  77. 2
      src/csharp/Grpc.Examples.Tests/NUnitMain.cs
  78. 2
      src/csharp/Grpc.Examples/Grpc.Examples.csproj
  79. 2
      src/csharp/Grpc.HealthCheck.Tests/Grpc.HealthCheck.Tests.csproj
  80. 2
      src/csharp/Grpc.HealthCheck.Tests/NUnitMain.cs
  81. 2
      src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj
  82. 2
      src/csharp/Grpc.IntegrationTesting.QpsWorker/Grpc.IntegrationTesting.QpsWorker.csproj
  83. 4
      src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj
  84. 2
      src/csharp/Grpc.IntegrationTesting.StressClient/Grpc.IntegrationTesting.StressClient.csproj
  85. 2
      src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj
  86. 2
      src/csharp/Grpc.IntegrationTesting/NUnitMain.cs
  87. 2
      src/csharp/Grpc.Microbenchmarks/Grpc.Microbenchmarks.csproj
  88. 2
      src/csharp/Grpc.Reflection.Tests/Grpc.Reflection.Tests.csproj
  89. 2
      src/csharp/Grpc.Reflection.Tests/NUnitMain.cs
  90. 2
      src/csharp/Grpc.Tools.Tests/Grpc.Tools.Tests.csproj
  91. 2
      src/csharp/Grpc.Tools.Tests/NUnitMain.cs
  92. 4
      src/php/ext/grpc/php_grpc.c
  93. 7
      src/proto/grpc/channelz/BUILD
  94. 1
      src/proto/grpc/health/v1/BUILD
  95. 3
      src/proto/grpc/testing/compiler_test.proto
  96. 2
      src/python/grpcio/grpc/__init__.py
  97. 1
      src/python/grpcio/grpc/_cython/BUILD.bazel
  98. 69
      src/python/grpcio/grpc/_cython/_cygrpc/channelz.pyx.pxi
  99. 10
      src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
  100. 1
      src/python/grpcio/grpc/_cython/cygrpc.pyx
  101. Some files were not shown because too many files have changed in this diff Show More

@ -81,7 +81,7 @@ How to get your contributions merged smoothly and quickly.
copyright holder for the contribution (yourself, if you are signing the
individual CLA, or your company, for corporate CLAs) in the same PR as your
contribution. This needs to be done only once, for each company, or
individual.
individual. Please keep this file in alphabetical order.
- Maintain **clean commit history** and use **meaningful commit messages**.
PRs with messy commit history are difficult to review and won't be merged.

@ -1,45 +1,59 @@
workspace(name="com_github_grpc_grpc")
workspace(name = "com_github_grpc_grpc")
load("//bazel:grpc_deps.bzl", "grpc_deps", "grpc_test_only_deps")
grpc_deps()
grpc_test_only_deps()
register_execution_platforms(
"//third_party/toolchains:all",
)
register_toolchains(
"//third_party/toolchains:all",
)
new_http_archive(
name="cython",
sha256="d68138a2381afbdd0876c3cb2a22389043fa01c4badede1228ee073032b07a27",
urls=[
name = "cython",
build_file = "//third_party:cython.BUILD",
sha256 = "d68138a2381afbdd0876c3cb2a22389043fa01c4badede1228ee073032b07a27",
strip_prefix = "cython-c2b80d87658a8525ce091cbe146cb7eaa29fed5c",
urls = [
"https://github.com/cython/cython/archive/c2b80d87658a8525ce091cbe146cb7eaa29fed5c.tar.gz",
],
strip_prefix="cython-c2b80d87658a8525ce091cbe146cb7eaa29fed5c",
build_file="//third_party:cython.BUILD",
)
load("//third_party/py:python_configure.bzl", "python_configure")
python_configure(name="local_config_python")
python_configure(name = "local_config_python")
git_repository(
name="io_bazel_rules_python",
remote="https://github.com/bazelbuild/rules_python.git",
commit="8b5d0683a7d878b28fffe464779c8a53659fc645",
name = "io_bazel_rules_python",
commit = "8b5d0683a7d878b28fffe464779c8a53659fc645",
remote = "https://github.com/bazelbuild/rules_python.git",
)
load("@io_bazel_rules_python//python:pip.bzl", "pip_repositories", "pip_import")
pip_repositories()
pip_import(
name="grpc_python_dependencies",
requirements="//:requirements.bazel.txt",
name = "grpc_python_dependencies",
requirements = "//:requirements.bazel.txt",
)
load("@grpc_python_dependencies//:requirements.bzl", "pip_install")
pip_install()
# NOTE(https://github.com/pubref/rules_protobuf/pull/196): Switch to upstream repo after this gets merged.
git_repository(
name="org_pubref_rules_protobuf",
remote="https://github.com/ghostwriternr/rules_protobuf",
tag="v0.8.2.1-alpha",
name = "org_pubref_rules_protobuf",
remote = "https://github.com/ghostwriternr/rules_protobuf",
tag = "v0.8.2.1-alpha",
)
load("@org_pubref_rules_protobuf//python:rules.bzl", "py_proto_repositories")
py_proto_repositories()

@ -131,7 +131,7 @@ def grpc_proto_library(
generate_mocks = generate_mocks,
)
def grpc_cc_test(name, srcs = [], deps = [], external_deps = [], args = [], data = [], uses_polling = True, language = "C++", size = "medium", timeout = "moderate", tags = []):
def grpc_cc_test(name, srcs = [], deps = [], external_deps = [], args = [], data = [], uses_polling = True, language = "C++", size = "medium", timeout = "moderate", tags = [], exec_compatible_with = []):
copts = []
if language.upper() == "C":
copts = if_not_windows(["-std=c99"])
@ -145,6 +145,7 @@ def grpc_cc_test(name, srcs = [], deps = [], external_deps = [], args = [], data
"linkopts": if_not_windows(["-pthread"]),
"size": size,
"timeout": timeout,
"exec_compatible_with": exec_compatible_with,
}
if uses_polling:
native.cc_test(testonly = True, tags = ["manual"], **args)
@ -162,6 +163,7 @@ def grpc_cc_test(name, srcs = [], deps = [], external_deps = [], args = [], data
"$(location %s)" % name,
] + args["args"],
tags = tags,
exec_compatible_with = exec_compatible_with,
)
else:
native.cc_test(**args)

@ -0,0 +1,22 @@
# gRPC Release Schedule
Below is the release schedule for gRPC [Java](https://github.com/grpc/grpc-java/releases), [Go](https://github.com/grpc/grpc-go/releases) and [Core](https://github.com/grpc/grpc/releases) and its dependent languages C++, C#, Objective-C, PHP, Python and Ruby.
Releases are scheduled every six weeks on Tuesdays on a best effort basis. In some unavoidable situations a release may be delayed or a language may skip a release altogether and do the next release to catch up with other languages. See the past releases in the links above. A six-week cycle gives us a good balance between delivering new features/fixes quickly and keeping the release overhead low.
Releases are cut from release branches. For Core and Java repos, the release branch is cut two weeks before the scheduled release date. For Go, the branch is cut just before the release. An RC (release candidate) is published for Core and its dependent languages just after the branch cut. This RC is later promoted to release version if no further changes are made to the release branch. We do our best to keep head of master branch stable at all times regardless of release schedule. Daily build packages from master branch for C#, PHP, Python, Ruby and Protoc plugins are published on [packages.grpc.io](https://packages.grpc.io/). If you depend on gRPC in production we recommend to set up your CI system to test the RCs and, if possible, the daily builds.
Names of gRPC releases are [here](https://github.com/grpc/grpc/blob/master/doc/g_stands_for.md).
Release |Scheduled Branch Cut|Scheduled Release Date
--------|--------------------|-------------
v1.17.0 |Nov 19, 2018 |Dec 4, 2018
v1.18.0 |Jan 2, 2019 |Jan 15, 2019
v1.19.0 |Feb 12, 2019 |Feb 26, 2019
v1.20.0 |Mar 26, 2019 |Apr 9, 2019
v1.21.0 |May 7, 2019 |May 21, 2019
v1.22.0 |Jun 18, 2019 |Jul 2, 2019
v1.23.0 |Jul 30, 2019 |Aug 13, 2019
v1.24.0 |Sept 10, 2019 |Sept 24, 2019
v1.25.0 |Oct 22, 2019 |Nov 5, 2019
v1.26.0 |Dec 3, 2019 |Dec 17, 2019

@ -51,7 +51,9 @@ but may not be supported in other languages:
- `ipv6:address[:port][,address[:port],...]` -- IPv6 addresses
- Can specify multiple comma-delimited addresses of the form `address[:port]`:
- `address` is the IPv6 address to use.
- `address` is the IPv6 address to use. To use with a `port` the `address`
must enclosed in literal square brakets (`[` and `]`). Example:
`ipv6:[2607:f8b0:400e:c00::ef]:443` or `ipv6:[::]:1234`
- `port` is the port to use. If not specified, 443 is used.
In the future, additional schemes such as `etcd` could be added.

@ -19,6 +19,7 @@ import sys
PYTHON_FOLDER = os.path.join(os.path.dirname(os.path.realpath(__file__)),
'..', '..', '..', 'src', 'python')
sys.path.insert(0, os.path.join(PYTHON_FOLDER, 'grpcio'))
sys.path.insert(0, os.path.join(PYTHON_FOLDER, 'grpcio_channelz'))
sys.path.insert(0, os.path.join(PYTHON_FOLDER, 'grpcio_health_checking'))
sys.path.insert(0, os.path.join(PYTHON_FOLDER, 'grpcio_reflection'))
sys.path.insert(0, os.path.join(PYTHON_FOLDER, 'grpcio_testing'))
@ -63,6 +64,8 @@ autodoc_default_options = {
autodoc_mock_imports = [
'grpc._cython',
'grpc_channelz.v1.channelz_pb2',
'grpc_channelz.v1.channelz_pb2_grpc',
'grpc_health.v1.health_pb2',
'grpc_health.v1.health_pb2_grpc',
'grpc_reflection.v1alpha.reflection_pb2',

@ -0,0 +1,12 @@
gRPC Channelz
====================
What is gRPC Channelz?
---------------------------------------------
Design Document `gRPC Channelz <https://github.com/grpc/proposal/blob/master/A14-channelz.md>`_
Module Contents
---------------
.. automodule:: grpc_channelz.v1.channelz

@ -10,6 +10,7 @@ API Reference
:caption: Contents:
grpc
grpc_channelz
grpc_health_checking
grpc_reflection
grpc_testing

@ -14,6 +14,7 @@
"""The Python implementation of the GRPC helloworld.Greeter client."""
from __future__ import print_function
import logging
import grpc
@ -32,4 +33,5 @@ def run():
if __name__ == '__main__':
logging.basicConfig()
run()

@ -14,6 +14,7 @@
"""The Python implementation of the GRPC helloworld.Greeter client with channel options and call timeout parameters."""
from __future__ import print_function
import logging
import grpc
@ -41,4 +42,5 @@ def run():
if __name__ == '__main__':
logging.basicConfig()
run()

@ -15,6 +15,7 @@
from concurrent import futures
import time
import logging
import grpc
@ -43,4 +44,5 @@ def serve():
if __name__ == '__main__':
logging.basicConfig()
serve()

@ -15,6 +15,7 @@
from concurrent import futures
import time
import logging
import grpc
from grpc_reflection.v1alpha import reflection
@ -49,4 +50,5 @@ def serve():
if __name__ == '__main__':
logging.basicConfig()
serve()

@ -14,6 +14,7 @@
"""The Python implementation of the gRPC helloworld.Greeter client."""
from __future__ import print_function
import logging
import grpc
@ -39,4 +40,5 @@ def run():
if __name__ == '__main__':
logging.basicConfig()
run()

@ -14,6 +14,7 @@
"""The Python implementation of the GRPC helloworld.Greeter client."""
from __future__ import print_function
import logging
import grpc
@ -37,4 +38,5 @@ def run():
if __name__ == '__main__':
logging.basicConfig()
run()

@ -15,6 +15,7 @@
from concurrent import futures
import time
import logging
import grpc
@ -49,4 +50,5 @@ def serve():
if __name__ == '__main__':
logging.basicConfig()
serve()

@ -17,6 +17,7 @@ from __future__ import print_function
import random
import time
import logging
import grpc
@ -126,4 +127,5 @@ def run():
if __name__ == '__main__':
logging.basicConfig()
run()

@ -16,6 +16,7 @@
from concurrent import futures
import time
import math
import logging
import grpc
@ -136,4 +137,5 @@ def serve():
if __name__ == '__main__':
logging.basicConfig()
serve()

@ -16,6 +16,7 @@
from __future__ import print_function
import random
import logging
import grpc
@ -116,4 +117,5 @@ def run():
if __name__ == '__main__':
logging.basicConfig()
run()

@ -16,6 +16,7 @@
from concurrent import futures
import time
import math
import logging
import grpc
@ -126,4 +127,5 @@ def serve():
if __name__ == '__main__':
logging.basicConfig()
serve()

@ -293,7 +293,7 @@ typedef struct {
"grpc.max_channel_trace_event_memory_per_node"
/** If non-zero, gRPC library will track stats and information at at per channel
* level. Disabling channelz naturally disables channel tracing. The default
* is for channelz to be disabled. */
* is for channelz to be enabled. */
#define GRPC_ARG_ENABLE_CHANNELZ "grpc.enable_channelz"
/** If non-zero, Cronet transport will coalesce packets to fewer frames
* when possible. */
@ -350,6 +350,11 @@ typedef struct {
/** If set, inhibits health checking (which may be enabled via the
* service config.) */
#define GRPC_ARG_INHIBIT_HEALTH_CHECKING "grpc.inhibit_health_checking"
/** If set, determines the number of milliseconds that the c-ares based
* DNS resolver will wait on queries before cancelling them. The default value
* is 10000. Setting this to "0" will disable c-ares query timeouts
* entirely. */
#define GRPC_ARG_DNS_ARES_QUERY_TIMEOUT_MS "grpc.dns_ares_query_timeout"
/** \} */
/** Result of a grpc call. If the caller satisfies the prerequisites of a

@ -24,6 +24,7 @@
#include <grpcpp/support/async_stream.h>
#include <grpcpp/support/async_unary_call.h>
#include <grpcpp/support/byte_buffer.h>
#include <grpcpp/support/client_callback.h>
#include <grpcpp/support/status.h>
namespace grpc {
@ -76,6 +77,10 @@ class GenericStub final {
const ByteBuffer* request, ByteBuffer* response,
std::function<void(Status)> on_completion);
void PrepareBidiStreamingCall(
ClientContext* context, const grpc::string& method,
experimental::ClientBidiReactor<ByteBuffer, ByteBuffer>* reactor);
private:
GenericStub* stub_;
};

@ -45,8 +45,10 @@ template <class ServiceType, class RequestType, class ResponseType>
class RpcMethodHandler;
template <class ServiceType, class RequestType, class ResponseType>
class ServerStreamingHandler;
template <class ServiceType, class RequestType, class ResponseType>
template <class RequestType, class ResponseType>
class CallbackUnaryHandler;
template <class RequestType, class ResponseType>
class CallbackServerStreamingHandler;
template <StatusCode code>
class ErrorMethodHandler;
template <class R>
@ -156,8 +158,10 @@ class ByteBuffer final {
friend class internal::RpcMethodHandler;
template <class ServiceType, class RequestType, class ResponseType>
friend class internal::ServerStreamingHandler;
template <class ServiceType, class RequestType, class ResponseType>
template <class RequestType, class ResponseType>
friend class internal::CallbackUnaryHandler;
template <class RequestType, class ResponseType>
friend class ::grpc::internal::CallbackServerStreamingHandler;
template <StatusCode code>
friend class internal::ErrorMethodHandler;
template <class R>

@ -32,6 +32,8 @@ namespace grpc {
namespace internal {
/// An exception-safe way of invoking a user-specified callback function
// TODO(vjpai): decide whether it is better for this to take a const lvalue
// parameter or an rvalue parameter, or if it even matters
template <class Func, class... Args>
void CatchingCallback(Func&& func, Args&&... args) {
#if GRPC_ALLOW_EXCEPTIONS
@ -45,6 +47,20 @@ void CatchingCallback(Func&& func, Args&&... args) {
#endif // GRPC_ALLOW_EXCEPTIONS
}
template <class ReturnType, class Func, class... Args>
ReturnType* CatchingReactorCreator(Func&& func, Args&&... args) {
#if GRPC_ALLOW_EXCEPTIONS
try {
return func(std::forward<Args>(args)...);
} catch (...) {
// fail the RPC, don't crash the library
return nullptr;
}
#else // GRPC_ALLOW_EXCEPTIONS
return func(std::forward<Args>(args)...);
#endif // GRPC_ALLOW_EXCEPTIONS
}
// The contract on these tags is that they are single-shot. They must be
// constructed and then fired at exactly one point. There is no expectation
// that they can be reused without reconstruction.
@ -145,18 +161,19 @@ class CallbackWithSuccessTag
// or on a tag that has been Set before unless the tag has been cleared.
void Set(grpc_call* call, std::function<void(bool)> f,
CompletionQueueTag* ops) {
GPR_CODEGEN_ASSERT(call_ == nullptr);
g_core_codegen_interface->grpc_call_ref(call);
call_ = call;
func_ = std::move(f);
ops_ = ops;
g_core_codegen_interface->grpc_call_ref(call);
functor_run = &CallbackWithSuccessTag::StaticRun;
}
void Clear() {
if (call_ != nullptr) {
func_ = nullptr;
grpc_call* call = call_;
call_ = nullptr;
func_ = nullptr;
g_core_codegen_interface->grpc_call_unref(call);
}
}
@ -182,11 +199,11 @@ class CallbackWithSuccessTag
}
void Run(bool ok) {
void* ignored = ops_;
bool new_ok = ok;
// Allow a "false" return value from FinalizeResult to silence the
// callback, just as it silences a CQ tag in the async cases
bool do_callback = ops_->FinalizeResult(&ignored, &new_ok);
GPR_CODEGEN_ASSERT(ignored == ops_);
auto* ops = ops_;
bool do_callback = ops_->FinalizeResult(&ignored, &ok);
GPR_CODEGEN_ASSERT(ignored == ops);
if (do_callback) {
CatchingCallback(func_, ok);

@ -53,6 +53,12 @@ template <class W, class R>
class ClientAsyncReaderWriterFactory;
template <class R>
class ClientAsyncResponseReaderFactory;
template <class W, class R>
class ClientCallbackReaderWriterFactory;
template <class R>
class ClientCallbackReaderFactory;
template <class W>
class ClientCallbackWriterFactory;
class InterceptedChannel;
} // namespace internal
@ -106,6 +112,12 @@ class ChannelInterface {
friend class ::grpc::internal::ClientAsyncReaderWriterFactory;
template <class R>
friend class ::grpc::internal::ClientAsyncResponseReaderFactory;
template <class W, class R>
friend class ::grpc::internal::ClientCallbackReaderWriterFactory;
template <class R>
friend class ::grpc::internal::ClientCallbackReaderFactory;
template <class W>
friend class ::grpc::internal::ClientCallbackWriterFactory;
template <class InputMessage, class OutputMessage>
friend class ::grpc::internal::BlockingUnaryCallImpl;
template <class InputMessage, class OutputMessage>

@ -22,6 +22,7 @@
#include <functional>
#include <grpcpp/impl/codegen/call.h>
#include <grpcpp/impl/codegen/call_op_set.h>
#include <grpcpp/impl/codegen/callback_common.h>
#include <grpcpp/impl/codegen/channel_interface.h>
#include <grpcpp/impl/codegen/config.h>
@ -88,6 +89,646 @@ class CallbackUnaryCallImpl {
call.PerformOps(ops);
}
};
} // namespace internal
namespace experimental {
// Forward declarations
template <class Request, class Response>
class ClientBidiReactor;
template <class Response>
class ClientReadReactor;
template <class Request>
class ClientWriteReactor;
// NOTE: The streaming objects are not actually implemented in the public API.
// These interfaces are provided for mocking only. Typical applications
// will interact exclusively with the reactors that they define.
template <class Request, class Response>
class ClientCallbackReaderWriter {
public:
virtual ~ClientCallbackReaderWriter() {}
virtual void StartCall() = 0;
virtual void Write(const Request* req, WriteOptions options) = 0;
virtual void WritesDone() = 0;
virtual void Read(Response* resp) = 0;
protected:
void BindReactor(ClientBidiReactor<Request, Response>* reactor) {
reactor->BindStream(this);
}
};
template <class Response>
class ClientCallbackReader {
public:
virtual ~ClientCallbackReader() {}
virtual void StartCall() = 0;
virtual void Read(Response* resp) = 0;
protected:
void BindReactor(ClientReadReactor<Response>* reactor) {
reactor->BindReader(this);
}
};
template <class Request>
class ClientCallbackWriter {
public:
virtual ~ClientCallbackWriter() {}
virtual void StartCall() = 0;
void Write(const Request* req) { Write(req, WriteOptions()); }
virtual void Write(const Request* req, WriteOptions options) = 0;
void WriteLast(const Request* req, WriteOptions options) {
Write(req, options.set_last_message());
}
virtual void WritesDone() = 0;
protected:
void BindReactor(ClientWriteReactor<Request>* reactor) {
reactor->BindWriter(this);
}
};
// The user must implement this reactor interface with reactions to each event
// type that gets called by the library. An empty reaction is provided by
// default
template <class Request, class Response>
class ClientBidiReactor {
public:
virtual ~ClientBidiReactor() {}
virtual void OnDone(const Status& s) {}
virtual void OnReadInitialMetadataDone(bool ok) {}
virtual void OnReadDone(bool ok) {}
virtual void OnWriteDone(bool ok) {}
virtual void OnWritesDoneDone(bool ok) {}
void StartCall() { stream_->StartCall(); }
void StartRead(Response* resp) { stream_->Read(resp); }
void StartWrite(const Request* req) { StartWrite(req, WriteOptions()); }
void StartWrite(const Request* req, WriteOptions options) {
stream_->Write(req, std::move(options));
}
void StartWriteLast(const Request* req, WriteOptions options) {
StartWrite(req, std::move(options.set_last_message()));
}
void StartWritesDone() { stream_->WritesDone(); }
private:
friend class ClientCallbackReaderWriter<Request, Response>;
void BindStream(ClientCallbackReaderWriter<Request, Response>* stream) {
stream_ = stream;
}
ClientCallbackReaderWriter<Request, Response>* stream_;
};
template <class Response>
class ClientReadReactor {
public:
virtual ~ClientReadReactor() {}
virtual void OnDone(const Status& s) {}
virtual void OnReadInitialMetadataDone(bool ok) {}
virtual void OnReadDone(bool ok) {}
void StartCall() { reader_->StartCall(); }
void StartRead(Response* resp) { reader_->Read(resp); }
private:
friend class ClientCallbackReader<Response>;
void BindReader(ClientCallbackReader<Response>* reader) { reader_ = reader; }
ClientCallbackReader<Response>* reader_;
};
template <class Request>
class ClientWriteReactor {
public:
virtual ~ClientWriteReactor() {}
virtual void OnDone(const Status& s) {}
virtual void OnReadInitialMetadataDone(bool ok) {}
virtual void OnWriteDone(bool ok) {}
virtual void OnWritesDoneDone(bool ok) {}
void StartCall() { writer_->StartCall(); }
void StartWrite(const Request* req) { StartWrite(req, WriteOptions()); }
void StartWrite(const Request* req, WriteOptions options) {
writer_->Write(req, std::move(options));
}
void StartWriteLast(const Request* req, WriteOptions options) {
StartWrite(req, std::move(options.set_last_message()));
}
void StartWritesDone() { writer_->WritesDone(); }
private:
friend class ClientCallbackWriter<Request>;
void BindWriter(ClientCallbackWriter<Request>* writer) { writer_ = writer; }
ClientCallbackWriter<Request>* writer_;
};
} // namespace experimental
namespace internal {
// Forward declare factory classes for friendship
template <class Request, class Response>
class ClientCallbackReaderWriterFactory;
template <class Response>
class ClientCallbackReaderFactory;
template <class Request>
class ClientCallbackWriterFactory;
template <class Request, class Response>
class ClientCallbackReaderWriterImpl
: public ::grpc::experimental::ClientCallbackReaderWriter<Request,
Response> {
public:
// always allocated against a call arena, no memory free required
static void operator delete(void* ptr, std::size_t size) {
assert(size == sizeof(ClientCallbackReaderWriterImpl));
}
// This operator should never be called as the memory should be freed as part
// of the arena destruction. It only exists to provide a matching operator
// delete to the operator new so that some compilers will not complain (see
// https://github.com/grpc/grpc/issues/11301) Note at the time of adding this
// there are no tests catching the compiler warning.
static void operator delete(void*, void*) { assert(0); }
void MaybeFinish() {
if (--callbacks_outstanding_ == 0) {
reactor_->OnDone(finish_status_);
auto* call = call_.call();
this->~ClientCallbackReaderWriterImpl();
g_core_codegen_interface->grpc_call_unref(call);
}
}
void StartCall() override {
// This call initiates two batches, plus any backlog, each with a callback
// 1. Send initial metadata (unless corked) + recv initial metadata
// 2. Any read backlog
// 3. Recv trailing metadata, on_completion callback
// 4. Any write backlog
started_ = true;
start_tag_.Set(call_.call(),
[this](bool ok) {
reactor_->OnReadInitialMetadataDone(ok);
MaybeFinish();
},
&start_ops_);
if (!start_corked_) {
start_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
context_->initial_metadata_flags());
}
start_ops_.RecvInitialMetadata(context_);
start_ops_.set_core_cq_tag(&start_tag_);
call_.PerformOps(&start_ops_);
// Also set up the read and write tags so that they don't have to be set up
// each time
write_tag_.Set(call_.call(),
[this](bool ok) {
reactor_->OnWriteDone(ok);
MaybeFinish();
},
&write_ops_);
write_ops_.set_core_cq_tag(&write_tag_);
read_tag_.Set(call_.call(),
[this](bool ok) {
reactor_->OnReadDone(ok);
MaybeFinish();
},
&read_ops_);
read_ops_.set_core_cq_tag(&read_tag_);
if (read_ops_at_start_) {
call_.PerformOps(&read_ops_);
}
finish_tag_.Set(call_.call(), [this](bool ok) { MaybeFinish(); },
&finish_ops_);
finish_ops_.ClientRecvStatus(context_, &finish_status_);
finish_ops_.set_core_cq_tag(&finish_tag_);
call_.PerformOps(&finish_ops_);
if (write_ops_at_start_) {
call_.PerformOps(&write_ops_);
}
if (writes_done_ops_at_start_) {
call_.PerformOps(&writes_done_ops_);
}
}
void Read(Response* msg) override {
read_ops_.RecvMessage(msg);
callbacks_outstanding_++;
if (started_) {
call_.PerformOps(&read_ops_);
} else {
read_ops_at_start_ = true;
}
}
void Write(const Request* msg, WriteOptions options) override {
if (start_corked_) {
write_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
context_->initial_metadata_flags());
start_corked_ = false;
}
// TODO(vjpai): don't assert
GPR_CODEGEN_ASSERT(write_ops_.SendMessage(*msg).ok());
if (options.is_last_message()) {
options.set_buffer_hint();
write_ops_.ClientSendClose();
}
callbacks_outstanding_++;
if (started_) {
call_.PerformOps(&write_ops_);
} else {
write_ops_at_start_ = true;
}
}
void WritesDone() override {
if (start_corked_) {
writes_done_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
context_->initial_metadata_flags());
start_corked_ = false;
}
writes_done_ops_.ClientSendClose();
writes_done_tag_.Set(call_.call(),
[this](bool ok) {
reactor_->OnWritesDoneDone(ok);
MaybeFinish();
},
&writes_done_ops_);
writes_done_ops_.set_core_cq_tag(&writes_done_tag_);
callbacks_outstanding_++;
if (started_) {
call_.PerformOps(&writes_done_ops_);
} else {
writes_done_ops_at_start_ = true;
}
}
private:
friend class ClientCallbackReaderWriterFactory<Request, Response>;
ClientCallbackReaderWriterImpl(
Call call, ClientContext* context,
::grpc::experimental::ClientBidiReactor<Request, Response>* reactor)
: context_(context),
call_(call),
reactor_(reactor),
start_corked_(context_->initial_metadata_corked_) {
this->BindReactor(reactor);
}
ClientContext* context_;
Call call_;
::grpc::experimental::ClientBidiReactor<Request, Response>* reactor_;
CallOpSet<CallOpSendInitialMetadata, CallOpRecvInitialMetadata> start_ops_;
CallbackWithSuccessTag start_tag_;
bool start_corked_;
CallOpSet<CallOpClientRecvStatus> finish_ops_;
CallbackWithSuccessTag finish_tag_;
Status finish_status_;
CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage, CallOpClientSendClose>
write_ops_;
CallbackWithSuccessTag write_tag_;
bool write_ops_at_start_{false};
CallOpSet<CallOpSendInitialMetadata, CallOpClientSendClose> writes_done_ops_;
CallbackWithSuccessTag writes_done_tag_;
bool writes_done_ops_at_start_{false};
CallOpSet<CallOpRecvMessage<Response>> read_ops_;
CallbackWithSuccessTag read_tag_;
bool read_ops_at_start_{false};
// Minimum of 2 outstanding callbacks to pre-register for start and finish
std::atomic_int callbacks_outstanding_{2};
bool started_{false};
};
template <class Request, class Response>
class ClientCallbackReaderWriterFactory {
public:
static void Create(
ChannelInterface* channel, const ::grpc::internal::RpcMethod& method,
ClientContext* context,
::grpc::experimental::ClientBidiReactor<Request, Response>* reactor) {
Call call = channel->CreateCall(method, context, channel->CallbackCQ());
g_core_codegen_interface->grpc_call_ref(call.call());
new (g_core_codegen_interface->grpc_call_arena_alloc(
call.call(), sizeof(ClientCallbackReaderWriterImpl<Request, Response>)))
ClientCallbackReaderWriterImpl<Request, Response>(call, context,
reactor);
}
};
template <class Response>
class ClientCallbackReaderImpl
: public ::grpc::experimental::ClientCallbackReader<Response> {
public:
// always allocated against a call arena, no memory free required
static void operator delete(void* ptr, std::size_t size) {
assert(size == sizeof(ClientCallbackReaderImpl));
}
// This operator should never be called as the memory should be freed as part
// of the arena destruction. It only exists to provide a matching operator
// delete to the operator new so that some compilers will not complain (see
// https://github.com/grpc/grpc/issues/11301) Note at the time of adding this
// there are no tests catching the compiler warning.
static void operator delete(void*, void*) { assert(0); }
void MaybeFinish() {
if (--callbacks_outstanding_ == 0) {
reactor_->OnDone(finish_status_);
auto* call = call_.call();
this->~ClientCallbackReaderImpl();
g_core_codegen_interface->grpc_call_unref(call);
}
}
void StartCall() override {
// This call initiates two batches, plus any backlog, each with a callback
// 1. Send initial metadata (unless corked) + recv initial metadata
// 2. Any backlog
// 3. Recv trailing metadata, on_completion callback
started_ = true;
start_tag_.Set(call_.call(),
[this](bool ok) {
reactor_->OnReadInitialMetadataDone(ok);
MaybeFinish();
},
&start_ops_);
start_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
context_->initial_metadata_flags());
start_ops_.RecvInitialMetadata(context_);
start_ops_.set_core_cq_tag(&start_tag_);
call_.PerformOps(&start_ops_);
// Also set up the read tag so it doesn't have to be set up each time
read_tag_.Set(call_.call(),
[this](bool ok) {
reactor_->OnReadDone(ok);
MaybeFinish();
},
&read_ops_);
read_ops_.set_core_cq_tag(&read_tag_);
if (read_ops_at_start_) {
call_.PerformOps(&read_ops_);
}
finish_tag_.Set(call_.call(), [this](bool ok) { MaybeFinish(); },
&finish_ops_);
finish_ops_.ClientRecvStatus(context_, &finish_status_);
finish_ops_.set_core_cq_tag(&finish_tag_);
call_.PerformOps(&finish_ops_);
}
void Read(Response* msg) override {
read_ops_.RecvMessage(msg);
callbacks_outstanding_++;
if (started_) {
call_.PerformOps(&read_ops_);
} else {
read_ops_at_start_ = true;
}
}
private:
friend class ClientCallbackReaderFactory<Response>;
template <class Request>
ClientCallbackReaderImpl(
Call call, ClientContext* context, Request* request,
::grpc::experimental::ClientReadReactor<Response>* reactor)
: context_(context), call_(call), reactor_(reactor) {
this->BindReactor(reactor);
// TODO(vjpai): don't assert
GPR_CODEGEN_ASSERT(start_ops_.SendMessage(*request).ok());
start_ops_.ClientSendClose();
}
ClientContext* context_;
Call call_;
::grpc::experimental::ClientReadReactor<Response>* reactor_;
CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage, CallOpClientSendClose,
CallOpRecvInitialMetadata>
start_ops_;
CallbackWithSuccessTag start_tag_;
CallOpSet<CallOpClientRecvStatus> finish_ops_;
CallbackWithSuccessTag finish_tag_;
Status finish_status_;
CallOpSet<CallOpRecvMessage<Response>> read_ops_;
CallbackWithSuccessTag read_tag_;
bool read_ops_at_start_{false};
// Minimum of 2 outstanding callbacks to pre-register for start and finish
std::atomic_int callbacks_outstanding_{2};
bool started_{false};
};
template <class Response>
class ClientCallbackReaderFactory {
public:
template <class Request>
static void Create(
ChannelInterface* channel, const ::grpc::internal::RpcMethod& method,
ClientContext* context, const Request* request,
::grpc::experimental::ClientReadReactor<Response>* reactor) {
Call call = channel->CreateCall(method, context, channel->CallbackCQ());
g_core_codegen_interface->grpc_call_ref(call.call());
new (g_core_codegen_interface->grpc_call_arena_alloc(
call.call(), sizeof(ClientCallbackReaderImpl<Response>)))
ClientCallbackReaderImpl<Response>(call, context, request, reactor);
}
};
template <class Request>
class ClientCallbackWriterImpl
: public ::grpc::experimental::ClientCallbackWriter<Request> {
public:
// always allocated against a call arena, no memory free required
static void operator delete(void* ptr, std::size_t size) {
assert(size == sizeof(ClientCallbackWriterImpl));
}
// This operator should never be called as the memory should be freed as part
// of the arena destruction. It only exists to provide a matching operator
// delete to the operator new so that some compilers will not complain (see
// https://github.com/grpc/grpc/issues/11301) Note at the time of adding this
// there are no tests catching the compiler warning.
static void operator delete(void*, void*) { assert(0); }
void MaybeFinish() {
if (--callbacks_outstanding_ == 0) {
reactor_->OnDone(finish_status_);
auto* call = call_.call();
this->~ClientCallbackWriterImpl();
g_core_codegen_interface->grpc_call_unref(call);
}
}
void StartCall() override {
// This call initiates two batches, plus any backlog, each with a callback
// 1. Send initial metadata (unless corked) + recv initial metadata
// 2. Recv trailing metadata, on_completion callback
// 3. Any backlog
started_ = true;
start_tag_.Set(call_.call(),
[this](bool ok) {
reactor_->OnReadInitialMetadataDone(ok);
MaybeFinish();
},
&start_ops_);
if (!start_corked_) {
start_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
context_->initial_metadata_flags());
}
start_ops_.RecvInitialMetadata(context_);
start_ops_.set_core_cq_tag(&start_tag_);
call_.PerformOps(&start_ops_);
// Also set up the read and write tags so that they don't have to be set up
// each time
write_tag_.Set(call_.call(),
[this](bool ok) {
reactor_->OnWriteDone(ok);
MaybeFinish();
},
&write_ops_);
write_ops_.set_core_cq_tag(&write_tag_);
finish_tag_.Set(call_.call(), [this](bool ok) { MaybeFinish(); },
&finish_ops_);
finish_ops_.ClientRecvStatus(context_, &finish_status_);
finish_ops_.set_core_cq_tag(&finish_tag_);
call_.PerformOps(&finish_ops_);
if (write_ops_at_start_) {
call_.PerformOps(&write_ops_);
}
if (writes_done_ops_at_start_) {
call_.PerformOps(&writes_done_ops_);
}
}
void Write(const Request* msg, WriteOptions options) override {
if (start_corked_) {
write_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
context_->initial_metadata_flags());
start_corked_ = false;
}
// TODO(vjpai): don't assert
GPR_CODEGEN_ASSERT(write_ops_.SendMessage(*msg).ok());
if (options.is_last_message()) {
options.set_buffer_hint();
write_ops_.ClientSendClose();
}
callbacks_outstanding_++;
if (started_) {
call_.PerformOps(&write_ops_);
} else {
write_ops_at_start_ = true;
}
}
void WritesDone() override {
if (start_corked_) {
writes_done_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
context_->initial_metadata_flags());
start_corked_ = false;
}
writes_done_ops_.ClientSendClose();
writes_done_tag_.Set(call_.call(),
[this](bool ok) {
reactor_->OnWritesDoneDone(ok);
MaybeFinish();
},
&writes_done_ops_);
writes_done_ops_.set_core_cq_tag(&writes_done_tag_);
callbacks_outstanding_++;
if (started_) {
call_.PerformOps(&writes_done_ops_);
} else {
writes_done_ops_at_start_ = true;
}
}
private:
friend class ClientCallbackWriterFactory<Request>;
template <class Response>
ClientCallbackWriterImpl(
Call call, ClientContext* context, Response* response,
::grpc::experimental::ClientWriteReactor<Request>* reactor)
: context_(context),
call_(call),
reactor_(reactor),
start_corked_(context_->initial_metadata_corked_) {
this->BindReactor(reactor);
finish_ops_.RecvMessage(response);
finish_ops_.AllowNoMessage();
}
ClientContext* context_;
Call call_;
::grpc::experimental::ClientWriteReactor<Request>* reactor_;
CallOpSet<CallOpSendInitialMetadata, CallOpRecvInitialMetadata> start_ops_;
CallbackWithSuccessTag start_tag_;
bool start_corked_;
CallOpSet<CallOpGenericRecvMessage, CallOpClientRecvStatus> finish_ops_;
CallbackWithSuccessTag finish_tag_;
Status finish_status_;
CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage, CallOpClientSendClose>
write_ops_;
CallbackWithSuccessTag write_tag_;
bool write_ops_at_start_{false};
CallOpSet<CallOpSendInitialMetadata, CallOpClientSendClose> writes_done_ops_;
CallbackWithSuccessTag writes_done_tag_;
bool writes_done_ops_at_start_{false};
// Minimum of 2 outstanding callbacks to pre-register for start and finish
std::atomic_int callbacks_outstanding_{2};
bool started_{false};
};
template <class Request>
class ClientCallbackWriterFactory {
public:
template <class Response>
static void Create(
ChannelInterface* channel, const ::grpc::internal::RpcMethod& method,
ClientContext* context, Response* response,
::grpc::experimental::ClientWriteReactor<Request>* reactor) {
Call call = channel->CreateCall(method, context, channel->CallbackCQ());
g_core_codegen_interface->grpc_call_ref(call.call());
new (g_core_codegen_interface->grpc_call_arena_alloc(
call.call(), sizeof(ClientCallbackWriterImpl<Request>)))
ClientCallbackWriterImpl<Request>(call, context, response, reactor);
}
};
} // namespace internal
} // namespace grpc

@ -71,6 +71,12 @@ template <class InputMessage, class OutputMessage>
class BlockingUnaryCallImpl;
template <class InputMessage, class OutputMessage>
class CallbackUnaryCallImpl;
template <class Request, class Response>
class ClientCallbackReaderWriterImpl;
template <class Response>
class ClientCallbackReaderImpl;
template <class Request>
class ClientCallbackWriterImpl;
} // namespace internal
template <class R>
@ -394,6 +400,12 @@ class ClientContext {
friend class ::grpc::internal::BlockingUnaryCallImpl;
template <class InputMessage, class OutputMessage>
friend class ::grpc::internal::CallbackUnaryCallImpl;
template <class Request, class Response>
friend class ::grpc::internal::ClientCallbackReaderWriterImpl;
template <class Response>
friend class ::grpc::internal::ClientCallbackReaderImpl;
template <class Request>
friend class ::grpc::internal::ClientCallbackWriterImpl;
// Used by friend class CallOpClientRecvStatus
void set_debug_error_string(const grpc::string& debug_error_string) {

@ -19,7 +19,9 @@
#ifndef GRPCPP_IMPL_CODEGEN_SERVER_CALLBACK_H
#define GRPCPP_IMPL_CODEGEN_SERVER_CALLBACK_H
#include <atomic>
#include <functional>
#include <type_traits>
#include <grpcpp/impl/codegen/call.h>
#include <grpcpp/impl/codegen/call_op_set.h>
@ -32,19 +34,33 @@
namespace grpc {
// forward declarations
// Declare base class of all reactors as internal
namespace internal {
template <class ServiceType, class RequestType, class ResponseType>
class CallbackUnaryHandler;
class ServerReactor {
public:
virtual ~ServerReactor() = default;
virtual void OnDone() {}
virtual void OnCancel() {}
};
} // namespace internal
namespace experimental {
// Forward declarations
template <class Request, class Response>
class ServerReadReactor;
template <class Request, class Response>
class ServerWriteReactor;
template <class Request, class Response>
class ServerBidiReactor;
// For unary RPCs, the exposed controller class is only an interface
// and the actual implementation is an internal class.
class ServerCallbackRpcController {
public:
virtual ~ServerCallbackRpcController() {}
virtual ~ServerCallbackRpcController() = default;
// The method handler must call this function when it is done so that
// the library knows to free its resources
@ -55,18 +71,193 @@ class ServerCallbackRpcController {
virtual void SendInitialMetadata(std::function<void(bool)>) = 0;
};
// NOTE: The actual streaming object classes are provided
// as API only to support mocking. There are no implementations of
// these class interfaces in the API.
template <class Request>
class ServerCallbackReader {
public:
virtual ~ServerCallbackReader() {}
virtual void Finish(Status s) = 0;
virtual void SendInitialMetadata() = 0;
virtual void Read(Request* msg) = 0;
protected:
template <class Response>
void BindReactor(ServerReadReactor<Request, Response>* reactor) {
reactor->BindReader(this);
}
};
template <class Response>
class ServerCallbackWriter {
public:
virtual ~ServerCallbackWriter() {}
virtual void Finish(Status s) = 0;
virtual void SendInitialMetadata() = 0;
virtual void Write(const Response* msg, WriteOptions options) = 0;
virtual void WriteAndFinish(const Response* msg, WriteOptions options,
Status s) {
// Default implementation that can/should be overridden
Write(msg, std::move(options));
Finish(std::move(s));
};
protected:
template <class Request>
void BindReactor(ServerWriteReactor<Request, Response>* reactor) {
reactor->BindWriter(this);
}
};
template <class Request, class Response>
class ServerCallbackReaderWriter {
public:
virtual ~ServerCallbackReaderWriter() {}
virtual void Finish(Status s) = 0;
virtual void SendInitialMetadata() = 0;
virtual void Read(Request* msg) = 0;
virtual void Write(const Response* msg, WriteOptions options) = 0;
virtual void WriteAndFinish(const Response* msg, WriteOptions options,
Status s) {
// Default implementation that can/should be overridden
Write(msg, std::move(options));
Finish(std::move(s));
};
protected:
void BindReactor(ServerBidiReactor<Request, Response>* reactor) {
reactor->BindStream(this);
}
};
// The following classes are reactors that are to be implemented
// by the user, returned as the result of the method handler for
// a callback method, and activated by the call to OnStarted
template <class Request, class Response>
class ServerBidiReactor : public internal::ServerReactor {
public:
~ServerBidiReactor() = default;
virtual void OnStarted(ServerContext*) {}
virtual void OnSendInitialMetadataDone(bool ok) {}
virtual void OnReadDone(bool ok) {}
virtual void OnWriteDone(bool ok) {}
void StartSendInitialMetadata() { stream_->SendInitialMetadata(); }
void StartRead(Request* msg) { stream_->Read(msg); }
void StartWrite(const Response* msg) { StartWrite(msg, WriteOptions()); }
void StartWrite(const Response* msg, WriteOptions options) {
stream_->Write(msg, std::move(options));
}
void StartWriteAndFinish(const Response* msg, WriteOptions options,
Status s) {
stream_->WriteAndFinish(msg, std::move(options), std::move(s));
}
void StartWriteLast(const Response* msg, WriteOptions options) {
StartWrite(msg, std::move(options.set_last_message()));
}
void Finish(Status s) { stream_->Finish(std::move(s)); }
private:
friend class ServerCallbackReaderWriter<Request, Response>;
void BindStream(ServerCallbackReaderWriter<Request, Response>* stream) {
stream_ = stream;
}
ServerCallbackReaderWriter<Request, Response>* stream_;
};
template <class Request, class Response>
class ServerReadReactor : public internal::ServerReactor {
public:
~ServerReadReactor() = default;
virtual void OnStarted(ServerContext*, Response* resp) {}
virtual void OnSendInitialMetadataDone(bool ok) {}
virtual void OnReadDone(bool ok) {}
void StartSendInitialMetadata() { reader_->SendInitialMetadata(); }
void StartRead(Request* msg) { reader_->Read(msg); }
void Finish(Status s) { reader_->Finish(std::move(s)); }
private:
friend class ServerCallbackReader<Request>;
void BindReader(ServerCallbackReader<Request>* reader) { reader_ = reader; }
ServerCallbackReader<Request>* reader_;
};
template <class Request, class Response>
class ServerWriteReactor : public internal::ServerReactor {
public:
~ServerWriteReactor() = default;
virtual void OnStarted(ServerContext*, const Request* req) {}
virtual void OnSendInitialMetadataDone(bool ok) {}
virtual void OnWriteDone(bool ok) {}
void StartSendInitialMetadata() { writer_->SendInitialMetadata(); }
void StartWrite(const Response* msg) { StartWrite(msg, WriteOptions()); }
void StartWrite(const Response* msg, WriteOptions options) {
writer_->Write(msg, std::move(options));
}
void StartWriteAndFinish(const Response* msg, WriteOptions options,
Status s) {
writer_->WriteAndFinish(msg, std::move(options), std::move(s));
}
void StartWriteLast(const Response* msg, WriteOptions options) {
StartWrite(msg, std::move(options.set_last_message()));
}
void Finish(Status s) { writer_->Finish(std::move(s)); }
private:
friend class ServerCallbackWriter<Response>;
void BindWriter(ServerCallbackWriter<Response>* writer) { writer_ = writer; }
ServerCallbackWriter<Response>* writer_;
};
} // namespace experimental
namespace internal {
template <class ServiceType, class RequestType, class ResponseType>
template <class Request, class Response>
class UnimplementedReadReactor
: public experimental::ServerReadReactor<Request, Response> {
public:
void OnDone() override { delete this; }
void OnStarted(ServerContext*, Response*) override {
this->Finish(Status(StatusCode::UNIMPLEMENTED, ""));
}
};
template <class Request, class Response>
class UnimplementedWriteReactor
: public experimental::ServerWriteReactor<Request, Response> {
public:
void OnDone() override { delete this; }
void OnStarted(ServerContext*, const Request*) override {
this->Finish(Status(StatusCode::UNIMPLEMENTED, ""));
}
};
template <class Request, class Response>
class UnimplementedBidiReactor
: public experimental::ServerBidiReactor<Request, Response> {
public:
void OnDone() override { delete this; }
void OnStarted(ServerContext*) override {
this->Finish(Status(StatusCode::UNIMPLEMENTED, ""));
}
};
template <class RequestType, class ResponseType>
class CallbackUnaryHandler : public MethodHandler {
public:
CallbackUnaryHandler(
std::function<void(ServerContext*, const RequestType*, ResponseType*,
experimental::ServerCallbackRpcController*)>
func,
ServiceType* service)
func)
: func_(func) {}
void RunHandler(const HandlerParameter& param) final {
// Arena allocate a controller structure (that includes request/response)
@ -81,9 +272,8 @@ class CallbackUnaryHandler : public MethodHandler {
if (status.ok()) {
// Call the actual function handler and expect the user to call finish
CatchingCallback(std::move(func_), param.server_context,
controller->request(), controller->response(),
controller);
CatchingCallback(func_, param.server_context, controller->request(),
controller->response(), controller);
} else {
// if deserialization failed, we need to fail the call
controller->Finish(status);
@ -117,79 +307,579 @@ class CallbackUnaryHandler : public MethodHandler {
: public experimental::ServerCallbackRpcController {
public:
void Finish(Status s) override {
finish_tag_.Set(
call_.call(),
[this](bool) {
grpc_call* call = call_.call();
auto call_requester = std::move(call_requester_);
this->~ServerCallbackRpcControllerImpl(); // explicitly call
// destructor
g_core_codegen_interface->grpc_call_unref(call);
call_requester();
},
&finish_buf_);
finish_tag_.Set(call_.call(), [this](bool) { MaybeDone(); },
&finish_ops_);
if (!ctx_->sent_initial_metadata_) {
finish_buf_.SendInitialMetadata(&ctx_->initial_metadata_,
finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_,
ctx_->initial_metadata_flags());
if (ctx_->compression_level_set()) {
finish_buf_.set_compression_level(ctx_->compression_level());
finish_ops_.set_compression_level(ctx_->compression_level());
}
ctx_->sent_initial_metadata_ = true;
}
// The response is dropped if the status is not OK.
if (s.ok()) {
finish_buf_.ServerSendStatus(&ctx_->trailing_metadata_,
finish_buf_.SendMessage(resp_));
finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_,
finish_ops_.SendMessage(resp_));
} else {
finish_buf_.ServerSendStatus(&ctx_->trailing_metadata_, s);
finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, s);
}
finish_buf_.set_core_cq_tag(&finish_tag_);
call_.PerformOps(&finish_buf_);
finish_ops_.set_core_cq_tag(&finish_tag_);
call_.PerformOps(&finish_ops_);
}
void SendInitialMetadata(std::function<void(bool)> f) override {
GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
meta_tag_.Set(call_.call(), std::move(f), &meta_buf_);
meta_buf_.SendInitialMetadata(&ctx_->initial_metadata_,
callbacks_outstanding_++;
// TODO(vjpai): Consider taking f as a move-capture if we adopt C++14
// and if performance of this operation matters
meta_tag_.Set(call_.call(),
[this, f](bool ok) {
f(ok);
MaybeDone();
},
&meta_ops_);
meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_,
ctx_->initial_metadata_flags());
if (ctx_->compression_level_set()) {
meta_buf_.set_compression_level(ctx_->compression_level());
meta_ops_.set_compression_level(ctx_->compression_level());
}
ctx_->sent_initial_metadata_ = true;
meta_buf_.set_core_cq_tag(&meta_tag_);
call_.PerformOps(&meta_buf_);
meta_ops_.set_core_cq_tag(&meta_tag_);
call_.PerformOps(&meta_ops_);
}
private:
template <class SrvType, class ReqType, class RespType>
friend class CallbackUnaryHandler;
friend class CallbackUnaryHandler<RequestType, ResponseType>;
ServerCallbackRpcControllerImpl(ServerContext* ctx, Call* call,
RequestType* req,
const RequestType* req,
std::function<void()> call_requester)
: ctx_(ctx),
call_(*call),
req_(req),
call_requester_(std::move(call_requester)) {}
call_requester_(std::move(call_requester)) {
ctx_->BeginCompletionOp(call, [this](bool) { MaybeDone(); }, nullptr);
}
~ServerCallbackRpcControllerImpl() { req_->~RequestType(); }
RequestType* request() { return req_; }
const RequestType* request() { return req_; }
ResponseType* response() { return &resp_; }
CallOpSet<CallOpSendInitialMetadata> meta_buf_;
void MaybeDone() {
if (--callbacks_outstanding_ == 0) {
grpc_call* call = call_.call();
auto call_requester = std::move(call_requester_);
this->~ServerCallbackRpcControllerImpl(); // explicitly call destructor
g_core_codegen_interface->grpc_call_unref(call);
call_requester();
}
}
CallOpSet<CallOpSendInitialMetadata> meta_ops_;
CallbackWithSuccessTag meta_tag_;
CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage,
CallOpServerSendStatus>
finish_buf_;
finish_ops_;
CallbackWithSuccessTag finish_tag_;
ServerContext* ctx_;
Call call_;
RequestType* req_;
const RequestType* req_;
ResponseType resp_;
std::function<void()> call_requester_;
std::atomic_int callbacks_outstanding_{
2}; // reserve for Finish and CompletionOp
};
};
template <class RequestType, class ResponseType>
class CallbackClientStreamingHandler : public MethodHandler {
public:
CallbackClientStreamingHandler(
std::function<
experimental::ServerReadReactor<RequestType, ResponseType>*()>
func)
: func_(std::move(func)) {}
void RunHandler(const HandlerParameter& param) final {
// Arena allocate a reader structure (that includes response)
g_core_codegen_interface->grpc_call_ref(param.call->call());
experimental::ServerReadReactor<RequestType, ResponseType>* reactor =
param.status.ok()
? CatchingReactorCreator<
experimental::ServerReadReactor<RequestType, ResponseType>>(
func_)
: nullptr;
if (reactor == nullptr) {
// if deserialization or reactor creator failed, we need to fail the call
reactor = new UnimplementedReadReactor<RequestType, ResponseType>;
}
auto* reader = new (g_core_codegen_interface->grpc_call_arena_alloc(
param.call->call(), sizeof(ServerCallbackReaderImpl)))
ServerCallbackReaderImpl(param.server_context, param.call,
std::move(param.call_requester), reactor);
reader->BindReactor(reactor);
reactor->OnStarted(param.server_context, reader->response());
reader->MaybeDone();
}
private:
std::function<experimental::ServerReadReactor<RequestType, ResponseType>*()>
func_;
class ServerCallbackReaderImpl
: public experimental::ServerCallbackReader<RequestType> {
public:
void Finish(Status s) override {
finish_tag_.Set(call_.call(), [this](bool) { MaybeDone(); },
&finish_ops_);
if (!ctx_->sent_initial_metadata_) {
finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_,
ctx_->initial_metadata_flags());
if (ctx_->compression_level_set()) {
finish_ops_.set_compression_level(ctx_->compression_level());
}
ctx_->sent_initial_metadata_ = true;
}
// The response is dropped if the status is not OK.
if (s.ok()) {
finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_,
finish_ops_.SendMessage(resp_));
} else {
finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, s);
}
finish_ops_.set_core_cq_tag(&finish_tag_);
call_.PerformOps(&finish_ops_);
}
void SendInitialMetadata() override {
GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
callbacks_outstanding_++;
meta_tag_.Set(call_.call(),
[this](bool ok) {
reactor_->OnSendInitialMetadataDone(ok);
MaybeDone();
},
&meta_ops_);
meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_,
ctx_->initial_metadata_flags());
if (ctx_->compression_level_set()) {
meta_ops_.set_compression_level(ctx_->compression_level());
}
ctx_->sent_initial_metadata_ = true;
meta_ops_.set_core_cq_tag(&meta_tag_);
call_.PerformOps(&meta_ops_);
}
void Read(RequestType* req) override {
callbacks_outstanding_++;
read_ops_.RecvMessage(req);
call_.PerformOps(&read_ops_);
}
private:
friend class CallbackClientStreamingHandler<RequestType, ResponseType>;
ServerCallbackReaderImpl(
ServerContext* ctx, Call* call, std::function<void()> call_requester,
experimental::ServerReadReactor<RequestType, ResponseType>* reactor)
: ctx_(ctx),
call_(*call),
call_requester_(std::move(call_requester)),
reactor_(reactor) {
ctx_->BeginCompletionOp(call, [this](bool) { MaybeDone(); }, reactor);
read_tag_.Set(call_.call(),
[this](bool ok) {
reactor_->OnReadDone(ok);
MaybeDone();
},
&read_ops_);
read_ops_.set_core_cq_tag(&read_tag_);
}
~ServerCallbackReaderImpl() {}
ResponseType* response() { return &resp_; }
void MaybeDone() {
if (--callbacks_outstanding_ == 0) {
reactor_->OnDone();
grpc_call* call = call_.call();
auto call_requester = std::move(call_requester_);
this->~ServerCallbackReaderImpl(); // explicitly call destructor
g_core_codegen_interface->grpc_call_unref(call);
call_requester();
}
}
CallOpSet<CallOpSendInitialMetadata> meta_ops_;
CallbackWithSuccessTag meta_tag_;
CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage,
CallOpServerSendStatus>
finish_ops_;
CallbackWithSuccessTag finish_tag_;
CallOpSet<CallOpRecvMessage<RequestType>> read_ops_;
CallbackWithSuccessTag read_tag_;
ServerContext* ctx_;
Call call_;
ResponseType resp_;
std::function<void()> call_requester_;
experimental::ServerReadReactor<RequestType, ResponseType>* reactor_;
std::atomic_int callbacks_outstanding_{
3}; // reserve for OnStarted, Finish, and CompletionOp
};
};
template <class RequestType, class ResponseType>
class CallbackServerStreamingHandler : public MethodHandler {
public:
CallbackServerStreamingHandler(
std::function<
experimental::ServerWriteReactor<RequestType, ResponseType>*()>
func)
: func_(std::move(func)) {}
void RunHandler(const HandlerParameter& param) final {
// Arena allocate a writer structure
g_core_codegen_interface->grpc_call_ref(param.call->call());
experimental::ServerWriteReactor<RequestType, ResponseType>* reactor =
param.status.ok()
? CatchingReactorCreator<
experimental::ServerWriteReactor<RequestType, ResponseType>>(
func_)
: nullptr;
if (reactor == nullptr) {
// if deserialization or reactor creator failed, we need to fail the call
reactor = new UnimplementedWriteReactor<RequestType, ResponseType>;
}
auto* writer = new (g_core_codegen_interface->grpc_call_arena_alloc(
param.call->call(), sizeof(ServerCallbackWriterImpl)))
ServerCallbackWriterImpl(param.server_context, param.call,
static_cast<RequestType*>(param.request),
std::move(param.call_requester), reactor);
writer->BindReactor(reactor);
reactor->OnStarted(param.server_context, writer->request());
writer->MaybeDone();
}
void* Deserialize(grpc_call* call, grpc_byte_buffer* req,
Status* status) final {
ByteBuffer buf;
buf.set_buffer(req);
auto* request = new (g_core_codegen_interface->grpc_call_arena_alloc(
call, sizeof(RequestType))) RequestType();
*status = SerializationTraits<RequestType>::Deserialize(&buf, request);
buf.Release();
if (status->ok()) {
return request;
}
request->~RequestType();
return nullptr;
}
private:
std::function<experimental::ServerWriteReactor<RequestType, ResponseType>*()>
func_;
class ServerCallbackWriterImpl
: public experimental::ServerCallbackWriter<ResponseType> {
public:
void Finish(Status s) override {
finish_tag_.Set(call_.call(), [this](bool) { MaybeDone(); },
&finish_ops_);
finish_ops_.set_core_cq_tag(&finish_tag_);
if (!ctx_->sent_initial_metadata_) {
finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_,
ctx_->initial_metadata_flags());
if (ctx_->compression_level_set()) {
finish_ops_.set_compression_level(ctx_->compression_level());
}
ctx_->sent_initial_metadata_ = true;
}
finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, s);
call_.PerformOps(&finish_ops_);
}
void SendInitialMetadata() override {
GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
callbacks_outstanding_++;
meta_tag_.Set(call_.call(),
[this](bool ok) {
reactor_->OnSendInitialMetadataDone(ok);
MaybeDone();
},
&meta_ops_);
meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_,
ctx_->initial_metadata_flags());
if (ctx_->compression_level_set()) {
meta_ops_.set_compression_level(ctx_->compression_level());
}
ctx_->sent_initial_metadata_ = true;
meta_ops_.set_core_cq_tag(&meta_tag_);
call_.PerformOps(&meta_ops_);
}
void Write(const ResponseType* resp, WriteOptions options) override {
callbacks_outstanding_++;
if (options.is_last_message()) {
options.set_buffer_hint();
}
if (!ctx_->sent_initial_metadata_) {
write_ops_.SendInitialMetadata(&ctx_->initial_metadata_,
ctx_->initial_metadata_flags());
if (ctx_->compression_level_set()) {
write_ops_.set_compression_level(ctx_->compression_level());
}
ctx_->sent_initial_metadata_ = true;
}
// TODO(vjpai): don't assert
GPR_CODEGEN_ASSERT(write_ops_.SendMessage(*resp, options).ok());
call_.PerformOps(&write_ops_);
}
void WriteAndFinish(const ResponseType* resp, WriteOptions options,
Status s) override {
// This combines the write into the finish callback
// Don't send any message if the status is bad
if (s.ok()) {
// TODO(vjpai): don't assert
GPR_CODEGEN_ASSERT(finish_ops_.SendMessage(*resp, options).ok());
}
Finish(std::move(s));
}
private:
friend class CallbackServerStreamingHandler<RequestType, ResponseType>;
ServerCallbackWriterImpl(
ServerContext* ctx, Call* call, const RequestType* req,
std::function<void()> call_requester,
experimental::ServerWriteReactor<RequestType, ResponseType>* reactor)
: ctx_(ctx),
call_(*call),
req_(req),
call_requester_(std::move(call_requester)),
reactor_(reactor) {
ctx_->BeginCompletionOp(call, [this](bool) { MaybeDone(); }, reactor);
write_tag_.Set(call_.call(),
[this](bool ok) {
reactor_->OnWriteDone(ok);
MaybeDone();
},
&write_ops_);
write_ops_.set_core_cq_tag(&write_tag_);
}
~ServerCallbackWriterImpl() { req_->~RequestType(); }
const RequestType* request() { return req_; }
void MaybeDone() {
if (--callbacks_outstanding_ == 0) {
reactor_->OnDone();
grpc_call* call = call_.call();
auto call_requester = std::move(call_requester_);
this->~ServerCallbackWriterImpl(); // explicitly call destructor
g_core_codegen_interface->grpc_call_unref(call);
call_requester();
}
}
CallOpSet<CallOpSendInitialMetadata> meta_ops_;
CallbackWithSuccessTag meta_tag_;
CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage,
CallOpServerSendStatus>
finish_ops_;
CallbackWithSuccessTag finish_tag_;
CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage> write_ops_;
CallbackWithSuccessTag write_tag_;
ServerContext* ctx_;
Call call_;
const RequestType* req_;
std::function<void()> call_requester_;
experimental::ServerWriteReactor<RequestType, ResponseType>* reactor_;
std::atomic_int callbacks_outstanding_{
3}; // reserve for OnStarted, Finish, and CompletionOp
};
};
template <class RequestType, class ResponseType>
class CallbackBidiHandler : public MethodHandler {
public:
CallbackBidiHandler(
std::function<
experimental::ServerBidiReactor<RequestType, ResponseType>*()>
func)
: func_(std::move(func)) {}
void RunHandler(const HandlerParameter& param) final {
g_core_codegen_interface->grpc_call_ref(param.call->call());
experimental::ServerBidiReactor<RequestType, ResponseType>* reactor =
param.status.ok()
? CatchingReactorCreator<
experimental::ServerBidiReactor<RequestType, ResponseType>>(
func_)
: nullptr;
if (reactor == nullptr) {
// if deserialization or reactor creator failed, we need to fail the call
reactor = new UnimplementedBidiReactor<RequestType, ResponseType>;
}
auto* stream = new (g_core_codegen_interface->grpc_call_arena_alloc(
param.call->call(), sizeof(ServerCallbackReaderWriterImpl)))
ServerCallbackReaderWriterImpl(param.server_context, param.call,
std::move(param.call_requester),
reactor);
stream->BindReactor(reactor);
reactor->OnStarted(param.server_context);
stream->MaybeDone();
}
private:
std::function<experimental::ServerBidiReactor<RequestType, ResponseType>*()>
func_;
class ServerCallbackReaderWriterImpl
: public experimental::ServerCallbackReaderWriter<RequestType,
ResponseType> {
public:
void Finish(Status s) override {
finish_tag_.Set(call_.call(), [this](bool) { MaybeDone(); },
&finish_ops_);
finish_ops_.set_core_cq_tag(&finish_tag_);
if (!ctx_->sent_initial_metadata_) {
finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_,
ctx_->initial_metadata_flags());
if (ctx_->compression_level_set()) {
finish_ops_.set_compression_level(ctx_->compression_level());
}
ctx_->sent_initial_metadata_ = true;
}
finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, s);
call_.PerformOps(&finish_ops_);
}
void SendInitialMetadata() override {
GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
callbacks_outstanding_++;
meta_tag_.Set(call_.call(),
[this](bool ok) {
reactor_->OnSendInitialMetadataDone(ok);
MaybeDone();
},
&meta_ops_);
meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_,
ctx_->initial_metadata_flags());
if (ctx_->compression_level_set()) {
meta_ops_.set_compression_level(ctx_->compression_level());
}
ctx_->sent_initial_metadata_ = true;
meta_ops_.set_core_cq_tag(&meta_tag_);
call_.PerformOps(&meta_ops_);
}
void Write(const ResponseType* resp, WriteOptions options) override {
callbacks_outstanding_++;
if (options.is_last_message()) {
options.set_buffer_hint();
}
if (!ctx_->sent_initial_metadata_) {
write_ops_.SendInitialMetadata(&ctx_->initial_metadata_,
ctx_->initial_metadata_flags());
if (ctx_->compression_level_set()) {
write_ops_.set_compression_level(ctx_->compression_level());
}
ctx_->sent_initial_metadata_ = true;
}
// TODO(vjpai): don't assert
GPR_CODEGEN_ASSERT(write_ops_.SendMessage(*resp, options).ok());
call_.PerformOps(&write_ops_);
}
void WriteAndFinish(const ResponseType* resp, WriteOptions options,
Status s) override {
// Don't send any message if the status is bad
if (s.ok()) {
// TODO(vjpai): don't assert
GPR_CODEGEN_ASSERT(finish_ops_.SendMessage(*resp, options).ok());
}
Finish(std::move(s));
}
void Read(RequestType* req) override {
callbacks_outstanding_++;
read_ops_.RecvMessage(req);
call_.PerformOps(&read_ops_);
}
private:
friend class CallbackBidiHandler<RequestType, ResponseType>;
ServerCallbackReaderWriterImpl(
ServerContext* ctx, Call* call, std::function<void()> call_requester,
experimental::ServerBidiReactor<RequestType, ResponseType>* reactor)
: ctx_(ctx),
call_(*call),
call_requester_(std::move(call_requester)),
reactor_(reactor) {
ctx_->BeginCompletionOp(call, [this](bool) { MaybeDone(); }, reactor);
write_tag_.Set(call_.call(),
[this](bool ok) {
reactor_->OnWriteDone(ok);
MaybeDone();
},
&write_ops_);
write_ops_.set_core_cq_tag(&write_tag_);
read_tag_.Set(call_.call(),
[this](bool ok) {
reactor_->OnReadDone(ok);
MaybeDone();
},
&read_ops_);
read_ops_.set_core_cq_tag(&read_tag_);
}
~ServerCallbackReaderWriterImpl() {}
void MaybeDone() {
if (--callbacks_outstanding_ == 0) {
reactor_->OnDone();
grpc_call* call = call_.call();
auto call_requester = std::move(call_requester_);
this->~ServerCallbackReaderWriterImpl(); // explicitly call destructor
g_core_codegen_interface->grpc_call_unref(call);
call_requester();
}
}
CallOpSet<CallOpSendInitialMetadata> meta_ops_;
CallbackWithSuccessTag meta_tag_;
CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage,
CallOpServerSendStatus>
finish_ops_;
CallbackWithSuccessTag finish_tag_;
CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage> write_ops_;
CallbackWithSuccessTag write_tag_;
CallOpSet<CallOpRecvMessage<RequestType>> read_ops_;
CallbackWithSuccessTag read_tag_;
ServerContext* ctx_;
Call call_;
std::function<void()> call_requester_;
experimental::ServerBidiReactor<RequestType, ResponseType>* reactor_;
std::atomic_int callbacks_outstanding_{
3}; // reserve for OnStarted, Finish, and CompletionOp
};
};

@ -66,13 +66,20 @@ template <class ServiceType, class RequestType, class ResponseType>
class ServerStreamingHandler;
template <class ServiceType, class RequestType, class ResponseType>
class BidiStreamingHandler;
template <class ServiceType, class RequestType, class ResponseType>
template <class RequestType, class ResponseType>
class CallbackUnaryHandler;
template <class RequestType, class ResponseType>
class CallbackClientStreamingHandler;
template <class RequestType, class ResponseType>
class CallbackServerStreamingHandler;
template <class RequestType, class ResponseType>
class CallbackBidiHandler;
template <class Streamer, bool WriteNeeded>
class TemplatedBidiStreamingHandler;
template <StatusCode code>
class ErrorMethodHandler;
class Call;
class ServerReactor;
} // namespace internal
class CompletionQueue;
@ -270,8 +277,14 @@ class ServerContext {
friend class ::grpc::internal::ServerStreamingHandler;
template <class Streamer, bool WriteNeeded>
friend class ::grpc::internal::TemplatedBidiStreamingHandler;
template <class ServiceType, class RequestType, class ResponseType>
template <class RequestType, class ResponseType>
friend class ::grpc::internal::CallbackUnaryHandler;
template <class RequestType, class ResponseType>
friend class ::grpc::internal::CallbackClientStreamingHandler;
template <class RequestType, class ResponseType>
friend class ::grpc::internal::CallbackServerStreamingHandler;
template <class RequestType, class ResponseType>
friend class ::grpc::internal::CallbackBidiHandler;
template <StatusCode code>
friend class internal::ErrorMethodHandler;
friend class ::grpc::ClientContext;
@ -282,7 +295,9 @@ class ServerContext {
class CompletionOp;
void BeginCompletionOp(internal::Call* call, bool callback);
void BeginCompletionOp(internal::Call* call,
std::function<void(bool)> callback,
internal::ServerReactor* reactor);
/// Return the tag queued by BeginCompletionOp()
internal::CompletionQueueTag* GetCompletionOpTag();

@ -87,6 +87,7 @@ CLASSIFIERS = [
# present, then it will still attempt to use Cython.
BUILD_WITH_CYTHON = os.environ.get('GRPC_PYTHON_BUILD_WITH_CYTHON', False)
# Export this variable to use the system installation of openssl. You need to
# have the header files installed (in /usr/include/openssl) and during
# runtime, the shared library must be installed
@ -105,6 +106,21 @@ BUILD_WITH_SYSTEM_ZLIB = os.environ.get('GRPC_PYTHON_BUILD_SYSTEM_ZLIB',
BUILD_WITH_SYSTEM_CARES = os.environ.get('GRPC_PYTHON_BUILD_SYSTEM_CARES',
False)
# For local development use only: This skips building gRPC Core and its
# dependencies, including protobuf and boringssl. This allows "incremental"
# compilation by first building gRPC Core using make, then building only the
# Python/Cython layers here.
#
# Note that this requires libboringssl.a in the libs/{dbg,opt}/ directory, which
# may require configuring make to not use the system openssl implementation:
#
# make HAS_SYSTEM_OPENSSL_ALPN=0
#
# TODO(ericgribkoff) Respect the BUILD_WITH_SYSTEM_* flags alongside this option
USE_PREBUILT_GRPC_CORE = os.environ.get(
'GRPC_PYTHON_USE_PREBUILT_GRPC_CORE', False)
# If this environmental variable is set, GRPC will not try to be compatible with
# libc versions old than the one it was compiled against.
DISABLE_LIBC_COMPATIBILITY = os.environ.get('GRPC_PYTHON_DISABLE_LIBC_COMPATIBILITY', False)
@ -249,7 +265,7 @@ def cython_extensions_and_necessity():
for name in CYTHON_EXTENSION_MODULE_NAMES]
config = os.environ.get('CONFIG', 'opt')
prefix = 'libs/' + config + '/'
if "darwin" in sys.platform:
if "darwin" in sys.platform or USE_PREBUILT_GRPC_CORE:
extra_objects = [prefix + 'libares.a',
prefix + 'libboringssl.a',
prefix + 'libgpr.a',

@ -132,6 +132,7 @@ grpc::string GetHeaderIncludes(grpc_generator::File* file,
"grpcpp/impl/codegen/async_generic_service.h",
"grpcpp/impl/codegen/async_stream.h",
"grpcpp/impl/codegen/async_unary_call.h",
"grpcpp/impl/codegen/client_callback.h",
"grpcpp/impl/codegen/method_handler_impl.h",
"grpcpp/impl/codegen/proto_utils.h",
"grpcpp/impl/codegen/rpc_method.h",
@ -580,11 +581,22 @@ void PrintHeaderClientMethodCallbackInterfaces(
"const $Request$* request, $Response$* response, "
"std::function<void(::grpc::Status)>) = 0;\n");
} else if (ClientOnlyStreaming(method)) {
// TODO(vjpai): Add support for client-side streaming
printer->Print(*vars,
"virtual void $Method$(::grpc::ClientContext* context, "
"$Response$* response, "
"::grpc::experimental::ClientWriteReactor< $Request$>* "
"reactor) = 0;\n");
} else if (ServerOnlyStreaming(method)) {
// TODO(vjpai): Add support for server-side streaming
printer->Print(*vars,
"virtual void $Method$(::grpc::ClientContext* context, "
"$Request$* request, "
"::grpc::experimental::ClientReadReactor< $Response$>* "
"reactor) = 0;\n");
} else if (method->BidiStreaming()) {
// TODO(vjpai): Add support for bidi streaming
printer->Print(*vars,
"virtual void $Method$(::grpc::ClientContext* context, "
"::grpc::experimental::ClientBidiReactor< "
"$Request$,$Response$>* reactor) = 0;\n");
}
}
@ -631,11 +643,23 @@ void PrintHeaderClientMethodCallback(grpc_generator::Printer* printer,
"const $Request$* request, $Response$* response, "
"std::function<void(::grpc::Status)>) override;\n");
} else if (ClientOnlyStreaming(method)) {
// TODO(vjpai): Add support for client-side streaming
printer->Print(*vars,
"void $Method$(::grpc::ClientContext* context, "
"$Response$* response, "
"::grpc::experimental::ClientWriteReactor< $Request$>* "
"reactor) override;\n");
} else if (ServerOnlyStreaming(method)) {
// TODO(vjpai): Add support for server-side streaming
printer->Print(*vars,
"void $Method$(::grpc::ClientContext* context, "
"$Request$* request, "
"::grpc::experimental::ClientReadReactor< $Response$>* "
"reactor) override;\n");
} else if (method->BidiStreaming()) {
// TODO(vjpai): Add support for bidi streaming
printer->Print(*vars,
"void $Method$(::grpc::ClientContext* context, "
"::grpc::experimental::ClientBidiReactor< "
"$Request$,$Response$>* reactor) override;\n");
}
}
@ -865,6 +889,11 @@ void PrintHeaderServerCallbackMethodsHelper(
" abort();\n"
" return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"
"}\n");
printer->Print(*vars,
"virtual ::grpc::experimental::ServerReadReactor< "
"$RealRequest$, $RealResponse$>* $Method$() {\n"
" return new ::grpc::internal::UnimplementedReadReactor<\n"
" $RealRequest$, $RealResponse$>;}\n");
} else if (ServerOnlyStreaming(method)) {
printer->Print(
*vars,
@ -876,6 +905,11 @@ void PrintHeaderServerCallbackMethodsHelper(
" abort();\n"
" return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"
"}\n");
printer->Print(*vars,
"virtual ::grpc::experimental::ServerWriteReactor< "
"$RealRequest$, $RealResponse$>* $Method$() {\n"
" return new ::grpc::internal::UnimplementedWriteReactor<\n"
" $RealRequest$, $RealResponse$>;}\n");
} else if (method->BidiStreaming()) {
printer->Print(
*vars,
@ -887,6 +921,11 @@ void PrintHeaderServerCallbackMethodsHelper(
" abort();\n"
" return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"
"}\n");
printer->Print(*vars,
"virtual ::grpc::experimental::ServerBidiReactor< "
"$RealRequest$, $RealResponse$>* $Method$() {\n"
" return new ::grpc::internal::UnimplementedBidiReactor<\n"
" $RealRequest$, $RealResponse$>;}\n");
}
}
@ -915,22 +954,36 @@ void PrintHeaderServerMethodCallback(
*vars,
" ::grpc::Service::experimental().MarkMethodCallback($Idx$,\n"
" new ::grpc::internal::CallbackUnaryHandler< "
"ExperimentalWithCallbackMethod_$Method$<BaseClass>, $RealRequest$, "
"$RealResponse$>(\n"
"$RealRequest$, $RealResponse$>(\n"
" [this](::grpc::ServerContext* context,\n"
" const $RealRequest$* request,\n"
" $RealResponse$* response,\n"
" ::grpc::experimental::ServerCallbackRpcController* "
"controller) {\n"
" this->$"
" return this->$"
"Method$(context, request, response, controller);\n"
" }, this));\n");
" }));\n");
} else if (ClientOnlyStreaming(method)) {
// TODO(vjpai): Add in code generation for all streaming methods
printer->Print(
*vars,
" ::grpc::Service::experimental().MarkMethodCallback($Idx$,\n"
" new ::grpc::internal::CallbackClientStreamingHandler< "
"$RealRequest$, $RealResponse$>(\n"
" [this] { return this->$Method$(); }));\n");
} else if (ServerOnlyStreaming(method)) {
// TODO(vjpai): Add in code generation for all streaming methods
printer->Print(
*vars,
" ::grpc::Service::experimental().MarkMethodCallback($Idx$,\n"
" new ::grpc::internal::CallbackServerStreamingHandler< "
"$RealRequest$, $RealResponse$>(\n"
" [this] { return this->$Method$(); }));\n");
} else if (method->BidiStreaming()) {
// TODO(vjpai): Add in code generation for all streaming methods
printer->Print(
*vars,
" ::grpc::Service::experimental().MarkMethodCallback($Idx$,\n"
" new ::grpc::internal::CallbackBidiHandler< "
"$RealRequest$, $RealResponse$>(\n"
" [this] { return this->$Method$(); }));\n");
}
printer->Print(*vars, "}\n");
printer->Print(*vars,
@ -967,8 +1020,7 @@ void PrintHeaderServerMethodRawCallback(
*vars,
" ::grpc::Service::experimental().MarkMethodRawCallback($Idx$,\n"
" new ::grpc::internal::CallbackUnaryHandler< "
"ExperimentalWithRawCallbackMethod_$Method$<BaseClass>, $RealRequest$, "
"$RealResponse$>(\n"
"$RealRequest$, $RealResponse$>(\n"
" [this](::grpc::ServerContext* context,\n"
" const $RealRequest$* request,\n"
" $RealResponse$* response,\n"
@ -976,13 +1028,28 @@ void PrintHeaderServerMethodRawCallback(
"controller) {\n"
" this->$"
"Method$(context, request, response, controller);\n"
" }, this));\n");
" }));\n");
} else if (ClientOnlyStreaming(method)) {
// TODO(vjpai): Add in code generation for all streaming methods
printer->Print(
*vars,
" ::grpc::Service::experimental().MarkMethodRawCallback($Idx$,\n"
" new ::grpc::internal::CallbackClientStreamingHandler< "
"$RealRequest$, $RealResponse$>(\n"
" [this] { return this->$Method$(); }));\n");
} else if (ServerOnlyStreaming(method)) {
// TODO(vjpai): Add in code generation for all streaming methods
printer->Print(
*vars,
" ::grpc::Service::experimental().MarkMethodRawCallback($Idx$,\n"
" new ::grpc::internal::CallbackServerStreamingHandler< "
"$RealRequest$, $RealResponse$>(\n"
" [this] { return this->$Method$(); }));\n");
} else if (method->BidiStreaming()) {
// TODO(vjpai): Add in code generation for all streaming methods
printer->Print(
*vars,
" ::grpc::Service::experimental().MarkMethodRawCallback($Idx$,\n"
" new ::grpc::internal::CallbackBidiHandler< "
"$RealRequest$, $RealResponse$>(\n"
" [this] { return this->$Method$(); }));\n");
}
printer->Print(*vars, "}\n");
printer->Print(*vars,
@ -1607,7 +1674,19 @@ void PrintSourceClientMethod(grpc_generator::Printer* printer,
"context, response);\n"
"}\n\n");
// TODO(vjpai): Add callback version
printer->Print(
*vars,
"void $ns$$Service$::"
"Stub::experimental_async::$Method$(::grpc::ClientContext* context, "
"$Response$* response, "
"::grpc::experimental::ClientWriteReactor< $Request$>* reactor) {\n");
printer->Print(*vars,
" ::grpc::internal::ClientCallbackWriterFactory< "
"$Request$>::Create("
"stub_->channel_.get(), "
"stub_->rpcmethod_$Method$_, "
"context, response, reactor);\n"
"}\n\n");
for (auto async_prefix : async_prefixes) {
(*vars)["AsyncPrefix"] = async_prefix.prefix;
@ -1641,7 +1720,19 @@ void PrintSourceClientMethod(grpc_generator::Printer* printer,
"context, request);\n"
"}\n\n");
// TODO(vjpai): Add callback version
printer->Print(
*vars,
"void $ns$$Service$::Stub::experimental_async::$Method$(::grpc::"
"ClientContext* context, "
"$Request$* request, "
"::grpc::experimental::ClientReadReactor< $Response$>* reactor) {\n");
printer->Print(*vars,
" ::grpc::internal::ClientCallbackReaderFactory< "
"$Response$>::Create("
"stub_->channel_.get(), "
"stub_->rpcmethod_$Method$_, "
"context, request, reactor);\n"
"}\n\n");
for (auto async_prefix : async_prefixes) {
(*vars)["AsyncPrefix"] = async_prefix.prefix;
@ -1675,7 +1766,19 @@ void PrintSourceClientMethod(grpc_generator::Printer* printer,
"context);\n"
"}\n\n");
// TODO(vjpai): Add callback version
printer->Print(
*vars,
"void $ns$$Service$::Stub::experimental_async::$Method$(::grpc::"
"ClientContext* context, "
"::grpc::experimental::ClientBidiReactor< $Request$,$Response$>* "
"reactor) {\n");
printer->Print(*vars,
" ::grpc::internal::ClientCallbackReaderWriterFactory< "
"$Request$,$Response$>::Create("
"stub_->channel_.get(), "
"stub_->rpcmethod_$Method$_, "
"context, reactor);\n"
"}\n\n");
for (auto async_prefix : async_prefixes) {
(*vars)["AsyncPrefix"] = async_prefix.prefix;

@ -749,7 +749,7 @@ void GrpcLb::BalancerCallState::OnBalancerMessageReceivedLocked(
void* arg, grpc_error* error) {
BalancerCallState* lb_calld = static_cast<BalancerCallState*>(arg);
GrpcLb* grpclb_policy = lb_calld->grpclb_policy();
// Empty payload means the LB call was cancelled.
// Null payload means the LB call was cancelled.
if (lb_calld != grpclb_policy->lb_calld_.get() ||
lb_calld->recv_message_payload_ == nullptr) {
lb_calld->Unref(DEBUG_LOCATION, "on_message_received");
@ -803,54 +803,45 @@ void GrpcLb::BalancerCallState::OnBalancerMessageReceivedLocked(
gpr_free(ipport);
}
}
/* update serverlist */
if (serverlist->num_servers > 0) {
// Start sending client load report only after we start using the
// serverlist returned from the current LB call.
if (lb_calld->client_stats_report_interval_ > 0 &&
lb_calld->client_stats_ == nullptr) {
lb_calld->client_stats_.reset(New<GrpcLbClientStats>());
// TODO(roth): We currently track this ref manually. Once the
// ClosureRef API is ready, we should pass the RefCountedPtr<> along
// with the callback.
auto self = lb_calld->Ref(DEBUG_LOCATION, "client_load_report");
self.release();
lb_calld->ScheduleNextClientLoadReportLocked();
}
if (grpc_grpclb_serverlist_equals(grpclb_policy->serverlist_,
serverlist)) {
if (grpc_lb_glb_trace.enabled()) {
gpr_log(GPR_INFO,
"[grpclb %p] Incoming server list identical to current, "
"ignoring.",
grpclb_policy);
}
grpc_grpclb_destroy_serverlist(serverlist);
} else { /* new serverlist */
if (grpclb_policy->serverlist_ != nullptr) {
/* dispose of the old serverlist */
grpc_grpclb_destroy_serverlist(grpclb_policy->serverlist_);
} else {
/* or dispose of the fallback */
grpc_lb_addresses_destroy(grpclb_policy->fallback_backend_addresses_);
grpclb_policy->fallback_backend_addresses_ = nullptr;
if (grpclb_policy->fallback_timer_callback_pending_) {
grpc_timer_cancel(&grpclb_policy->lb_fallback_timer_);
}
}
// and update the copy in the GrpcLb instance. This
// serverlist instance will be destroyed either upon the next
// update or when the GrpcLb instance is destroyed.
grpclb_policy->serverlist_ = serverlist;
grpclb_policy->serverlist_index_ = 0;
grpclb_policy->CreateOrUpdateRoundRobinPolicyLocked();
}
} else {
// Start sending client load report only after we start using the
// serverlist returned from the current LB call.
if (lb_calld->client_stats_report_interval_ > 0 &&
lb_calld->client_stats_ == nullptr) {
lb_calld->client_stats_.reset(New<GrpcLbClientStats>());
// TODO(roth): We currently track this ref manually. Once the
// ClosureRef API is ready, we should pass the RefCountedPtr<> along
// with the callback.
auto self = lb_calld->Ref(DEBUG_LOCATION, "client_load_report");
self.release();
lb_calld->ScheduleNextClientLoadReportLocked();
}
// Check if the serverlist differs from the previous one.
if (grpc_grpclb_serverlist_equals(grpclb_policy->serverlist_, serverlist)) {
if (grpc_lb_glb_trace.enabled()) {
gpr_log(GPR_INFO, "[grpclb %p] Received empty server list, ignoring.",
gpr_log(GPR_INFO,
"[grpclb %p] Incoming server list identical to current, "
"ignoring.",
grpclb_policy);
}
grpc_grpclb_destroy_serverlist(serverlist);
} else { // New serverlist.
if (grpclb_policy->serverlist_ != nullptr) {
// Dispose of the old serverlist.
grpc_grpclb_destroy_serverlist(grpclb_policy->serverlist_);
} else {
// Dispose of the fallback.
grpc_lb_addresses_destroy(grpclb_policy->fallback_backend_addresses_);
grpclb_policy->fallback_backend_addresses_ = nullptr;
if (grpclb_policy->fallback_timer_callback_pending_) {
grpc_timer_cancel(&grpclb_policy->lb_fallback_timer_);
}
}
// Update the serverlist in the GrpcLb instance. This serverlist
// instance will be destroyed either upon the next update or when the
// GrpcLb instance is destroyed.
grpclb_policy->serverlist_ = serverlist;
grpclb_policy->serverlist_index_ = 0;
grpclb_policy->CreateOrUpdateRoundRobinPolicyLocked();
}
} else {
// No valid initial response or serverlist found.
@ -1583,7 +1574,7 @@ void GrpcLb::AddPendingPick(PendingPick* pp) {
bool GrpcLb::PickFromRoundRobinPolicyLocked(bool force_async, PendingPick* pp,
grpc_error** error) {
// Check for drops if we are not using fallback backend addresses.
if (serverlist_ != nullptr) {
if (serverlist_ != nullptr && serverlist_->num_servers > 0) {
// Look at the index into the serverlist to see if we should drop this call.
grpc_grpclb_server* server = serverlist_->servers[serverlist_index_++];
if (serverlist_index_ == serverlist_->num_servers) {
@ -1681,7 +1672,6 @@ grpc_channel_args* GrpcLb::CreateRoundRobinPolicyArgsLocked() {
grpc_lb_addresses* addresses;
bool is_backend_from_grpclb_load_balancer = false;
if (serverlist_ != nullptr) {
GPR_ASSERT(serverlist_->num_servers > 0);
addresses = ProcessServerlist(serverlist_);
is_backend_from_grpclb_load_balancer = true;
} else {

@ -122,6 +122,8 @@ class AresDnsResolver : public Resolver {
char* service_config_json_ = nullptr;
// has shutdown been initiated
bool shutdown_initiated_ = false;
// timeout in milliseconds for active DNS queries
int query_timeout_ms_;
};
AresDnsResolver::AresDnsResolver(const ResolverArgs& args)
@ -159,6 +161,11 @@ AresDnsResolver::AresDnsResolver(const ResolverArgs& args)
grpc_combiner_scheduler(combiner()));
GRPC_CLOSURE_INIT(&on_resolved_, OnResolvedLocked, this,
grpc_combiner_scheduler(combiner()));
const grpc_arg* query_timeout_ms_arg =
grpc_channel_args_find(channel_args_, GRPC_ARG_DNS_ARES_QUERY_TIMEOUT_MS);
query_timeout_ms_ = grpc_channel_arg_get_integer(
query_timeout_ms_arg,
{GRPC_DNS_ARES_DEFAULT_QUERY_TIMEOUT_MS, 0, INT_MAX});
}
AresDnsResolver::~AresDnsResolver() {
@ -410,7 +417,8 @@ void AresDnsResolver::StartResolvingLocked() {
pending_request_ = grpc_dns_lookup_ares_locked(
dns_server_, name_to_resolve_, kDefaultPort, interested_parties_,
&on_resolved_, &lb_addresses_, true /* check_grpclb */,
request_service_config_ ? &service_config_json_ : nullptr, combiner());
request_service_config_ ? &service_config_json_ : nullptr,
query_timeout_ms_, combiner());
last_resolution_timestamp_ = grpc_core::ExecCtx::Get()->Now();
}

@ -33,6 +33,7 @@
#include "src/core/lib/gpr/string.h"
#include "src/core/lib/iomgr/iomgr_internal.h"
#include "src/core/lib/iomgr/sockaddr_utils.h"
#include "src/core/lib/iomgr/timer.h"
typedef struct fd_node {
/** the owner of this fd node */
@ -76,6 +77,12 @@ struct grpc_ares_ev_driver {
grpc_ares_request* request;
/** Owned by the ev_driver. Creates new GrpcPolledFd's */
grpc_core::UniquePtr<grpc_core::GrpcPolledFdFactory> polled_fd_factory;
/** query timeout in milliseconds */
int query_timeout_ms;
/** alarm to cancel active queries */
grpc_timer query_timeout;
/** cancels queries on a timeout */
grpc_closure on_timeout_locked;
};
static void grpc_ares_notify_on_event_locked(grpc_ares_ev_driver* ev_driver);
@ -116,8 +123,11 @@ static void fd_node_shutdown_locked(fd_node* fdn, const char* reason) {
}
}
static void on_timeout_locked(void* arg, grpc_error* error);
grpc_error* grpc_ares_ev_driver_create_locked(grpc_ares_ev_driver** ev_driver,
grpc_pollset_set* pollset_set,
int query_timeout_ms,
grpc_combiner* combiner,
grpc_ares_request* request) {
*ev_driver = grpc_core::New<grpc_ares_ev_driver>();
@ -146,6 +156,9 @@ grpc_error* grpc_ares_ev_driver_create_locked(grpc_ares_ev_driver** ev_driver,
grpc_core::NewGrpcPolledFdFactory((*ev_driver)->combiner);
(*ev_driver)
->polled_fd_factory->ConfigureAresChannelLocked((*ev_driver)->channel);
GRPC_CLOSURE_INIT(&(*ev_driver)->on_timeout_locked, on_timeout_locked,
*ev_driver, grpc_combiner_scheduler(combiner));
(*ev_driver)->query_timeout_ms = query_timeout_ms;
return GRPC_ERROR_NONE;
}
@ -155,6 +168,7 @@ void grpc_ares_ev_driver_on_queries_complete_locked(
// is working, grpc_ares_notify_on_event_locked will shut down the
// fds; if it's not working, there are no fds to shut down.
ev_driver->shutting_down = true;
grpc_timer_cancel(&ev_driver->query_timeout);
grpc_ares_ev_driver_unref(ev_driver);
}
@ -185,6 +199,17 @@ static fd_node* pop_fd_node_locked(fd_node** head, ares_socket_t as) {
return nullptr;
}
static void on_timeout_locked(void* arg, grpc_error* error) {
grpc_ares_ev_driver* driver = static_cast<grpc_ares_ev_driver*>(arg);
GRPC_CARES_TRACE_LOG(
"ev_driver=%p on_timeout_locked. driver->shutting_down=%d. err=%s",
driver, driver->shutting_down, grpc_error_string(error));
if (!driver->shutting_down && error == GRPC_ERROR_NONE) {
grpc_ares_ev_driver_shutdown_locked(driver);
}
grpc_ares_ev_driver_unref(driver);
}
static void on_readable_locked(void* arg, grpc_error* error) {
fd_node* fdn = static_cast<fd_node*>(arg);
grpc_ares_ev_driver* ev_driver = fdn->ev_driver;
@ -314,6 +339,17 @@ void grpc_ares_ev_driver_start_locked(grpc_ares_ev_driver* ev_driver) {
if (!ev_driver->working) {
ev_driver->working = true;
grpc_ares_notify_on_event_locked(ev_driver);
grpc_millis timeout =
ev_driver->query_timeout_ms == 0
? GRPC_MILLIS_INF_FUTURE
: ev_driver->query_timeout_ms + grpc_core::ExecCtx::Get()->Now();
GRPC_CARES_TRACE_LOG(
"ev_driver=%p grpc_ares_ev_driver_start_locked. timeout in %" PRId64
" ms",
ev_driver, timeout);
grpc_ares_ev_driver_ref(ev_driver);
grpc_timer_init(&ev_driver->query_timeout, timeout,
&ev_driver->on_timeout_locked);
}
}

@ -43,6 +43,7 @@ ares_channel* grpc_ares_ev_driver_get_channel_locked(
created successfully. */
grpc_error* grpc_ares_ev_driver_create_locked(grpc_ares_ev_driver** ev_driver,
grpc_pollset_set* pollset_set,
int query_timeout_ms,
grpc_combiner* combiner,
grpc_ares_request* request);

@ -359,7 +359,7 @@ done:
void grpc_dns_lookup_ares_continue_after_check_localhost_and_ip_literals_locked(
grpc_ares_request* r, const char* dns_server, const char* name,
const char* default_port, grpc_pollset_set* interested_parties,
bool check_grpclb, grpc_combiner* combiner) {
bool check_grpclb, int query_timeout_ms, grpc_combiner* combiner) {
grpc_error* error = GRPC_ERROR_NONE;
grpc_ares_hostbyname_request* hr = nullptr;
ares_channel* channel = nullptr;
@ -388,7 +388,7 @@ void grpc_dns_lookup_ares_continue_after_check_localhost_and_ip_literals_locked(
port = gpr_strdup(default_port);
}
error = grpc_ares_ev_driver_create_locked(&r->ev_driver, interested_parties,
combiner, r);
query_timeout_ms, combiner, r);
if (error != GRPC_ERROR_NONE) goto error_cleanup;
channel = grpc_ares_ev_driver_get_channel_locked(r->ev_driver);
// If dns_server is specified, use it.
@ -522,7 +522,7 @@ static grpc_ares_request* grpc_dns_lookup_ares_locked_impl(
const char* dns_server, const char* name, const char* default_port,
grpc_pollset_set* interested_parties, grpc_closure* on_done,
grpc_lb_addresses** addrs, bool check_grpclb, char** service_config_json,
grpc_combiner* combiner) {
int query_timeout_ms, grpc_combiner* combiner) {
grpc_ares_request* r =
static_cast<grpc_ares_request*>(gpr_zalloc(sizeof(grpc_ares_request)));
r->ev_driver = nullptr;
@ -546,7 +546,7 @@ static grpc_ares_request* grpc_dns_lookup_ares_locked_impl(
// Look up name using c-ares lib.
grpc_dns_lookup_ares_continue_after_check_localhost_and_ip_literals_locked(
r, dns_server, name, default_port, interested_parties, check_grpclb,
combiner);
query_timeout_ms, combiner);
return r;
}
@ -554,6 +554,7 @@ grpc_ares_request* (*grpc_dns_lookup_ares_locked)(
const char* dns_server, const char* name, const char* default_port,
grpc_pollset_set* interested_parties, grpc_closure* on_done,
grpc_lb_addresses** addrs, bool check_grpclb, char** service_config_json,
int query_timeout_ms,
grpc_combiner* combiner) = grpc_dns_lookup_ares_locked_impl;
static void grpc_cancel_ares_request_locked_impl(grpc_ares_request* r) {
@ -648,7 +649,8 @@ static void grpc_resolve_address_invoke_dns_lookup_ares_locked(
r->ares_request = grpc_dns_lookup_ares_locked(
nullptr /* dns_server */, r->name, r->default_port, r->interested_parties,
&r->on_dns_lookup_done_locked, &r->lb_addrs, false /* check_grpclb */,
nullptr /* service_config_json */, r->combiner);
nullptr /* service_config_json */, GRPC_DNS_ARES_DEFAULT_QUERY_TIMEOUT_MS,
r->combiner);
}
static void grpc_resolve_address_ares_impl(const char* name,

@ -26,6 +26,8 @@
#include "src/core/lib/iomgr/polling_entity.h"
#include "src/core/lib/iomgr/resolve_address.h"
#define GRPC_DNS_ARES_DEFAULT_QUERY_TIMEOUT_MS 10000
extern grpc_core::TraceFlag grpc_trace_cares_address_sorting;
extern grpc_core::TraceFlag grpc_trace_cares_resolver;
@ -60,7 +62,7 @@ extern grpc_ares_request* (*grpc_dns_lookup_ares_locked)(
const char* dns_server, const char* name, const char* default_port,
grpc_pollset_set* interested_parties, grpc_closure* on_done,
grpc_lb_addresses** addresses, bool check_grpclb,
char** service_config_json, grpc_combiner* combiner);
char** service_config_json, int query_timeout_ms, grpc_combiner* combiner);
/* Cancel the pending grpc_ares_request \a request */
extern void (*grpc_cancel_ares_request_locked)(grpc_ares_request* request);

@ -30,7 +30,7 @@ static grpc_ares_request* grpc_dns_lookup_ares_locked_impl(
const char* dns_server, const char* name, const char* default_port,
grpc_pollset_set* interested_parties, grpc_closure* on_done,
grpc_lb_addresses** addrs, bool check_grpclb, char** service_config_json,
grpc_combiner* combiner) {
int query_timeout_ms, grpc_combiner* combiner) {
return NULL;
}
@ -38,6 +38,7 @@ grpc_ares_request* (*grpc_dns_lookup_ares_locked)(
const char* dns_server, const char* name, const char* default_port,
grpc_pollset_set* interested_parties, grpc_closure* on_done,
grpc_lb_addresses** addrs, bool check_grpclb, char** service_config_json,
int query_timeout_ms,
grpc_combiner* combiner) = grpc_dns_lookup_ares_locked_impl;
static void grpc_cancel_ares_request_locked_impl(grpc_ares_request* r) {}

@ -1390,6 +1390,7 @@ static void perform_stream_op_locked(void* stream_op,
GRPC_STATS_INC_HTTP2_OP_BATCHES();
s->context = op->payload->context;
s->traced = op->is_traced;
if (grpc_http_trace.enabled()) {
char* str = grpc_transport_stream_op_batch_string(op);
gpr_log(GPR_INFO, "perform_stream_op_locked: %s; on_complete = %p", str,

@ -32,6 +32,7 @@ void ContextList::Execute(void* arg, grpc_core::Timestamps* ts,
while (head != nullptr) {
if (error == GRPC_ERROR_NONE && ts != nullptr) {
if (write_timestamps_callback_g) {
ts->byte_offset = static_cast<uint32_t>(head->byte_offset_);
write_timestamps_callback_g(head->s_->context, ts);
}
}

@ -50,6 +50,7 @@ class ContextList {
/* Create a new element in the list and add it at the front */
ContextList* elem = grpc_core::New<ContextList>();
elem->s_ = s;
elem->byte_offset_ = s->byte_counter;
elem->next_ = *head;
*head = elem;
}
@ -61,6 +62,7 @@ class ContextList {
private:
grpc_chttp2_stream* s_ = nullptr;
ContextList* next_ = nullptr;
size_t byte_offset_ = 0;
};
void grpc_http2_set_write_timestamps_callback(

@ -642,10 +642,12 @@ struct grpc_chttp2_stream {
/** Whether bytes stored in unprocessed_incoming_byte_stream is decompressed
*/
bool unprocessed_incoming_frames_decompressed = false;
/** gRPC header bytes that are already decompressed */
size_t decompressed_header_bytes = 0;
/** Whether the bytes needs to be traced using Fathom */
bool traced = false;
/** gRPC header bytes that are already decompressed */
size_t decompressed_header_bytes = 0;
/** Byte counter for number of bytes written */
size_t byte_counter = 0;
};
/** Transport writing call flow:

@ -363,6 +363,7 @@ class DataSendContext {
grpc_chttp2_encode_data(s_->id, &s_->compressed_data_buffer, send_bytes,
is_last_frame_, &s_->stats.outgoing, &t_->outbuf);
s_->flow_control->SentData(send_bytes);
s_->byte_counter += send_bytes;
if (s_->compressed_data_buffer.length == 0) {
s_->sending_bytes += s_->uncompressed_data_size;
}
@ -488,9 +489,6 @@ class StreamWriteContext {
return; // early out: nothing to do
}
if (s_->traced && grpc_endpoint_can_track_err(t_->ep)) {
grpc_core::ContextList::Append(&t_->cl, s_);
}
while ((s_->flow_controlled_buffer.length > 0 ||
s_->compressed_data_buffer.length > 0) &&
data_send_context.max_outgoing() > 0) {
@ -500,6 +498,9 @@ class StreamWriteContext {
data_send_context.CompressMoreBytes();
}
}
if (s_->traced && grpc_endpoint_can_track_err(t_->ep)) {
grpc_core::ContextList::Append(&t_->cl, s_);
}
write_context_->ResetPingClock();
if (data_send_context.is_last_frame()) {
SentLastFrame();

@ -100,10 +100,7 @@ class InlinedVector {
void reserve(size_t capacity) {
if (capacity > capacity_) {
T* new_dynamic = static_cast<T*>(gpr_malloc(sizeof(T) * capacity));
for (size_t i = 0; i < size_; ++i) {
new (&new_dynamic[i]) T(std::move(data()[i]));
data()[i].~T();
}
move_elements(data(), new_dynamic, size_);
gpr_free(dynamic_);
dynamic_ = new_dynamic;
capacity_ = capacity;
@ -131,13 +128,25 @@ class InlinedVector {
size_--;
}
size_t size() const { return size_; }
bool empty() const { return size_ == 0; }
size_t capacity() const { return capacity_; }
void clear() {
destroy_elements();
init_data();
}
private:
void copy_from(const InlinedVector& v) {
// if v is allocated, copy over the buffer.
// if v is allocated, make sure we have enough capacity.
if (v.dynamic_ != nullptr) {
reserve(v.capacity_);
memcpy(dynamic_, v.dynamic_, v.size_ * sizeof(T));
} else {
memcpy(inline_, v.inline_, v.size_ * sizeof(T));
}
// copy over elements
for (size_t i = 0; i < v.size_; ++i) {
new (&(data()[i])) T(v[i]);
}
// copy over metadata
size_ = v.size_;
@ -145,11 +154,12 @@ class InlinedVector {
}
void move_from(InlinedVector& v) {
// if v is allocated, then we steal its buffer, else we copy it.
// if v is allocated, then we steal its dynamic array; otherwise, we
// move the elements individually.
if (v.dynamic_ != nullptr) {
dynamic_ = v.dynamic_;
} else {
memcpy(inline_, v.inline_, v.size_ * sizeof(T));
move_elements(v.data(), data(), v.size_);
}
// copy over metadata
size_ = v.size_;
@ -158,17 +168,13 @@ class InlinedVector {
v.init_data();
}
size_t size() const { return size_; }
bool empty() const { return size_ == 0; }
size_t capacity() const { return capacity_; }
void clear() {
destroy_elements();
init_data();
static void move_elements(T* src, T* dst, size_t num_elements) {
for (size_t i = 0; i < num_elements; ++i) {
new (&dst[i]) T(std::move(src[i]));
src[i].~T();
}
}
private:
void init_data() {
dynamic_ = nullptr;
size_ = 0;

@ -35,6 +35,9 @@ void TracedBuffer::AddNewEntry(TracedBuffer** head, uint32_t seq_no,
TracedBuffer* new_elem = New<TracedBuffer>(seq_no, arg);
/* Store the current time as the sendmsg time. */
new_elem->ts_.sendmsg_time = gpr_now(GPR_CLOCK_REALTIME);
new_elem->ts_.scheduled_time = gpr_inf_past(GPR_CLOCK_REALTIME);
new_elem->ts_.sent_time = gpr_inf_past(GPR_CLOCK_REALTIME);
new_elem->ts_.acked_time = gpr_inf_past(GPR_CLOCK_REALTIME);
if (*head == nullptr) {
*head = new_elem;
return;

@ -37,6 +37,8 @@ struct Timestamps {
gpr_timespec scheduled_time;
gpr_timespec sent_time;
gpr_timespec acked_time;
uint32_t byte_offset; /* byte offset relative to the start of the RPC */
};
/** TracedBuffer is a class to keep track of timestamps for a specific buffer in
@ -73,7 +75,7 @@ class TracedBuffer {
private:
GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_NEW
TracedBuffer(int seq_no, void* arg)
TracedBuffer(uint32_t seq_no, void* arg)
: seq_no_(seq_no), arg_(arg), next_(nullptr) {}
uint32_t seq_no_; /* The sequence number for the last byte in the buffer */

@ -1242,6 +1242,8 @@ static void pollset_set_del_pollset_set(grpc_pollset_set* bag,
* Event engine binding
*/
static void shutdown_background_closure(void) {}
static void shutdown_engine(void) {
fd_global_shutdown();
pollset_global_shutdown();
@ -1255,6 +1257,7 @@ static void shutdown_engine(void) {
static const grpc_event_engine_vtable vtable = {
sizeof(grpc_pollset),
true,
false,
fd_create,
fd_wrapped_fd,
@ -1284,6 +1287,7 @@ static const grpc_event_engine_vtable vtable = {
pollset_set_add_fd,
pollset_set_del_fd,
shutdown_background_closure,
shutdown_engine,
};

@ -1604,6 +1604,8 @@ static void pollset_set_del_pollset_set(grpc_pollset_set* bag,
* Event engine binding
*/
static void shutdown_background_closure(void) {}
static void shutdown_engine(void) {
fd_global_shutdown();
pollset_global_shutdown();
@ -1612,6 +1614,7 @@ static void shutdown_engine(void) {
static const grpc_event_engine_vtable vtable = {
sizeof(grpc_pollset),
true,
false,
fd_create,
fd_wrapped_fd,
@ -1641,6 +1644,7 @@ static const grpc_event_engine_vtable vtable = {
pollset_set_add_fd,
pollset_set_del_fd,
shutdown_background_closure,
shutdown_engine,
};

@ -1782,6 +1782,8 @@ static void global_cv_fd_table_shutdown() {
* event engine binding
*/
static void shutdown_background_closure(void) {}
static void shutdown_engine(void) {
pollset_global_shutdown();
if (grpc_cv_wakeup_fds_enabled()) {
@ -1796,6 +1798,7 @@ static void shutdown_engine(void) {
static const grpc_event_engine_vtable vtable = {
sizeof(grpc_pollset),
false,
false,
fd_create,
fd_wrapped_fd,
@ -1825,6 +1828,7 @@ static const grpc_event_engine_vtable vtable = {
pollset_set_add_fd,
pollset_set_del_fd,
shutdown_background_closure,
shutdown_engine,
};

@ -244,6 +244,10 @@ bool grpc_event_engine_can_track_errors(void) {
return false;
}
bool grpc_event_engine_run_in_background(void) {
return g_event_engine->run_in_background;
}
grpc_fd* grpc_fd_create(int fd, const char* name, bool track_err) {
GRPC_POLLING_API_TRACE("fd_create(%d, %s, %d)", fd, name, track_err);
GRPC_FD_TRACE("fd_create(%d, %s, %d)", fd, name, track_err);
@ -395,4 +399,8 @@ void grpc_pollset_set_del_fd(grpc_pollset_set* pollset_set, grpc_fd* fd) {
g_event_engine->pollset_set_del_fd(pollset_set, fd);
}
void grpc_shutdown_background_closure(void) {
g_event_engine->shutdown_background_closure();
}
#endif // GRPC_POSIX_SOCKET_EV

@ -42,6 +42,7 @@ typedef struct grpc_fd grpc_fd;
typedef struct grpc_event_engine_vtable {
size_t pollset_size;
bool can_track_err;
bool run_in_background;
grpc_fd* (*fd_create)(int fd, const char* name, bool track_err);
int (*fd_wrapped_fd)(grpc_fd* fd);
@ -79,6 +80,7 @@ typedef struct grpc_event_engine_vtable {
void (*pollset_set_add_fd)(grpc_pollset_set* pollset_set, grpc_fd* fd);
void (*pollset_set_del_fd)(grpc_pollset_set* pollset_set, grpc_fd* fd);
void (*shutdown_background_closure)(void);
void (*shutdown_engine)(void);
} grpc_event_engine_vtable;
@ -101,6 +103,11 @@ const char* grpc_get_poll_strategy_name();
*/
bool grpc_event_engine_can_track_errors();
/* Returns true if polling engine runs in the background, false otherwise.
* Currently only 'epollbg' runs in the background.
*/
bool grpc_event_engine_run_in_background();
/* Create a wrapped file descriptor.
Requires fd is a non-blocking file descriptor.
\a track_err if true means that error events would be tracked separately
@ -174,6 +181,9 @@ void grpc_pollset_add_fd(grpc_pollset* pollset, struct grpc_fd* fd);
void grpc_pollset_set_add_fd(grpc_pollset_set* pollset_set, grpc_fd* fd);
void grpc_pollset_set_del_fd(grpc_pollset_set* pollset_set, grpc_fd* fd);
/* Shut down all the closures registered in the background poller. */
void grpc_shutdown_background_closure();
/* override to allow tests to hook poll() usage */
typedef int (*grpc_poll_function_type)(struct pollfd*, nfds_t, int);
extern grpc_poll_function_type grpc_poll_function;

@ -60,7 +60,7 @@ void grpc_prefork() {
}
if (strcmp(grpc_get_poll_strategy_name(), "epoll1") != 0 &&
strcmp(grpc_get_poll_strategy_name(), "poll") != 0) {
gpr_log(GPR_ERROR,
gpr_log(GPR_INFO,
"Fork support is only compatible with the epoll1 and poll polling "
"strategies");
}

@ -157,6 +157,10 @@ void grpc_iomgr_shutdown() {
gpr_cv_destroy(&g_rcv);
}
void grpc_iomgr_shutdown_background_closure() {
grpc_iomgr_platform_shutdown_background_closure();
}
void grpc_iomgr_register_object(grpc_iomgr_object* obj, const char* name) {
obj->name = gpr_strdup(name);
gpr_mu_lock(&g_mu);

@ -35,6 +35,10 @@ void grpc_iomgr_start();
* exec_ctx. */
void grpc_iomgr_shutdown();
/** Signals the intention to shutdown all the closures registered in the
* background poller. */
void grpc_iomgr_shutdown_background_closure();
/* Exposed only for testing */
size_t grpc_iomgr_count_objects_for_testing();

@ -40,9 +40,11 @@ static void iomgr_platform_init(void) {
}
static void iomgr_platform_flush(void) {}
static void iomgr_platform_shutdown(void) { grpc_pollset_global_shutdown(); }
static void iomgr_platform_shutdown_background_closure(void) {}
static grpc_iomgr_platform_vtable vtable = {
iomgr_platform_init, iomgr_platform_flush, iomgr_platform_shutdown};
iomgr_platform_init, iomgr_platform_flush, iomgr_platform_shutdown,
iomgr_platform_shutdown_background_closure};
void grpc_custom_iomgr_init(grpc_socket_vtable* socket,
grpc_custom_resolver_vtable* resolver,

@ -41,3 +41,7 @@ void grpc_iomgr_platform_init() { iomgr_platform_vtable->init(); }
void grpc_iomgr_platform_flush() { iomgr_platform_vtable->flush(); }
void grpc_iomgr_platform_shutdown() { iomgr_platform_vtable->shutdown(); }
void grpc_iomgr_platform_shutdown_background_closure() {
iomgr_platform_vtable->shutdown_background_closure();
}

@ -35,6 +35,7 @@ typedef struct grpc_iomgr_platform_vtable {
void (*init)(void);
void (*flush)(void);
void (*shutdown)(void);
void (*shutdown_background_closure)(void);
} grpc_iomgr_platform_vtable;
void grpc_iomgr_register_object(grpc_iomgr_object* obj, const char* name);
@ -52,6 +53,9 @@ void grpc_iomgr_platform_flush(void);
/** tear down all platform specific global iomgr structures */
void grpc_iomgr_platform_shutdown(void);
/** shut down all the closures registered in the background poller */
void grpc_iomgr_platform_shutdown_background_closure(void);
bool grpc_iomgr_abort_on_leaks(void);
#endif /* GRPC_CORE_LIB_IOMGR_IOMGR_INTERNAL_H */

@ -51,8 +51,13 @@ static void iomgr_platform_shutdown(void) {
grpc_wakeup_fd_global_destroy();
}
static void iomgr_platform_shutdown_background_closure(void) {
grpc_shutdown_background_closure();
}
static grpc_iomgr_platform_vtable vtable = {
iomgr_platform_init, iomgr_platform_flush, iomgr_platform_shutdown};
iomgr_platform_init, iomgr_platform_flush, iomgr_platform_shutdown,
iomgr_platform_shutdown_background_closure};
void grpc_set_default_iomgr_platform() {
grpc_set_tcp_client_impl(&grpc_posix_tcp_client_vtable);

@ -54,8 +54,13 @@ static void iomgr_platform_shutdown(void) {
grpc_wakeup_fd_global_destroy();
}
static void iomgr_platform_shutdown_background_closure(void) {
grpc_shutdown_background_closure();
}
static grpc_iomgr_platform_vtable vtable = {
iomgr_platform_init, iomgr_platform_flush, iomgr_platform_shutdown};
iomgr_platform_init, iomgr_platform_flush, iomgr_platform_shutdown,
iomgr_platform_shutdown_background_closure};
void grpc_set_default_iomgr_platform() {
char* enable_cfstream = getenv(grpc_cfstream_env_var);

@ -71,8 +71,11 @@ static void iomgr_platform_shutdown(void) {
winsock_shutdown();
}
static void iomgr_platform_shutdown_background_closure(void) {}
static grpc_iomgr_platform_vtable vtable = {
iomgr_platform_init, iomgr_platform_flush, iomgr_platform_shutdown};
iomgr_platform_init, iomgr_platform_flush, iomgr_platform_shutdown,
iomgr_platform_shutdown_background_closure};
void grpc_set_default_iomgr_platform() {
grpc_set_tcp_client_impl(&grpc_windows_tcp_client_vtable);

@ -65,7 +65,7 @@ void grpc_set_resolver_impl(grpc_address_resolver_vtable* vtable);
/* Asynchronously resolve addr. Use default_port if a port isn't designated
in addr, otherwise use the port in addr. */
/* TODO(ctiller): add a timeout here */
/* TODO(apolcyn): add a timeout here */
void grpc_resolve_address(const char* addr, const char* default_port,
grpc_pollset_set* interested_parties,
grpc_closure* on_done,

@ -260,10 +260,17 @@ static void notify_on_write(grpc_tcp* tcp) {
if (grpc_tcp_trace.enabled()) {
gpr_log(GPR_INFO, "TCP:%p notify_on_write", tcp);
}
cover_self(tcp);
GRPC_CLOSURE_INIT(&tcp->write_done_closure,
tcp_drop_uncovered_then_handle_write, tcp,
grpc_schedule_on_exec_ctx);
if (grpc_event_engine_run_in_background()) {
// If there is a polling engine always running in the background, there is
// no need to run the backup poller.
GRPC_CLOSURE_INIT(&tcp->write_done_closure, tcp_handle_write, tcp,
grpc_schedule_on_exec_ctx);
} else {
cover_self(tcp);
GRPC_CLOSURE_INIT(&tcp->write_done_closure,
tcp_drop_uncovered_then_handle_write, tcp,
grpc_schedule_on_exec_ctx);
}
grpc_fd_notify_on_write(tcp->em_fd, &tcp->write_done_closure);
}
@ -627,7 +634,7 @@ static bool tcp_write_with_timestamps(grpc_tcp* tcp, struct msghdr* msg,
if (sending_length == static_cast<size_t>(length)) {
gpr_mu_lock(&tcp->tb_mu);
grpc_core::TracedBuffer::AddNewEntry(
&tcp->tb_head, static_cast<int>(tcp->bytes_counter + length),
&tcp->tb_head, static_cast<uint32_t>(tcp->bytes_counter + length),
tcp->outgoing_buffer_arg);
gpr_mu_unlock(&tcp->tb_mu);
tcp->outgoing_buffer_arg = nullptr;
@ -679,11 +686,9 @@ struct cmsghdr* process_timestamp(grpc_tcp* tcp, msghdr* msg,
}
/** For linux platforms, reads the socket's error queue and processes error
* messages from the queue. Returns true if all the errors processed were
* timestamps. Returns false if any of the errors were not timestamps. For
* non-linux platforms, error processing is not used/enabled currently.
* messages from the queue.
*/
static bool process_errors(grpc_tcp* tcp) {
static void process_errors(grpc_tcp* tcp) {
while (true) {
struct iovec iov;
iov.iov_base = nullptr;
@ -712,10 +717,10 @@ static bool process_errors(grpc_tcp* tcp) {
} while (r < 0 && saved_errno == EINTR);
if (r == -1 && saved_errno == EAGAIN) {
return true; /* No more errors to process */
return; /* No more errors to process */
}
if (r == -1) {
return false;
return;
}
if (grpc_tcp_trace.enabled()) {
if ((msg.msg_flags & MSG_CTRUNC) == 1) {
@ -725,8 +730,9 @@ static bool process_errors(grpc_tcp* tcp) {
if (msg.msg_controllen == 0) {
/* There was no control message found. It was probably spurious. */
return true;
return;
}
bool seen = false;
for (auto cmsg = CMSG_FIRSTHDR(&msg); cmsg && cmsg->cmsg_len;
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_level != SOL_SOCKET ||
@ -738,9 +744,13 @@ static bool process_errors(grpc_tcp* tcp) {
"unknown control message cmsg_level:%d cmsg_type:%d",
cmsg->cmsg_level, cmsg->cmsg_type);
}
return false;
return;
}
cmsg = process_timestamp(tcp, &msg, cmsg);
seen = true;
}
if (!seen) {
return;
}
}
}

@ -161,6 +161,7 @@ void grpc_shutdown(void) {
if (--g_initializations == 0) {
{
grpc_core::ExecCtx exec_ctx(0);
grpc_iomgr_shutdown_background_closure();
{
grpc_timer_manager_set_threading(
false); // shutdown timer_manager thread

@ -27,6 +27,7 @@
#include <grpc/support/log.h>
#include <grpc/support/sync.h>
#include "src/core/lib/gpr/alloc.h"
#include "src/core/lib/gpr/string.h"
#include "src/core/lib/iomgr/executor.h"
#include "src/core/lib/slice/slice_internal.h"
@ -149,7 +150,7 @@ void grpc_transport_move_stats(grpc_transport_stream_stats* from,
}
size_t grpc_transport_stream_size(grpc_transport* transport) {
return transport->vtable->sizeof_stream;
return GPR_ROUND_UP_TO_ALIGNMENT_SIZE(transport->vtable->sizeof_stream);
}
void grpc_transport_destroy(grpc_transport* transport) {

@ -129,7 +129,8 @@ struct grpc_transport_stream_op_batch {
recv_initial_metadata(false),
recv_message(false),
recv_trailing_metadata(false),
cancel_stream(false) {}
cancel_stream(false),
is_traced(false) {}
/** Should be scheduled when all of the non-recv operations in the batch
are complete.
@ -167,6 +168,9 @@ struct grpc_transport_stream_op_batch {
/** Cancel this stream with the provided error */
bool cancel_stream : 1;
/** Is this stream traced */
bool is_traced : 1;
/***************************************************************************
* remaining fields are initialized and used at the discretion of the
* current handler of the op */

@ -116,12 +116,13 @@ void alts_handshaker_client_handle_response(alts_handshaker_client* c,
"cb is nullptr in alts_tsi_handshaker_handle_response()");
return;
}
if (handshaker == nullptr || recv_buffer == nullptr) {
if (handshaker == nullptr) {
gpr_log(GPR_ERROR,
"Invalid arguments to alts_tsi_handshaker_handle_response()");
"handshaker is nullptr in alts_tsi_handshaker_handle_response()");
cb(TSI_INTERNAL_ERROR, user_data, nullptr, 0, nullptr);
return;
}
/* TSI handshake has been shutdown. */
if (alts_tsi_handshaker_has_shutdown(handshaker)) {
gpr_log(GPR_ERROR, "TSI handshake shutdown");
cb(TSI_HANDSHAKE_SHUTDOWN, user_data, nullptr, 0, nullptr);
@ -133,6 +134,12 @@ void alts_handshaker_client_handle_response(alts_handshaker_client* c,
cb(TSI_INTERNAL_ERROR, user_data, nullptr, 0, nullptr);
return;
}
if (recv_buffer == nullptr) {
gpr_log(GPR_ERROR,
"recv_buffer is nullptr in alts_tsi_handshaker_handle_response()");
cb(TSI_INTERNAL_ERROR, user_data, nullptr, 0, nullptr);
return;
}
grpc_gcp_handshaker_resp* resp =
alts_tsi_utils_deserialize_response(recv_buffer);
grpc_byte_buffer_destroy(client->recv_buffer);

@ -72,4 +72,13 @@ void GenericStub::experimental_type::UnaryCall(
context, request, response, std::move(on_completion));
}
void GenericStub::experimental_type::PrepareBidiStreamingCall(
ClientContext* context, const grpc::string& method,
experimental::ClientBidiReactor<ByteBuffer, ByteBuffer>* reactor) {
internal::ClientCallbackReaderWriterFactory<ByteBuffer, ByteBuffer>::Create(
stub_->channel_.get(),
internal::RpcMethod(method.c_str(), internal::RpcMethod::BIDI_STREAMING),
context, reactor);
}
} // namespace grpc

@ -291,7 +291,7 @@ class Server::SyncRequest final : public internal::CompletionQueueTag {
void ContinueRunAfterInterception() {
{
ctx_.BeginCompletionOp(&call_, false);
ctx_.BeginCompletionOp(&call_, nullptr, nullptr);
global_callbacks_->PreSynchronousRequest(&ctx_);
auto* handler = resources_ ? method_->handler()
: server_->resource_exhausted_handler_.get();
@ -456,7 +456,6 @@ class Server::CallbackRequest final : public internal::CompletionQueueTag {
}
}
void ContinueRunAfterInterception() {
req_->ctx_.BeginCompletionOp(call_, true);
req_->method_->handler()->RunHandler(
internal::MethodHandler::HandlerParameter(
call_, &req_->ctx_, req_->request_, req_->request_status_,
@ -1018,7 +1017,7 @@ bool ServerInterface::BaseAsyncRequest::FinalizeResult(void** tag,
}
}
if (*status && call_) {
context_->BeginCompletionOp(&call_wrapper_, false);
context_->BeginCompletionOp(&call_wrapper_, nullptr, nullptr);
}
*tag = tag_;
if (delete_on_finalize_) {
@ -1029,7 +1028,7 @@ bool ServerInterface::BaseAsyncRequest::FinalizeResult(void** tag,
void ServerInterface::BaseAsyncRequest::
ContinueFinalizeResultAfterInterception() {
context_->BeginCompletionOp(&call_wrapper_, false);
context_->BeginCompletionOp(&call_wrapper_, nullptr, nullptr);
// Queue a tag which will be returned immediately
grpc_core::ExecCtx exec_ctx;
grpc_cq_begin_op(notification_cq_->cq(), this);

@ -17,6 +17,7 @@
*/
#include <grpcpp/server_context.h>
#include <grpcpp/support/server_callback.h>
#include <algorithm>
#include <mutex>
@ -41,8 +42,9 @@ class ServerContext::CompletionOp final : public internal::CallOpSetInterface {
public:
// initial refs: one in the server context, one in the cq
// must ref the call before calling constructor and after deleting this
CompletionOp(internal::Call* call)
CompletionOp(internal::Call* call, internal::ServerReactor* reactor)
: call_(*call),
reactor_(reactor),
has_tag_(false),
tag_(nullptr),
core_cq_tag_(this),
@ -124,9 +126,9 @@ class ServerContext::CompletionOp final : public internal::CallOpSetInterface {
return;
}
/* Start a dummy op so that we can return the tag */
GPR_CODEGEN_ASSERT(GRPC_CALL_OK ==
g_core_codegen_interface->grpc_call_start_batch(
call_.call(), nullptr, 0, this, nullptr));
GPR_CODEGEN_ASSERT(
GRPC_CALL_OK ==
grpc_call_start_batch(call_.call(), nullptr, 0, core_cq_tag_, nullptr));
}
private:
@ -136,13 +138,14 @@ class ServerContext::CompletionOp final : public internal::CallOpSetInterface {
}
internal::Call call_;
internal::ServerReactor* reactor_;
bool has_tag_;
void* tag_;
void* core_cq_tag_;
std::mutex mu_;
int refs_;
bool finalized_;
int cancelled_;
int cancelled_; // This is an int (not bool) because it is passed to core
bool done_intercepting_;
internal::InterceptorBatchMethodsImpl interceptor_methods_;
};
@ -190,7 +193,16 @@ bool ServerContext::CompletionOp::FinalizeResult(void** tag, bool* status) {
}
finalized_ = true;
if (!*status) cancelled_ = 1;
// If for some reason the incoming status is false, mark that as a
// cancellation.
// TODO(vjpai): does this ever happen?
if (!*status) {
cancelled_ = 1;
}
if (cancelled_ && (reactor_ != nullptr)) {
reactor_->OnCancel();
}
/* Release the lock since we are going to be running through interceptors now
*/
lock.unlock();
@ -251,21 +263,25 @@ void ServerContext::Clear() {
initial_metadata_.clear();
trailing_metadata_.clear();
client_metadata_.Reset();
if (call_) {
grpc_call_unref(call_);
}
if (completion_op_) {
completion_op_->Unref();
completion_op_ = nullptr;
completion_tag_.Clear();
}
if (rpc_info_) {
rpc_info_->Unref();
rpc_info_ = nullptr;
}
if (call_) {
auto* call = call_;
call_ = nullptr;
grpc_call_unref(call);
}
// Don't need to clear out call_, completion_op_, or rpc_info_ because this is
// either called from destructor or just before Setup
}
void ServerContext::BeginCompletionOp(internal::Call* call, bool callback) {
void ServerContext::BeginCompletionOp(internal::Call* call,
std::function<void(bool)> callback,
internal::ServerReactor* reactor) {
GPR_ASSERT(!completion_op_);
if (rpc_info_) {
rpc_info_->Ref();
@ -273,10 +289,11 @@ void ServerContext::BeginCompletionOp(internal::Call* call, bool callback) {
grpc_call_ref(call->call());
completion_op_ =
new (grpc_call_arena_alloc(call->call(), sizeof(CompletionOp)))
CompletionOp(call);
if (callback) {
completion_tag_.Set(call->call(), nullptr, completion_op_);
CompletionOp(call, reactor);
if (callback != nullptr) {
completion_tag_.Set(call->call(), std::move(callback), completion_op_);
completion_op_->set_core_cq_tag(&completion_tag_);
completion_op_->set_tag(completion_op_);
} else if (has_notify_when_done_tag_) {
completion_op_->set_tag(async_notify_when_done_tag_);
}

@ -25,7 +25,7 @@ namespace Grpc.Core.Tests
{
public class AppDomainUnloadTest
{
#if NETCOREAPP1_0
#if NETCOREAPP1_1 || NETCOREAPP2_1
[Test]
[Ignore("Not supported for CoreCLR")]
public void AppDomainUnloadHookCanCleanupAbandonedCall()

@ -4,7 +4,7 @@
<Import Project="..\Grpc.Core\Common.csproj.include" />
<PropertyGroup>
<TargetFrameworks>net45;netcoreapp1.0</TargetFrameworks>
<TargetFrameworks>net45;netcoreapp1.1</TargetFrameworks>
<AssemblyName>Grpc.Core.Tests</AssemblyName>
<OutputType>Exe</OutputType>
<PackageId>Grpc.Core.Tests</PackageId>

@ -34,7 +34,7 @@ namespace Grpc.Core.Tests
{
// Make logger immune to NUnit capturing stdout and stderr to workaround https://github.com/nunit/nunit/issues/1406.
GrpcEnvironment.SetLogger(new ConsoleLogger());
#if NETCOREAPP1_0
#if NETCOREAPP1_1 || NETCOREAPP2_1
return new AutoRun(typeof(NUnitMain).GetTypeInfo().Assembly).Execute(args, new ExtendedTextWrapper(Console.Out), Console.In);
#else
return new AutoRun().Execute(args);

@ -31,7 +31,7 @@ namespace Grpc.Core.Tests
public class SanityTest
{
// TODO: make sanity test work for CoreCLR as well
#if !NETCOREAPP1_0
#if !NETCOREAPP1_1 && !NETCOREAPP2_1
/// <summary>
/// Because we depend on a native library, sometimes when things go wrong, the
/// entire NUnit test process crashes. To be able to track down problems better,
@ -44,7 +44,7 @@ namespace Grpc.Core.Tests
public void TestsJsonUpToDate()
{
var discoveredTests = DiscoverAllTestClasses();
var testsFromFile
var testsFromFile
= JsonConvert.DeserializeObject<Dictionary<string, List<string>>>(ReadTestsJson());
Assert.AreEqual(discoveredTests, testsFromFile);

@ -83,13 +83,13 @@ namespace Grpc.Core.Internal
// See https://github.com/grpc/grpc/pull/7303 for one option.
var assemblyDirectory = Path.GetDirectoryName(GetAssemblyPath());
// With old-style VS projects, the native libraries get copied using a .targets rule to the build output folder
// With "classic" VS projects, the native libraries get copied using a .targets rule to the build output folder
// alongside the compiled assembly.
// With dotnet cli projects targeting net45 framework, the native libraries (just the required ones)
// are similarly copied to the built output folder, through the magic of Microsoft.NETCore.Platforms.
var classicPath = Path.Combine(assemblyDirectory, GetNativeLibraryFilename());
// With dotnet cli project targeting netcoreapp1.0, projects will use Grpc.Core assembly directly in the location where it got restored
// With dotnet cli project targeting netcoreappX.Y, projects will use Grpc.Core assembly directly in the location where it got restored
// by nuget. We locate the native libraries based on known structure of Grpc.Core nuget package.
// When "dotnet publish" is used, the runtimes directory is copied next to the published assemblies.
string runtimesDirectory = string.Format("runtimes/{0}/native", GetPlatformString());

@ -4,7 +4,7 @@
<Import Project="..\Grpc.Core\Common.csproj.include" />
<PropertyGroup>
<TargetFrameworks>net45;netcoreapp1.0</TargetFrameworks>
<TargetFrameworks>net45;netcoreapp1.1</TargetFrameworks>
<AssemblyName>Grpc.Examples.MathClient</AssemblyName>
<OutputType>Exe</OutputType>
<PackageId>Grpc.Examples.MathClient</PackageId>

@ -4,7 +4,7 @@
<Import Project="..\Grpc.Core\Common.csproj.include" />
<PropertyGroup>
<TargetFrameworks>net45;netcoreapp1.0</TargetFrameworks>
<TargetFrameworks>net45;netcoreapp1.1</TargetFrameworks>
<AssemblyName>Grpc.Examples.MathServer</AssemblyName>
<OutputType>Exe</OutputType>
<PackageId>Grpc.Examples.MathServer</PackageId>

@ -4,7 +4,7 @@
<Import Project="..\Grpc.Core\Common.csproj.include" />
<PropertyGroup>
<TargetFrameworks>net45;netcoreapp1.0</TargetFrameworks>
<TargetFrameworks>net45;netcoreapp1.1</TargetFrameworks>
<AssemblyName>Grpc.Examples.Tests</AssemblyName>
<OutputType>Exe</OutputType>
<PackageId>Grpc.Examples.Tests</PackageId>

@ -34,7 +34,7 @@ namespace Grpc.Examples.Tests
{
// Make logger immune to NUnit capturing stdout and stderr to workaround https://github.com/nunit/nunit/issues/1406.
GrpcEnvironment.SetLogger(new ConsoleLogger());
#if NETCOREAPP1_0
#if NETCOREAPP1_1 || NETCOREAPP2_1
return new AutoRun(typeof(NUnitMain).GetTypeInfo().Assembly).Execute(args, new ExtendedTextWrapper(Console.Out), Console.In);
#else
return new AutoRun().Execute(args);

@ -4,7 +4,7 @@
<Import Project="..\Grpc.Core\Common.csproj.include" />
<PropertyGroup>
<TargetFrameworks>net45;netcoreapp1.0</TargetFrameworks>
<TargetFrameworks>net45;netcoreapp1.1</TargetFrameworks>
<AssemblyName>Grpc.Examples</AssemblyName>
<PackageId>Grpc.Examples</PackageId>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>

@ -4,7 +4,7 @@
<Import Project="..\Grpc.Core\Common.csproj.include" />
<PropertyGroup>
<TargetFrameworks>net45;netcoreapp1.0</TargetFrameworks>
<TargetFrameworks>net45;netcoreapp1.1</TargetFrameworks>
<AssemblyName>Grpc.HealthCheck.Tests</AssemblyName>
<OutputType>Exe</OutputType>
<PackageId>Grpc.HealthCheck.Tests</PackageId>

@ -34,7 +34,7 @@ namespace Grpc.HealthCheck.Tests
{
// Make logger immune to NUnit capturing stdout and stderr to workaround https://github.com/nunit/nunit/issues/1406.
GrpcEnvironment.SetLogger(new ConsoleLogger());
#if NETCOREAPP1_0
#if NETCOREAPP1_1 || NETCOREAPP2_1
return new AutoRun(typeof(NUnitMain).GetTypeInfo().Assembly).Execute(args, new ExtendedTextWrapper(Console.Out), Console.In);
#else
return new AutoRun().Execute(args);

@ -4,7 +4,7 @@
<Import Project="..\Grpc.Core\Common.csproj.include" />
<PropertyGroup>
<TargetFrameworks>net45;netcoreapp1.0</TargetFrameworks>
<TargetFrameworks>net45;netcoreapp1.1</TargetFrameworks>
<AssemblyName>Grpc.IntegrationTesting.Client</AssemblyName>
<OutputType>Exe</OutputType>
<PackageId>Grpc.IntegrationTesting.Client</PackageId>

@ -4,7 +4,7 @@
<Import Project="..\Grpc.Core\Common.csproj.include" />
<PropertyGroup>
<TargetFrameworks>net45;netcoreapp1.0</TargetFrameworks>
<TargetFrameworks>net45;netcoreapp1.1</TargetFrameworks>
<AssemblyName>Grpc.IntegrationTesting.QpsWorker</AssemblyName>
<OutputType>Exe</OutputType>
<PackageId>Grpc.IntegrationTesting.QpsWorker</PackageId>

@ -4,7 +4,7 @@
<Import Project="..\Grpc.Core\Common.csproj.include" />
<PropertyGroup>
<TargetFrameworks>net45;netcoreapp1.0</TargetFrameworks>
<TargetFrameworks>net45;netcoreapp1.1</TargetFrameworks>
<AssemblyName>Grpc.IntegrationTesting.Server</AssemblyName>
<OutputType>Exe</OutputType>
<PackageId>Grpc.IntegrationTesting.Server</PackageId>
@ -19,7 +19,7 @@
<Reference Include="System" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Grpc.Core\Version.cs" />
</ItemGroup>

@ -4,7 +4,7 @@
<Import Project="..\Grpc.Core\Common.csproj.include" />
<PropertyGroup>
<TargetFrameworks>net45;netcoreapp1.0</TargetFrameworks>
<TargetFrameworks>net45;netcoreapp1.1</TargetFrameworks>
<AssemblyName>Grpc.IntegrationTesting.StressClient</AssemblyName>
<OutputType>Exe</OutputType>
<PackageId>Grpc.IntegrationTesting.StressClient</PackageId>

@ -4,7 +4,7 @@
<Import Project="..\Grpc.Core\Common.csproj.include" />
<PropertyGroup>
<TargetFrameworks>net45;netcoreapp1.0</TargetFrameworks>
<TargetFrameworks>net45;netcoreapp1.1</TargetFrameworks>
<AssemblyName>Grpc.IntegrationTesting</AssemblyName>
<OutputType>Exe</OutputType>
<PackageId>Grpc.IntegrationTesting</PackageId>

@ -34,7 +34,7 @@ namespace Grpc.IntegrationTesting
{
// Make logger immune to NUnit capturing stdout and stderr to workaround https://github.com/nunit/nunit/issues/1406.
GrpcEnvironment.SetLogger(new ConsoleLogger());
#if NETCOREAPP1_0
#if NETCOREAPP1_1 || NETCOREAPP2_1
return new AutoRun(typeof(NUnitMain).GetTypeInfo().Assembly).Execute(args, new ExtendedTextWrapper(Console.Out), Console.In);
#else
return new AutoRun().Execute(args);

@ -4,7 +4,7 @@
<Import Project="..\Grpc.Core\Common.csproj.include" />
<PropertyGroup>
<TargetFrameworks>net45;netcoreapp1.0</TargetFrameworks>
<TargetFrameworks>net45;netcoreapp1.1</TargetFrameworks>
<AssemblyName>Grpc.Microbenchmarks</AssemblyName>
<OutputType>Exe</OutputType>
<PackageId>Grpc.Microbenchmarks</PackageId>

@ -4,7 +4,7 @@
<Import Project="..\Grpc.Core\Common.csproj.include" />
<PropertyGroup>
<TargetFrameworks>net45;netcoreapp1.0</TargetFrameworks>
<TargetFrameworks>net45;netcoreapp1.1</TargetFrameworks>
<AssemblyName>Grpc.Reflection.Tests</AssemblyName>
<OutputType>Exe</OutputType>
<PackageId>Grpc.Reflection.Tests</PackageId>

@ -34,7 +34,7 @@ namespace Grpc.Reflection.Tests
{
// Make logger immune to NUnit capturing stdout and stderr to workaround https://github.com/nunit/nunit/issues/1406.
GrpcEnvironment.SetLogger(new ConsoleLogger());
#if NETCOREAPP1_0
#if NETCOREAPP1_1 || NETCOREAPP2_1
return new AutoRun(typeof(NUnitMain).GetTypeInfo().Assembly).Execute(args, new ExtendedTextWrapper(Console.Out), Console.In);
#else
return new AutoRun().Execute(args);

@ -3,7 +3,7 @@
<Import Project="..\Grpc.Core\Version.csproj.include" />
<PropertyGroup>
<TargetFrameworks>net45;netcoreapp1.0</TargetFrameworks>
<TargetFrameworks>net45;netcoreapp1.1</TargetFrameworks>
<OutputType>Exe</OutputType>
</PropertyGroup>

@ -24,7 +24,7 @@ namespace Grpc.Tools.Tests
static class NUnitMain
{
public static int Main(string[] args) =>
#if NETCOREAPP1_0 || NETCOREAPP1_1
#if NETCOREAPP1_1 || NETCOREAPP2_1
new AutoRun(typeof(NUnitMain).GetTypeInfo().Assembly).Execute(args);
#else
new AutoRun().Execute(args);

@ -170,7 +170,9 @@ void prefork() {
acquire_persistent_locks();
}
void postfork_child(TSRMLS_D) {
void postfork_child() {
TSRMLS_FETCH();
// loop through persistant list and destroy all underlying grpc_channel objs
destroy_grpc_channels();

@ -24,3 +24,10 @@ grpc_proto_library(
has_services = True,
well_known_protos = True,
)
filegroup(
name = "channelz_proto_file",
srcs = [
"channelz.proto",
],
)

@ -29,4 +29,3 @@ filegroup(
"health.proto",
],
)

@ -20,6 +20,9 @@
syntax = "proto3";
// Ignored detached comment
// The comments in this file are not meant for readability
// but rather to test to make sure that the code generator
// properly preserves comments on files, services, and RPCs
// Ignored package leading comment
package grpc.testing;

@ -1723,7 +1723,7 @@ def server(thread_pool,
handlers. The interceptors are given control in the order they are
specified. This is an EXPERIMENTAL API.
options: An optional list of key-value pairs (channel args in gRPC runtime)
to configure the channel.
to configure the channel.
maximum_concurrent_rpcs: The maximum number of concurrent RPCs this server
will service before returning RESOURCE_EXHAUSTED status, or None to
indicate no limit.

@ -12,6 +12,7 @@ pyx_library(
"_cygrpc/grpc_string.pyx.pxi",
"_cygrpc/arguments.pyx.pxi",
"_cygrpc/call.pyx.pxi",
"_cygrpc/channelz.pyx.pxi",
"_cygrpc/channel.pyx.pxi",
"_cygrpc/credentials.pyx.pxi",
"_cygrpc/completion_queue.pyx.pxi",

@ -0,0 +1,69 @@
# 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.
def channelz_get_top_channels(start_channel_id):
cdef char *c_returned_str = grpc_channelz_get_top_channels(
start_channel_id,
)
if c_returned_str == NULL:
raise ValueError('Failed to get top channels, please ensure your' \
' start_channel_id==%s is valid' % start_channel_id)
return c_returned_str
def channelz_get_servers(start_server_id):
cdef char *c_returned_str = grpc_channelz_get_servers(start_server_id)
if c_returned_str == NULL:
raise ValueError('Failed to get servers, please ensure your' \
' start_server_id==%s is valid' % start_server_id)
return c_returned_str
def channelz_get_server(server_id):
cdef char *c_returned_str = grpc_channelz_get_server(server_id)
if c_returned_str == NULL:
raise ValueError('Failed to get the server, please ensure your' \
' server_id==%s is valid' % server_id)
return c_returned_str
def channelz_get_server_sockets(server_id, start_socket_id):
cdef char *c_returned_str = grpc_channelz_get_server_sockets(
server_id,
start_socket_id,
)
if c_returned_str == NULL:
raise ValueError('Failed to get server sockets, please ensure your' \
' server_id==%s and start_socket_id==%s is valid' %
(server_id, start_socket_id))
return c_returned_str
def channelz_get_channel(channel_id):
cdef char *c_returned_str = grpc_channelz_get_channel(channel_id)
if c_returned_str == NULL:
raise ValueError('Failed to get the channel, please ensure your' \
' channel_id==%s is valid' % (channel_id))
return c_returned_str
def channelz_get_subchannel(subchannel_id):
cdef char *c_returned_str = grpc_channelz_get_subchannel(subchannel_id)
if c_returned_str == NULL:
raise ValueError('Failed to get the subchannel, please ensure your' \
' subchannel_id==%s is valid' % (subchannel_id))
return c_returned_str
def channelz_get_socket(socket_id):
cdef char *c_returned_str = grpc_channelz_get_socket(socket_id)
if c_returned_str == NULL:
raise ValueError('Failed to get the socket, please ensure your' \
' socket_id==%s is valid' % (socket_id))
return c_returned_str

@ -13,6 +13,7 @@
# limitations under the License.
cimport libc.time
from libc.stdint cimport intptr_t
# Typedef types with approximately the same semantics to provide their names to
@ -384,6 +385,15 @@ cdef extern from "grpc/grpc.h":
void grpc_server_cancel_all_calls(grpc_server *server) nogil
void grpc_server_destroy(grpc_server *server) nogil
char* grpc_channelz_get_top_channels(intptr_t start_channel_id)
char* grpc_channelz_get_servers(intptr_t start_server_id)
char* grpc_channelz_get_server(intptr_t server_id)
char* grpc_channelz_get_server_sockets(intptr_t server_id,
intptr_t start_socket_id)
char* grpc_channelz_get_channel(intptr_t channel_id)
char* grpc_channelz_get_subchannel(intptr_t subchannel_id)
char* grpc_channelz_get_socket(intptr_t socket_id)
cdef extern from "grpc/grpc_security.h":

@ -35,6 +35,7 @@ include "_cygrpc/server.pyx.pxi"
include "_cygrpc/tag.pyx.pxi"
include "_cygrpc/time.pyx.pxi"
include "_cygrpc/_hooks.pyx.pxi"
include "_cygrpc/channelz.pyx.pxi"
include "_cygrpc/grpc_gevent.pyx.pxi"

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save