Merge pull request #8239 from ctiller/buffer_pools_for_realsies

Resource quotas
pull/8430/head^2
Craig Tiller 8 years ago committed by GitHub
commit 6628e36a89
  1. 18
      BUILD
  2. 9
      CMakeLists.txt
  3. 48
      Makefile
  4. 1
      binding.gyp
  5. 16
      build.yaml
  6. 1
      config.m4
  7. 5
      gRPC-Core.podspec
  8. 5
      grpc.def
  9. 3
      grpc.gemspec
  10. 70
      include/grpc++/resource_quota.h
  11. 8
      include/grpc++/server_builder.h
  12. 8
      include/grpc++/support/channel_arguments.h
  13. 17
      include/grpc/grpc.h
  14. 5
      include/grpc/impl/codegen/grpc_types.h
  15. 3
      package.xml
  16. 4
      src/core/ext/client_channel/client_channel.c
  17. 3
      src/core/ext/client_channel/subchannel.c
  18. 2
      src/core/ext/client_channel/subchannel.h
  19. 6
      src/core/ext/lb_policy/pick_first/pick_first.c
  20. 13
      src/core/ext/lb_policy/round_robin/round_robin.c
  21. 3
      src/core/ext/transport/chttp2/client/insecure/channel_create.c
  22. 6
      src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c
  23. 6
      src/core/ext/transport/chttp2/client/secure/secure_channel_create.c
  24. 4
      src/core/ext/transport/chttp2/server/insecure/server_chttp2.c
  25. 8
      src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c
  26. 3
      src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c
  27. 169
      src/core/ext/transport/chttp2/transport/chttp2_transport.c
  28. 20
      src/core/ext/transport/chttp2/transport/internal.h
  29. 11
      src/core/ext/transport/chttp2/transport/stream_map.c
  30. 3
      src/core/ext/transport/chttp2/transport/stream_map.h
  31. 22
      src/core/lib/http/httpcli.c
  32. 2
      src/core/lib/http/httpcli.h
  33. 4
      src/core/lib/iomgr/endpoint.c
  34. 4
      src/core/lib/iomgr/endpoint.h
  35. 5
      src/core/lib/iomgr/endpoint_pair.h
  36. 13
      src/core/lib/iomgr/endpoint_pair_posix.c
  37. 714
      src/core/lib/iomgr/resource_quota.c
  38. 224
      src/core/lib/iomgr/resource_quota.h
  39. 5
      src/core/lib/iomgr/tcp_client.h
  40. 50
      src/core/lib/iomgr/tcp_client_posix.c
  41. 45
      src/core/lib/iomgr/tcp_client_posix.h
  42. 80
      src/core/lib/iomgr/tcp_posix.c
  43. 4
      src/core/lib/iomgr/tcp_posix.h
  44. 3
      src/core/lib/iomgr/tcp_server.h
  45. 23
      src/core/lib/iomgr/tcp_server_posix.c
  46. 5
      src/core/lib/security/credentials/google_default/google_default_credentials.c
  47. 16
      src/core/lib/security/credentials/jwt/jwt_verifier.c
  48. 20
      src/core/lib/security/credentials/oauth2/oauth2_credentials.c
  49. 7
      src/core/lib/security/transport/secure_endpoint.c
  50. 6
      src/core/lib/surface/call.c
  51. 2
      src/core/lib/surface/init.c
  52. 16
      src/cpp/common/channel_arguments.cc
  53. 51
      src/cpp/common/resource_quota_cc.cc
  54. 22
      src/cpp/server/server_builder.cc
  55. 11
      src/proto/grpc/testing/control.proto
  56. 8
      src/proto/grpc/testing/stats.proto
  57. 1
      src/python/grpcio/grpc_core_dependencies.py
  58. 10
      src/ruby/ext/grpc/rb_grpc_imports.generated.c
  59. 15
      src/ruby/ext/grpc/rb_grpc_imports.generated.h
  60. 5
      test/core/bad_client/bad_client.c
  61. 8
      test/core/end2end/end2end_nosec_tests.c
  62. 8
      test/core/end2end/end2end_tests.c
  63. 4
      test/core/end2end/fixtures/h2_sockpair+trace.c
  64. 4
      test/core/end2end/fixtures/h2_sockpair.c
  65. 4
      test/core/end2end/fixtures/h2_sockpair_1byte.c
  66. 10
      test/core/end2end/fixtures/http_proxy.c
  67. 16
      test/core/end2end/fuzzers/api_fuzzer.c
  68. 6
      test/core/end2end/fuzzers/client_fuzzer.c
  69. 6
      test/core/end2end/fuzzers/server_fuzzer.c
  70. 24
      test/core/end2end/gen_build_yaml.py
  71. 1
      test/core/end2end/tests/max_message_length.c
  72. 1
      test/core/end2end/tests/network_status_change.c
  73. 359
      test/core/end2end/tests/resource_quota_server.c
  74. 11
      test/core/http/httpcli_test.c
  75. 11
      test/core/http/httpscli_test.c
  76. 1
      test/core/internal_api_canaries/iomgr.c
  77. 6
      test/core/iomgr/endpoint_pair_test.c
  78. 6
      test/core/iomgr/fd_conservation_posix_test.c
  79. 749
      test/core/iomgr/resource_quota_test.c
  80. 4
      test/core/iomgr/tcp_client_posix_test.c
  81. 36
      test/core/iomgr/tcp_posix_test.c
  82. 15
      test/core/iomgr/tcp_server_posix_test.c
  83. 5
      test/core/security/secure_endpoint_test.c
  84. 2
      test/core/surface/concurrent_connectivity_test.c
  85. 41
      test/core/util/mock_endpoint.c
  86. 3
      test/core/util/mock_endpoint.h
  87. 34
      test/core/util/passthru_endpoint.c
  88. 3
      test/core/util/passthru_endpoint.h
  89. 20
      test/core/util/port_server_client.c
  90. 4
      test/core/util/test_tcp_server.c
  91. 39
      test/cpp/end2end/end2end_test.cc
  92. 57
      test/cpp/qps/client.h
  93. 11
      test/cpp/qps/client_async.cc
  94. 7
      test/cpp/qps/client_sync.cc
  95. 31
      test/cpp/qps/driver.cc
  96. 6
      test/cpp/qps/report.cc
  97. 6
      test/cpp/qps/server_async.cc
  98. 6
      test/cpp/qps/server_sync.cc
  99. 1
      tools/doxygen/Doxyfile.c++
  100. 2
      tools/doxygen/Doxyfile.c++.internal
  101. Some files were not shown because too many files have changed in this diff Show More

18
BUILD

@ -204,6 +204,7 @@ cc_library(
"src/core/lib/iomgr/pollset_windows.h",
"src/core/lib/iomgr/port.h",
"src/core/lib/iomgr/resolve_address.h",
"src/core/lib/iomgr/resource_quota.h",
"src/core/lib/iomgr/sockaddr.h",
"src/core/lib/iomgr/sockaddr_posix.h",
"src/core/lib/iomgr/sockaddr_utils.h",
@ -212,6 +213,7 @@ cc_library(
"src/core/lib/iomgr/socket_utils_posix.h",
"src/core/lib/iomgr/socket_windows.h",
"src/core/lib/iomgr/tcp_client.h",
"src/core/lib/iomgr/tcp_client_posix.h",
"src/core/lib/iomgr/tcp_posix.h",
"src/core/lib/iomgr/tcp_server.h",
"src/core/lib/iomgr/tcp_uv.h",
@ -375,6 +377,7 @@ cc_library(
"src/core/lib/iomgr/resolve_address_posix.c",
"src/core/lib/iomgr/resolve_address_uv.c",
"src/core/lib/iomgr/resolve_address_windows.c",
"src/core/lib/iomgr/resource_quota.c",
"src/core/lib/iomgr/sockaddr_utils.c",
"src/core/lib/iomgr/socket_utils_common_posix.c",
"src/core/lib/iomgr/socket_utils_linux.c",
@ -625,6 +628,7 @@ cc_library(
"src/core/lib/iomgr/pollset_windows.h",
"src/core/lib/iomgr/port.h",
"src/core/lib/iomgr/resolve_address.h",
"src/core/lib/iomgr/resource_quota.h",
"src/core/lib/iomgr/sockaddr.h",
"src/core/lib/iomgr/sockaddr_posix.h",
"src/core/lib/iomgr/sockaddr_utils.h",
@ -633,6 +637,7 @@ cc_library(
"src/core/lib/iomgr/socket_utils_posix.h",
"src/core/lib/iomgr/socket_windows.h",
"src/core/lib/iomgr/tcp_client.h",
"src/core/lib/iomgr/tcp_client_posix.h",
"src/core/lib/iomgr/tcp_posix.h",
"src/core/lib/iomgr/tcp_server.h",
"src/core/lib/iomgr/tcp_uv.h",
@ -781,6 +786,7 @@ cc_library(
"src/core/lib/iomgr/resolve_address_posix.c",
"src/core/lib/iomgr/resolve_address_uv.c",
"src/core/lib/iomgr/resolve_address_windows.c",
"src/core/lib/iomgr/resource_quota.c",
"src/core/lib/iomgr/sockaddr_utils.c",
"src/core/lib/iomgr/socket_utils_common_posix.c",
"src/core/lib/iomgr/socket_utils_linux.c",
@ -1001,6 +1007,7 @@ cc_library(
"src/core/lib/iomgr/pollset_windows.h",
"src/core/lib/iomgr/port.h",
"src/core/lib/iomgr/resolve_address.h",
"src/core/lib/iomgr/resource_quota.h",
"src/core/lib/iomgr/sockaddr.h",
"src/core/lib/iomgr/sockaddr_posix.h",
"src/core/lib/iomgr/sockaddr_utils.h",
@ -1009,6 +1016,7 @@ cc_library(
"src/core/lib/iomgr/socket_utils_posix.h",
"src/core/lib/iomgr/socket_windows.h",
"src/core/lib/iomgr/tcp_client.h",
"src/core/lib/iomgr/tcp_client_posix.h",
"src/core/lib/iomgr/tcp_posix.h",
"src/core/lib/iomgr/tcp_server.h",
"src/core/lib/iomgr/tcp_uv.h",
@ -1149,6 +1157,7 @@ cc_library(
"src/core/lib/iomgr/resolve_address_posix.c",
"src/core/lib/iomgr/resolve_address_uv.c",
"src/core/lib/iomgr/resolve_address_windows.c",
"src/core/lib/iomgr/resource_quota.c",
"src/core/lib/iomgr/sockaddr_utils.c",
"src/core/lib/iomgr/socket_utils_common_posix.c",
"src/core/lib/iomgr/socket_utils_linux.c",
@ -1352,6 +1361,7 @@ cc_library(
"src/cpp/common/channel_filter.cc",
"src/cpp/common/completion_queue_cc.cc",
"src/cpp/common/core_codegen.cc",
"src/cpp/common/resource_quota_cc.cc",
"src/cpp/common/rpc_method.cc",
"src/cpp/server/async_generic_service.cc",
"src/cpp/server/create_default_thread_pool.cc",
@ -1396,6 +1406,7 @@ cc_library(
"include/grpc++/impl/thd.h",
"include/grpc++/impl/thd_cxx11.h",
"include/grpc++/impl/thd_no_cxx11.h",
"include/grpc++/resource_quota.h",
"include/grpc++/security/auth_context.h",
"include/grpc++/security/auth_metadata_processor.h",
"include/grpc++/security/credentials.h",
@ -1500,6 +1511,7 @@ cc_library(
"src/cpp/common/channel_filter.cc",
"src/cpp/common/completion_queue_cc.cc",
"src/cpp/common/core_codegen.cc",
"src/cpp/common/resource_quota_cc.cc",
"src/cpp/common/rpc_method.cc",
"src/cpp/server/async_generic_service.cc",
"src/cpp/server/create_default_thread_pool.cc",
@ -1544,6 +1556,7 @@ cc_library(
"include/grpc++/impl/thd.h",
"include/grpc++/impl/thd_cxx11.h",
"include/grpc++/impl/thd_no_cxx11.h",
"include/grpc++/resource_quota.h",
"include/grpc++/security/auth_context.h",
"include/grpc++/security/auth_metadata_processor.h",
"include/grpc++/security/credentials.h",
@ -1722,6 +1735,7 @@ cc_library(
"src/cpp/common/channel_filter.cc",
"src/cpp/common/completion_queue_cc.cc",
"src/cpp/common/core_codegen.cc",
"src/cpp/common/resource_quota_cc.cc",
"src/cpp/common/rpc_method.cc",
"src/cpp/server/async_generic_service.cc",
"src/cpp/server/create_default_thread_pool.cc",
@ -1766,6 +1780,7 @@ cc_library(
"include/grpc++/impl/thd.h",
"include/grpc++/impl/thd_cxx11.h",
"include/grpc++/impl/thd_no_cxx11.h",
"include/grpc++/resource_quota.h",
"include/grpc++/security/auth_context.h",
"include/grpc++/security/auth_metadata_processor.h",
"include/grpc++/security/credentials.h",
@ -2073,6 +2088,7 @@ objc_library(
"src/core/lib/iomgr/resolve_address_posix.c",
"src/core/lib/iomgr/resolve_address_uv.c",
"src/core/lib/iomgr/resolve_address_windows.c",
"src/core/lib/iomgr/resource_quota.c",
"src/core/lib/iomgr/sockaddr_utils.c",
"src/core/lib/iomgr/socket_utils_common_posix.c",
"src/core/lib/iomgr/socket_utils_linux.c",
@ -2302,6 +2318,7 @@ objc_library(
"src/core/lib/iomgr/pollset_windows.h",
"src/core/lib/iomgr/port.h",
"src/core/lib/iomgr/resolve_address.h",
"src/core/lib/iomgr/resource_quota.h",
"src/core/lib/iomgr/sockaddr.h",
"src/core/lib/iomgr/sockaddr_posix.h",
"src/core/lib/iomgr/sockaddr_utils.h",
@ -2310,6 +2327,7 @@ objc_library(
"src/core/lib/iomgr/socket_utils_posix.h",
"src/core/lib/iomgr/socket_windows.h",
"src/core/lib/iomgr/tcp_client.h",
"src/core/lib/iomgr/tcp_client_posix.h",
"src/core/lib/iomgr/tcp_posix.h",
"src/core/lib/iomgr/tcp_server.h",
"src/core/lib/iomgr/tcp_uv.h",

@ -334,6 +334,7 @@ add_library(grpc
src/core/lib/iomgr/resolve_address_posix.c
src/core/lib/iomgr/resolve_address_uv.c
src/core/lib/iomgr/resolve_address_windows.c
src/core/lib/iomgr/resource_quota.c
src/core/lib/iomgr/sockaddr_utils.c
src/core/lib/iomgr/socket_utils_common_posix.c
src/core/lib/iomgr/socket_utils_linux.c
@ -605,6 +606,7 @@ add_library(grpc_cronet
src/core/lib/iomgr/resolve_address_posix.c
src/core/lib/iomgr/resolve_address_uv.c
src/core/lib/iomgr/resolve_address_windows.c
src/core/lib/iomgr/resource_quota.c
src/core/lib/iomgr/sockaddr_utils.c
src/core/lib/iomgr/socket_utils_common_posix.c
src/core/lib/iomgr/socket_utils_linux.c
@ -848,6 +850,7 @@ add_library(grpc_unsecure
src/core/lib/iomgr/resolve_address_posix.c
src/core/lib/iomgr/resolve_address_uv.c
src/core/lib/iomgr/resolve_address_windows.c
src/core/lib/iomgr/resource_quota.c
src/core/lib/iomgr/sockaddr_utils.c
src/core/lib/iomgr/socket_utils_common_posix.c
src/core/lib/iomgr/socket_utils_linux.c
@ -1061,6 +1064,7 @@ add_library(grpc++
src/cpp/common/channel_filter.cc
src/cpp/common/completion_queue_cc.cc
src/cpp/common/core_codegen.cc
src/cpp/common/resource_quota_cc.cc
src/cpp/common/rpc_method.cc
src/cpp/server/async_generic_service.cc
src/cpp/server/create_default_thread_pool.cc
@ -1122,6 +1126,7 @@ foreach(_hdr
include/grpc++/impl/thd.h
include/grpc++/impl/thd_cxx11.h
include/grpc++/impl/thd_no_cxx11.h
include/grpc++/resource_quota.h
include/grpc++/security/auth_context.h
include/grpc++/security/auth_metadata_processor.h
include/grpc++/security/credentials.h
@ -1224,6 +1229,7 @@ add_library(grpc++_cronet
src/cpp/common/channel_filter.cc
src/cpp/common/completion_queue_cc.cc
src/cpp/common/core_codegen.cc
src/cpp/common/resource_quota_cc.cc
src/cpp/common/rpc_method.cc
src/cpp/server/async_generic_service.cc
src/cpp/server/create_default_thread_pool.cc
@ -1285,6 +1291,7 @@ foreach(_hdr
include/grpc++/impl/thd.h
include/grpc++/impl/thd_cxx11.h
include/grpc++/impl/thd_no_cxx11.h
include/grpc++/resource_quota.h
include/grpc++/security/auth_context.h
include/grpc++/security/auth_metadata_processor.h
include/grpc++/security/credentials.h
@ -1478,6 +1485,7 @@ add_library(grpc++_unsecure
src/cpp/common/channel_filter.cc
src/cpp/common/completion_queue_cc.cc
src/cpp/common/core_codegen.cc
src/cpp/common/resource_quota_cc.cc
src/cpp/common/rpc_method.cc
src/cpp/server/async_generic_service.cc
src/cpp/server/create_default_thread_pool.cc
@ -1539,6 +1547,7 @@ foreach(_hdr
include/grpc++/impl/thd.h
include/grpc++/impl/thd_cxx11.h
include/grpc++/impl/thd_no_cxx11.h
include/grpc++/resource_quota.h
include/grpc++/security/auth_context.h
include/grpc++/security/auth_metadata_processor.h
include/grpc++/security/credentials.h

@ -1008,6 +1008,7 @@ no_server_test: $(BINDIR)/$(CONFIG)/no_server_test
percent_decode_fuzzer: $(BINDIR)/$(CONFIG)/percent_decode_fuzzer
percent_encode_fuzzer: $(BINDIR)/$(CONFIG)/percent_encode_fuzzer
resolve_address_test: $(BINDIR)/$(CONFIG)/resolve_address_test
resource_quota_test: $(BINDIR)/$(CONFIG)/resource_quota_test
secure_channel_create_test: $(BINDIR)/$(CONFIG)/secure_channel_create_test
secure_endpoint_test: $(BINDIR)/$(CONFIG)/secure_endpoint_test
sequential_connectivity_test: $(BINDIR)/$(CONFIG)/sequential_connectivity_test
@ -1331,6 +1332,7 @@ buildtests_c: privatelibs_c \
$(BINDIR)/$(CONFIG)/murmur_hash_test \
$(BINDIR)/$(CONFIG)/no_server_test \
$(BINDIR)/$(CONFIG)/resolve_address_test \
$(BINDIR)/$(CONFIG)/resource_quota_test \
$(BINDIR)/$(CONFIG)/secure_channel_create_test \
$(BINDIR)/$(CONFIG)/secure_endpoint_test \
$(BINDIR)/$(CONFIG)/sequential_connectivity_test \
@ -1717,6 +1719,8 @@ test_c: buildtests_c
$(Q) $(BINDIR)/$(CONFIG)/no_server_test || ( echo test no_server_test failed ; exit 1 )
$(E) "[RUN] Testing resolve_address_test"
$(Q) $(BINDIR)/$(CONFIG)/resolve_address_test || ( echo test resolve_address_test failed ; exit 1 )
$(E) "[RUN] Testing resource_quota_test"
$(Q) $(BINDIR)/$(CONFIG)/resource_quota_test || ( echo test resource_quota_test failed ; exit 1 )
$(E) "[RUN] Testing secure_channel_create_test"
$(Q) $(BINDIR)/$(CONFIG)/secure_channel_create_test || ( echo test secure_channel_create_test failed ; exit 1 )
$(E) "[RUN] Testing secure_endpoint_test"
@ -2619,6 +2623,7 @@ LIBGRPC_SRC = \
src/core/lib/iomgr/resolve_address_posix.c \
src/core/lib/iomgr/resolve_address_uv.c \
src/core/lib/iomgr/resolve_address_windows.c \
src/core/lib/iomgr/resource_quota.c \
src/core/lib/iomgr/sockaddr_utils.c \
src/core/lib/iomgr/socket_utils_common_posix.c \
src/core/lib/iomgr/socket_utils_linux.c \
@ -2908,6 +2913,7 @@ LIBGRPC_CRONET_SRC = \
src/core/lib/iomgr/resolve_address_posix.c \
src/core/lib/iomgr/resolve_address_uv.c \
src/core/lib/iomgr/resolve_address_windows.c \
src/core/lib/iomgr/resource_quota.c \
src/core/lib/iomgr/sockaddr_utils.c \
src/core/lib/iomgr/socket_utils_common_posix.c \
src/core/lib/iomgr/socket_utils_linux.c \
@ -3188,6 +3194,7 @@ LIBGRPC_TEST_UTIL_SRC = \
src/core/lib/iomgr/resolve_address_posix.c \
src/core/lib/iomgr/resolve_address_uv.c \
src/core/lib/iomgr/resolve_address_windows.c \
src/core/lib/iomgr/resource_quota.c \
src/core/lib/iomgr/sockaddr_utils.c \
src/core/lib/iomgr/socket_utils_common_posix.c \
src/core/lib/iomgr/socket_utils_linux.c \
@ -3397,6 +3404,7 @@ LIBGRPC_UNSECURE_SRC = \
src/core/lib/iomgr/resolve_address_posix.c \
src/core/lib/iomgr/resolve_address_uv.c \
src/core/lib/iomgr/resolve_address_windows.c \
src/core/lib/iomgr/resource_quota.c \
src/core/lib/iomgr/sockaddr_utils.c \
src/core/lib/iomgr/socket_utils_common_posix.c \
src/core/lib/iomgr/socket_utils_linux.c \
@ -3693,6 +3701,7 @@ LIBGRPC++_SRC = \
src/cpp/common/channel_filter.cc \
src/cpp/common/completion_queue_cc.cc \
src/cpp/common/core_codegen.cc \
src/cpp/common/resource_quota_cc.cc \
src/cpp/common/rpc_method.cc \
src/cpp/server/async_generic_service.cc \
src/cpp/server/create_default_thread_pool.cc \
@ -3737,6 +3746,7 @@ PUBLIC_HEADERS_CXX += \
include/grpc++/impl/thd.h \
include/grpc++/impl/thd_cxx11.h \
include/grpc++/impl/thd_no_cxx11.h \
include/grpc++/resource_quota.h \
include/grpc++/security/auth_context.h \
include/grpc++/security/auth_metadata_processor.h \
include/grpc++/security/credentials.h \
@ -3885,6 +3895,7 @@ LIBGRPC++_CRONET_SRC = \
src/cpp/common/channel_filter.cc \
src/cpp/common/completion_queue_cc.cc \
src/cpp/common/core_codegen.cc \
src/cpp/common/resource_quota_cc.cc \
src/cpp/common/rpc_method.cc \
src/cpp/server/async_generic_service.cc \
src/cpp/server/create_default_thread_pool.cc \
@ -3929,6 +3940,7 @@ PUBLIC_HEADERS_CXX += \
include/grpc++/impl/thd.h \
include/grpc++/impl/thd_cxx11.h \
include/grpc++/impl/thd_no_cxx11.h \
include/grpc++/resource_quota.h \
include/grpc++/security/auth_context.h \
include/grpc++/security/auth_metadata_processor.h \
include/grpc++/security/credentials.h \
@ -4464,6 +4476,7 @@ LIBGRPC++_UNSECURE_SRC = \
src/cpp/common/channel_filter.cc \
src/cpp/common/completion_queue_cc.cc \
src/cpp/common/core_codegen.cc \
src/cpp/common/resource_quota_cc.cc \
src/cpp/common/rpc_method.cc \
src/cpp/server/async_generic_service.cc \
src/cpp/server/create_default_thread_pool.cc \
@ -4508,6 +4521,7 @@ PUBLIC_HEADERS_CXX += \
include/grpc++/impl/thd.h \
include/grpc++/impl/thd_cxx11.h \
include/grpc++/impl/thd_no_cxx11.h \
include/grpc++/resource_quota.h \
include/grpc++/security/auth_context.h \
include/grpc++/security/auth_metadata_processor.h \
include/grpc++/security/credentials.h \
@ -6942,6 +6956,7 @@ LIBEND2END_TESTS_SRC = \
test/core/end2end/tests/registered_call.c \
test/core/end2end/tests/request_with_flags.c \
test/core/end2end/tests/request_with_payload.c \
test/core/end2end/tests/resource_quota_server.c \
test/core/end2end/tests/server_finishes_request.c \
test/core/end2end/tests/shutdown_finishes_calls.c \
test/core/end2end/tests/shutdown_finishes_tags.c \
@ -7024,6 +7039,7 @@ LIBEND2END_NOSEC_TESTS_SRC = \
test/core/end2end/tests/registered_call.c \
test/core/end2end/tests/request_with_flags.c \
test/core/end2end/tests/request_with_payload.c \
test/core/end2end/tests/resource_quota_server.c \
test/core/end2end/tests/server_finishes_request.c \
test/core/end2end/tests/shutdown_finishes_calls.c \
test/core/end2end/tests/shutdown_finishes_tags.c \
@ -10420,6 +10436,38 @@ endif
endif
RESOURCE_QUOTA_TEST_SRC = \
test/core/iomgr/resource_quota_test.c \
RESOURCE_QUOTA_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(RESOURCE_QUOTA_TEST_SRC))))
ifeq ($(NO_SECURE),true)
# You can't build secure targets if you don't have OpenSSL.
$(BINDIR)/$(CONFIG)/resource_quota_test: openssl_dep_error
else
$(BINDIR)/$(CONFIG)/resource_quota_test: $(RESOURCE_QUOTA_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
$(E) "[LD] Linking $@"
$(Q) mkdir -p `dirname $@`
$(Q) $(LD) $(LDFLAGS) $(RESOURCE_QUOTA_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/resource_quota_test
endif
$(OBJDIR)/$(CONFIG)/test/core/iomgr/resource_quota_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
deps_resource_quota_test: $(RESOURCE_QUOTA_TEST_OBJS:.o=.dep)
ifneq ($(NO_SECURE),true)
ifneq ($(NO_DEPS),true)
-include $(RESOURCE_QUOTA_TEST_OBJS:.o=.dep)
endif
endif
SECURE_CHANNEL_CREATE_TEST_SRC = \
test/core/surface/secure_channel_create_test.c \

@ -612,6 +612,7 @@
'src/core/lib/iomgr/resolve_address_posix.c',
'src/core/lib/iomgr/resolve_address_uv.c',
'src/core/lib/iomgr/resolve_address_windows.c',
'src/core/lib/iomgr/resource_quota.c',
'src/core/lib/iomgr/sockaddr_utils.c',
'src/core/lib/iomgr/socket_utils_common_posix.c',
'src/core/lib/iomgr/socket_utils_linux.c',

@ -208,6 +208,7 @@ filegroups:
- src/core/lib/iomgr/pollset_windows.h
- src/core/lib/iomgr/port.h
- src/core/lib/iomgr/resolve_address.h
- src/core/lib/iomgr/resource_quota.h
- src/core/lib/iomgr/sockaddr.h
- src/core/lib/iomgr/sockaddr_posix.h
- src/core/lib/iomgr/sockaddr_utils.h
@ -216,6 +217,7 @@ filegroups:
- src/core/lib/iomgr/socket_utils_posix.h
- src/core/lib/iomgr/socket_windows.h
- src/core/lib/iomgr/tcp_client.h
- src/core/lib/iomgr/tcp_client_posix.h
- src/core/lib/iomgr/tcp_posix.h
- src/core/lib/iomgr/tcp_server.h
- src/core/lib/iomgr/tcp_uv.h
@ -303,6 +305,7 @@ filegroups:
- src/core/lib/iomgr/resolve_address_posix.c
- src/core/lib/iomgr/resolve_address_uv.c
- src/core/lib/iomgr/resolve_address_windows.c
- src/core/lib/iomgr/resource_quota.c
- src/core/lib/iomgr/sockaddr_utils.c
- src/core/lib/iomgr/socket_utils_common_posix.c
- src/core/lib/iomgr/socket_utils_linux.c
@ -713,6 +716,7 @@ filegroups:
- include/grpc++/impl/thd.h
- include/grpc++/impl/thd_cxx11.h
- include/grpc++/impl/thd_no_cxx11.h
- include/grpc++/resource_quota.h
- include/grpc++/security/auth_context.h
- include/grpc++/security/auth_metadata_processor.h
- include/grpc++/security/credentials.h
@ -750,6 +754,7 @@ filegroups:
- src/cpp/common/channel_filter.cc
- src/cpp/common/completion_queue_cc.cc
- src/cpp/common/core_codegen.cc
- src/cpp/common/resource_quota_cc.cc
- src/cpp/common/rpc_method.cc
- src/cpp/server/async_generic_service.cc
- src/cpp/server/create_default_thread_pool.cc
@ -2427,6 +2432,17 @@ targets:
- gpr
exclude_iomgrs:
- uv
- name: resource_quota_test
cpu_cost: 30
build: test
language: c
src:
- test/core/iomgr/resource_quota_test.c
deps:
- grpc_test_util
- grpc
- gpr_test_util
- gpr
- name: secure_channel_create_test
build: test
language: c

@ -128,6 +128,7 @@ if test "$PHP_GRPC" != "no"; then
src/core/lib/iomgr/resolve_address_posix.c \
src/core/lib/iomgr/resolve_address_uv.c \
src/core/lib/iomgr/resolve_address_windows.c \
src/core/lib/iomgr/resource_quota.c \
src/core/lib/iomgr/sockaddr_utils.c \
src/core/lib/iomgr/socket_utils_common_posix.c \
src/core/lib/iomgr/socket_utils_linux.c \

@ -291,6 +291,7 @@ Pod::Spec.new do |s|
'src/core/lib/iomgr/pollset_windows.h',
'src/core/lib/iomgr/port.h',
'src/core/lib/iomgr/resolve_address.h',
'src/core/lib/iomgr/resource_quota.h',
'src/core/lib/iomgr/sockaddr.h',
'src/core/lib/iomgr/sockaddr_posix.h',
'src/core/lib/iomgr/sockaddr_utils.h',
@ -299,6 +300,7 @@ Pod::Spec.new do |s|
'src/core/lib/iomgr/socket_utils_posix.h',
'src/core/lib/iomgr/socket_windows.h',
'src/core/lib/iomgr/tcp_client.h',
'src/core/lib/iomgr/tcp_client_posix.h',
'src/core/lib/iomgr/tcp_posix.h',
'src/core/lib/iomgr/tcp_server.h',
'src/core/lib/iomgr/tcp_uv.h',
@ -466,6 +468,7 @@ Pod::Spec.new do |s|
'src/core/lib/iomgr/resolve_address_posix.c',
'src/core/lib/iomgr/resolve_address_uv.c',
'src/core/lib/iomgr/resolve_address_windows.c',
'src/core/lib/iomgr/resource_quota.c',
'src/core/lib/iomgr/sockaddr_utils.c',
'src/core/lib/iomgr/socket_utils_common_posix.c',
'src/core/lib/iomgr/socket_utils_linux.c',
@ -684,6 +687,7 @@ Pod::Spec.new do |s|
'src/core/lib/iomgr/pollset_windows.h',
'src/core/lib/iomgr/port.h',
'src/core/lib/iomgr/resolve_address.h',
'src/core/lib/iomgr/resource_quota.h',
'src/core/lib/iomgr/sockaddr.h',
'src/core/lib/iomgr/sockaddr_posix.h',
'src/core/lib/iomgr/sockaddr_utils.h',
@ -692,6 +696,7 @@ Pod::Spec.new do |s|
'src/core/lib/iomgr/socket_utils_posix.h',
'src/core/lib/iomgr/socket_windows.h',
'src/core/lib/iomgr/tcp_client.h',
'src/core/lib/iomgr/tcp_client_posix.h',
'src/core/lib/iomgr/tcp_posix.h',
'src/core/lib/iomgr/tcp_server.h',
'src/core/lib/iomgr/tcp_uv.h',

@ -94,6 +94,11 @@ EXPORTS
grpc_header_nonbin_value_is_legal
grpc_is_binary_header
grpc_call_error_to_string
grpc_resource_quota_create
grpc_resource_quota_ref
grpc_resource_quota_unref
grpc_resource_quota_resize
grpc_resource_quota_arg_vtable
grpc_insecure_channel_create_from_fd
grpc_server_add_insecure_channel_from_fd
grpc_use_signal

@ -211,6 +211,7 @@ Gem::Specification.new do |s|
s.files += %w( src/core/lib/iomgr/pollset_windows.h )
s.files += %w( src/core/lib/iomgr/port.h )
s.files += %w( src/core/lib/iomgr/resolve_address.h )
s.files += %w( src/core/lib/iomgr/resource_quota.h )
s.files += %w( src/core/lib/iomgr/sockaddr.h )
s.files += %w( src/core/lib/iomgr/sockaddr_posix.h )
s.files += %w( src/core/lib/iomgr/sockaddr_utils.h )
@ -219,6 +220,7 @@ Gem::Specification.new do |s|
s.files += %w( src/core/lib/iomgr/socket_utils_posix.h )
s.files += %w( src/core/lib/iomgr/socket_windows.h )
s.files += %w( src/core/lib/iomgr/tcp_client.h )
s.files += %w( src/core/lib/iomgr/tcp_client_posix.h )
s.files += %w( src/core/lib/iomgr/tcp_posix.h )
s.files += %w( src/core/lib/iomgr/tcp_server.h )
s.files += %w( src/core/lib/iomgr/tcp_uv.h )
@ -386,6 +388,7 @@ Gem::Specification.new do |s|
s.files += %w( src/core/lib/iomgr/resolve_address_posix.c )
s.files += %w( src/core/lib/iomgr/resolve_address_uv.c )
s.files += %w( src/core/lib/iomgr/resolve_address_windows.c )
s.files += %w( src/core/lib/iomgr/resource_quota.c )
s.files += %w( src/core/lib/iomgr/sockaddr_utils.c )
s.files += %w( src/core/lib/iomgr/socket_utils_common_posix.c )
s.files += %w( src/core/lib/iomgr/socket_utils_linux.c )

@ -0,0 +1,70 @@
/*
*
* Copyright 2016, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef GRPCXX_RESOURCE_QUOTA_H
#define GRPCXX_RESOURCE_QUOTA_H
struct grpc_resource_quota;
#include <grpc++/impl/codegen/config.h>
namespace grpc {
/// ResourceQuota represents a bound on memory usage by the gRPC library.
/// A ResourceQuota can be attached to a server (via ServerBuilder), or a client
/// channel (via ChannelArguments). gRPC will attempt to keep memory used by
/// all attached entities below the ResourceQuota bound.
class ResourceQuota GRPC_FINAL {
public:
explicit ResourceQuota(const grpc::string& name);
ResourceQuota();
~ResourceQuota();
/// Resize this ResourceQuota to a new size. If new_size is smaller than the
/// current size of the pool, memory usage will be monotonically decreased
/// until it falls under new_size. No time bound is given for this to occur
/// however.
ResourceQuota& Resize(size_t new_size);
grpc_resource_quota* c_resource_quota() const { return impl_; }
private:
ResourceQuota(const ResourceQuota& rhs);
ResourceQuota& operator=(const ResourceQuota& rhs);
grpc_resource_quota* const impl_;
};
} // namespace grpc
#endif // GRPCXX_RESOURCE_QUOTA_H

@ -43,9 +43,12 @@
#include <grpc++/support/config.h>
#include <grpc/compression.h>
struct grpc_resource_quota;
namespace grpc {
class AsyncGenericService;
class ResourceQuota;
class CompletionQueue;
class RpcService;
class Server;
@ -61,6 +64,7 @@ class ServerBuilderPluginTest;
class ServerBuilder {
public:
ServerBuilder();
~ServerBuilder();
/// Register a service. This call does not take ownership of the service.
/// The service must exist for the lifetime of the \a Server instance returned
@ -113,6 +117,9 @@ class ServerBuilder {
ServerBuilder& SetDefaultCompressionAlgorithm(
grpc_compression_algorithm algorithm);
/// Set the attached buffer pool for this server
ServerBuilder& SetResourceQuota(const ResourceQuota& resource_quota);
ServerBuilder& SetOption(std::unique_ptr<ServerBuilderOption> option);
/// Tries to bind \a server to the given \a addr.
@ -187,6 +194,7 @@ class ServerBuilder {
std::vector<ServerCompletionQueue*> cqs_;
std::shared_ptr<ServerCredentials> creds_;
std::vector<std::unique_ptr<ServerBuilderPlugin>> plugins_;
grpc_resource_quota* resource_quota_;
AsyncGenericService* generic_service_;
struct {
bool is_set;

@ -46,6 +46,8 @@ namespace testing {
class ChannelArgumentsTest;
} // namespace testing
class ResourceQuota;
/// Options for channel creation. The user can use generic setters to pass
/// key value pairs down to c channel creation code. For grpc related options,
/// concrete setters are provided.
@ -80,6 +82,9 @@ class ChannelArguments {
/// The given string will be sent at the front of the user agent string.
void SetUserAgentPrefix(const grpc::string& user_agent_prefix);
/// The given buffer pool will be attached to the constructed channel
void SetResourceQuota(const ResourceQuota& resource_quota);
// Generic channel argument setters. Only for advanced use cases.
/// Set an integer argument \a value under \a key.
void SetInt(const grpc::string& key, int value);
@ -88,6 +93,9 @@ class ChannelArguments {
/// Set a pointer argument \a value under \a key. Owership is not transferred.
void SetPointer(const grpc::string& key, void* value);
void SetPointerWithVtable(const grpc::string& key, void* value,
const grpc_arg_pointer_vtable* vtable);
/// Set a textual argument \a value under \a key.
void SetString(const grpc::string& key, const grpc::string& value);

@ -401,6 +401,23 @@ GRPCAPI int grpc_is_binary_header(const char *key, size_t length);
/** Convert grpc_call_error values to a string */
GRPCAPI const char *grpc_call_error_to_string(grpc_call_error error);
/** Create a buffer pool */
GRPCAPI grpc_resource_quota *grpc_resource_quota_create(const char *trace_name);
/** Add a reference to a buffer pool */
GRPCAPI void grpc_resource_quota_ref(grpc_resource_quota *resource_quota);
/** Drop a reference to a buffer pool */
GRPCAPI void grpc_resource_quota_unref(grpc_resource_quota *resource_quota);
/** Update the size of a buffer pool */
GRPCAPI void grpc_resource_quota_resize(grpc_resource_quota *resource_quota,
size_t new_size);
/** Fetch a vtable for a grpc_channel_arg that points to a grpc_resource_quota
*/
GRPCAPI const grpc_arg_pointer_vtable *grpc_resource_quota_arg_vtable(void);
#ifdef __cplusplus
}
#endif

@ -201,6 +201,9 @@ typedef struct {
#define GRPC_ARG_MAX_METADATA_SIZE "grpc.max_metadata_size"
/** If non-zero, allow the use of SO_REUSEPORT if it's available (default 1) */
#define GRPC_ARG_ALLOW_REUSEPORT "grpc.so_reuseport"
/** If non-zero, a pointer to a buffer pool (use grpc_resource_quota_arg_vtable
to fetch an appropriate pointer arg vtable */
#define GRPC_ARG_RESOURCE_QUOTA "grpc.resource_quota"
/** Service config data, to be passed to subchannels.
Not intended for external use. */
#define GRPC_ARG_SERVICE_CONFIG "grpc.service_config"
@ -467,6 +470,8 @@ typedef struct grpc_op {
} data;
} grpc_op;
typedef struct grpc_resource_quota grpc_resource_quota;
#ifdef __cplusplus
}
#endif

@ -218,6 +218,7 @@
<file baseinstalldir="/" name="src/core/lib/iomgr/pollset_windows.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/port.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/resolve_address.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/resource_quota.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/sockaddr.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/sockaddr_posix.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/sockaddr_utils.h" role="src" />
@ -226,6 +227,7 @@
<file baseinstalldir="/" name="src/core/lib/iomgr/socket_utils_posix.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/socket_windows.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/tcp_client.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/tcp_client_posix.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/tcp_posix.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/tcp_server.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/tcp_uv.h" role="src" />
@ -393,6 +395,7 @@
<file baseinstalldir="/" name="src/core/lib/iomgr/resolve_address_posix.c" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/resolve_address_uv.c" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/resolve_address_windows.c" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/resource_quota.c" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/sockaddr_utils.c" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/socket_utils_common_posix.c" role="src" />
<file baseinstalldir="/" name="src/core/lib/iomgr/socket_utils_linux.c" role="src" />

@ -1022,6 +1022,10 @@ static void cc_destroy_call_elem(grpc_exec_ctx *exec_ctx,
GPR_ASSERT(calld->creation_phase == GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING);
gpr_mu_destroy(&calld->mu);
GPR_ASSERT(calld->waiting_ops_count == 0);
if (calld->connected_subchannel != NULL) {
GRPC_CONNECTED_SUBCHANNEL_UNREF(exec_ctx, calld->connected_subchannel,
"picked");
}
gpr_free(calld->waiting_ops);
gpr_free(and_free_memory);
}

@ -183,9 +183,10 @@ static void connection_destroy(grpc_exec_ctx *exec_ctx, void *arg,
gpr_free(c);
}
void grpc_connected_subchannel_ref(
grpc_connected_subchannel *grpc_connected_subchannel_ref(
grpc_connected_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) {
GRPC_CHANNEL_STACK_REF(CHANNEL_STACK_FROM_CONNECTION(c), REF_REASON);
return c;
}
void grpc_connected_subchannel_unref(grpc_exec_ctx *exec_ctx,

@ -97,7 +97,7 @@ grpc_subchannel *grpc_subchannel_weak_ref(
void grpc_subchannel_weak_unref(grpc_exec_ctx *exec_ctx,
grpc_subchannel *channel
GRPC_SUBCHANNEL_REF_EXTRA_ARGS);
void grpc_connected_subchannel_ref(
grpc_connected_subchannel *grpc_connected_subchannel_ref(
grpc_connected_subchannel *channel GRPC_SUBCHANNEL_REF_EXTRA_ARGS);
void grpc_connected_subchannel_unref(grpc_exec_ctx *exec_ctx,
grpc_connected_subchannel *channel

@ -209,7 +209,7 @@ static int pf_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
/* Check atomically for a selected channel */
grpc_connected_subchannel *selected = GET_SELECTED(p);
if (selected != NULL) {
*target = selected;
*target = GRPC_CONNECTED_SUBCHANNEL_REF(selected, "picked");
return 1;
}
@ -218,7 +218,7 @@ static int pf_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
selected = GET_SELECTED(p);
if (selected) {
gpr_mu_unlock(&p->mu);
*target = selected;
*target = GRPC_CONNECTED_SUBCHANNEL_REF(selected, "picked");
return 1;
} else {
if (!p->started_picking) {
@ -310,7 +310,7 @@ static void pf_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg,
/* update any calls that were waiting for a pick */
while ((pp = p->pending_picks)) {
p->pending_picks = pp->next;
*pp->target = selected;
*pp->target = GRPC_CONNECTED_SUBCHANNEL_REF(selected, "picked");
grpc_exec_ctx_sched(exec_ctx, pp->on_complete, GRPC_ERROR_NONE, NULL);
gpr_free(pp);
}

@ -397,7 +397,9 @@ static int rr_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
gpr_mu_lock(&p->mu);
if ((selected = peek_next_connected_locked(p))) {
/* readily available, report right away */
*target = grpc_subchannel_get_connected_subchannel(selected->subchannel);
*target = GRPC_CONNECTED_SUBCHANNEL_REF(
grpc_subchannel_get_connected_subchannel(selected->subchannel),
"picked");
if (user_data != NULL) {
*user_data = selected->user_data;
@ -463,8 +465,9 @@ static void rr_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg,
while ((pp = p->pending_picks)) {
p->pending_picks = pp->next;
*pp->target =
grpc_subchannel_get_connected_subchannel(selected->subchannel);
*pp->target = GRPC_CONNECTED_SUBCHANNEL_REF(
grpc_subchannel_get_connected_subchannel(selected->subchannel),
"picked");
if (pp->user_data != NULL) {
*pp->user_data = selected->user_data;
}
@ -578,7 +581,9 @@ static void rr_ping_one(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
gpr_mu_lock(&p->mu);
if ((selected = peek_next_connected_locked(p))) {
gpr_mu_unlock(&p->mu);
target = grpc_subchannel_get_connected_subchannel(selected->subchannel);
target = GRPC_CONNECTED_SUBCHANNEL_REF(
grpc_subchannel_get_connected_subchannel(selected->subchannel),
"picked");
grpc_connected_subchannel_ping(exec_ctx, target, closure);
} else {
gpr_mu_unlock(&p->mu);

@ -154,7 +154,8 @@ static void connector_connect(grpc_exec_ctx *exec_ctx, grpc_connector *con,
c->tcp = NULL;
grpc_closure_init(&c->connected, connected, c);
grpc_tcp_client_connect(exec_ctx, &c->connected, &c->tcp,
args->interested_parties, args->addr, args->deadline);
args->interested_parties, args->channel_args,
args->addr, args->deadline);
}
static const grpc_connector_vtable connector_vtable = {

@ -44,6 +44,7 @@
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/iomgr/endpoint.h"
#include "src/core/lib/iomgr/exec_ctx.h"
#include "src/core/lib/iomgr/tcp_client_posix.h"
#include "src/core/lib/iomgr/tcp_posix.h"
#include "src/core/lib/surface/api_trace.h"
#include "src/core/lib/surface/channel.h"
@ -65,9 +66,8 @@ grpc_channel *grpc_insecure_channel_create_from_fd(
int flags = fcntl(fd, F_GETFL, 0);
GPR_ASSERT(fcntl(fd, F_SETFL, flags | O_NONBLOCK) == 0);
grpc_endpoint *client =
grpc_tcp_create(grpc_fd_create(fd, "client"),
GRPC_TCP_DEFAULT_READ_SLICE_SIZE, "fd-client");
grpc_endpoint *client = grpc_tcp_client_create_from_fd(
&exec_ctx, grpc_fd_create(fd, "client"), args, "fd-client");
grpc_transport *transport =
grpc_create_chttp2_transport(&exec_ctx, final_args, client, 1);

@ -212,9 +212,9 @@ static void connector_connect(grpc_exec_ctx *exec_ctx, grpc_connector *con,
GPR_ASSERT(c->connecting_endpoint == NULL);
gpr_mu_unlock(&c->mu);
grpc_closure_init(&c->connected_closure, connected, c);
grpc_tcp_client_connect(exec_ctx, &c->connected_closure,
&c->newly_connecting_endpoint,
args->interested_parties, args->addr, args->deadline);
grpc_tcp_client_connect(
exec_ctx, &c->connected_closure, &c->newly_connecting_endpoint,
args->interested_parties, args->channel_args, args->addr, args->deadline);
}
static const grpc_connector_vtable connector_vtable = {

@ -139,8 +139,8 @@ int grpc_server_add_insecure_http2_port(grpc_server *server, const char *addr) {
goto error;
}
err =
grpc_tcp_server_create(NULL, grpc_server_get_channel_args(server), &tcp);
err = grpc_tcp_server_create(&exec_ctx, NULL,
grpc_server_get_channel_args(server), &tcp);
if (err != GRPC_ERROR_NONE) {
goto error;
}

@ -57,8 +57,12 @@ void grpc_server_add_insecure_channel_from_fd(grpc_server *server,
char *name;
gpr_asprintf(&name, "fd:%d", fd);
grpc_endpoint *server_endpoint = grpc_tcp_create(
grpc_fd_create(fd, name), GRPC_TCP_DEFAULT_READ_SLICE_SIZE, name);
grpc_resource_quota *resource_quota = grpc_resource_quota_from_channel_args(
grpc_server_get_channel_args(server));
grpc_endpoint *server_endpoint =
grpc_tcp_create(grpc_fd_create(fd, name), resource_quota,
GRPC_TCP_DEFAULT_READ_SLICE_SIZE, name);
grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
gpr_free(name);

@ -271,7 +271,8 @@ int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr,
memset(server_state, 0, sizeof(*server_state));
grpc_closure_init(&server_state->tcp_server_shutdown_complete,
tcp_server_shutdown_complete, server_state);
err = grpc_tcp_server_create(&server_state->tcp_server_shutdown_complete,
err = grpc_tcp_server_create(&exec_ctx,
&server_state->tcp_server_shutdown_complete,
grpc_server_get_channel_args(server), &tcp);
if (err != GRPC_ERROR_NONE) {
goto error;

@ -114,6 +114,20 @@ static void fail_pending_writes(grpc_exec_ctx *exec_ctx,
grpc_chttp2_transport *t, grpc_chttp2_stream *s,
grpc_error *error);
static void benign_reclaimer(grpc_exec_ctx *exec_ctx, void *t,
grpc_error *error);
static void benign_reclaimer_locked(grpc_exec_ctx *exec_ctx, void *t,
grpc_error *error);
static void destructive_reclaimer(grpc_exec_ctx *exec_ctx, void *t,
grpc_error *error);
static void destructive_reclaimer_locked(grpc_exec_ctx *exec_ctx, void *t,
grpc_error *error);
static void post_benign_reclaimer(grpc_exec_ctx *exec_ctx,
grpc_chttp2_transport *t);
static void post_destructive_reclaimer(grpc_exec_ctx *exec_ctx,
grpc_chttp2_transport *t);
static void close_transport_locked(grpc_exec_ctx *exec_ctx,
grpc_chttp2_transport *t, grpc_error *error);
static void end_all_the_calls(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
@ -241,6 +255,11 @@ static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
grpc_closure_init(&t->write_action_end_locked, write_action_end_locked, t);
grpc_closure_init(&t->read_action_begin, read_action_begin, t);
grpc_closure_init(&t->read_action_locked, read_action_locked, t);
grpc_closure_init(&t->benign_reclaimer, benign_reclaimer, t);
grpc_closure_init(&t->destructive_reclaimer, destructive_reclaimer, t);
grpc_closure_init(&t->benign_reclaimer_locked, benign_reclaimer_locked, t);
grpc_closure_init(&t->destructive_reclaimer_locked,
destructive_reclaimer_locked, t);
grpc_chttp2_goaway_parser_init(&t->goaway_parser);
grpc_chttp2_hpack_parser_init(&t->hpack_parser);
@ -362,6 +381,7 @@ static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
}
grpc_chttp2_initiate_write(exec_ctx, t, false, "init");
post_benign_reclaimer(exec_ctx, t);
}
static void destroy_transport_locked(grpc_exec_ctx *exec_ctx, void *tp,
@ -467,6 +487,7 @@ static int init_stream(grpc_exec_ctx *exec_ctx, grpc_transport *gt,
[GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE];
*t->accepting_stream = s;
grpc_chttp2_stream_map_add(&t->stream_map, s->id, s);
post_destructive_reclaimer(exec_ctx, t);
}
GPR_TIMER_END("init_stream", 0);
@ -675,6 +696,13 @@ static void write_action_end_locked(grpc_exec_ctx *exec_ctx, void *tp,
close_transport_locked(exec_ctx, t, GRPC_ERROR_REF(error));
}
if (t->sent_goaway_state == GRPC_CHTTP2_GOAWAY_SEND_SCHEDULED) {
t->sent_goaway_state = GRPC_CHTTP2_GOAWAY_SENT;
if (grpc_chttp2_stream_map_size(&t->stream_map) == 0) {
close_transport_locked(exec_ctx, t, GRPC_ERROR_CREATE("goaway sent"));
}
}
grpc_chttp2_end_write(exec_ctx, t, GRPC_ERROR_REF(error));
switch (t->write_state) {
@ -780,6 +808,7 @@ static void maybe_start_some_streams(grpc_exec_ctx *exec_ctx,
[GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE];
s->max_recv_bytes = GPR_MAX(stream_incoming_window, s->max_recv_bytes);
grpc_chttp2_stream_map_add(&t->stream_map, s->id, s);
post_destructive_reclaimer(exec_ctx, t);
grpc_chttp2_become_writable(exec_ctx, t, s, true, "new_stream");
}
/* cancel out streams that will never be started */
@ -993,9 +1022,14 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx, void *stream_op,
}
if (!s->write_closed) {
if (t->is_client) {
GPR_ASSERT(s->id == 0);
grpc_chttp2_list_add_waiting_for_concurrency(t, s);
maybe_start_some_streams(exec_ctx, t);
if (!t->closed) {
GPR_ASSERT(s->id == 0);
grpc_chttp2_list_add_waiting_for_concurrency(t, s);
maybe_start_some_streams(exec_ctx, t);
} else {
grpc_chttp2_cancel_stream(exec_ctx, t, s,
GRPC_ERROR_CREATE("Transport closed"));
}
} else {
GPR_ASSERT(s->id != 0);
grpc_chttp2_become_writable(exec_ctx, t, s, true,
@ -1185,6 +1219,14 @@ void grpc_chttp2_ack_ping(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
gpr_free(msg);
}
static void send_goaway(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
grpc_chttp2_error_code error, gpr_slice data) {
t->sent_goaway_state = GRPC_CHTTP2_GOAWAY_SEND_SCHEDULED;
grpc_chttp2_goaway_append(t->last_new_stream_id, (uint32_t)error, data,
&t->qbuf);
grpc_chttp2_initiate_write(exec_ctx, t, false, "goaway_sent");
}
static void perform_transport_op_locked(grpc_exec_ctx *exec_ctx,
void *stream_op,
grpc_error *error_ignored) {
@ -1199,15 +1241,9 @@ static void perform_transport_op_locked(grpc_exec_ctx *exec_ctx,
}
if (op->send_goaway) {
t->sent_goaway = 1;
grpc_chttp2_goaway_append(
t->last_new_stream_id,
(uint32_t)grpc_chttp2_grpc_status_to_http2_error(op->goaway_status),
gpr_slice_ref(*op->goaway_message), &t->qbuf);
close_transport = grpc_chttp2_stream_map_size(&t->stream_map) == 0
? GRPC_ERROR_CREATE("GOAWAY sent")
: GRPC_ERROR_NONE;
grpc_chttp2_initiate_write(exec_ctx, t, false, "goaway_sent");
send_goaway(exec_ctx, t,
grpc_chttp2_grpc_status_to_http2_error(op->goaway_status),
gpr_slice_ref(*op->goaway_message));
}
if (op->set_accept_stream) {
@ -1341,10 +1377,14 @@ static void remove_stream(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
s->data_parser.parsing_frame = NULL;
}
if (grpc_chttp2_stream_map_size(&t->stream_map) == 0 && t->sent_goaway) {
close_transport_locked(
exec_ctx, t, GRPC_ERROR_CREATE_REFERENCING(
"Last stream closed after sending GOAWAY", &error, 1));
if (grpc_chttp2_stream_map_size(&t->stream_map) == 0) {
post_benign_reclaimer(exec_ctx, t);
if (t->sent_goaway_state == GRPC_CHTTP2_GOAWAY_SENT) {
close_transport_locked(
exec_ctx, t,
GRPC_ERROR_CREATE_REFERENCING(
"Last stream closed after sending GOAWAY", &error, 1));
}
}
if (grpc_chttp2_list_remove_writable_stream(t, s)) {
GRPC_CHTTP2_STREAM_UNREF(exec_ctx, s, "chttp2_writing:remove_stream");
@ -2071,6 +2111,103 @@ grpc_chttp2_incoming_byte_stream *grpc_chttp2_incoming_byte_stream_create(
return incoming_byte_stream;
}
/*******************************************************************************
* RESOURCE QUOTAS
*/
static void post_benign_reclaimer(grpc_exec_ctx *exec_ctx,
grpc_chttp2_transport *t) {
if (!t->benign_reclaimer_registered) {
t->benign_reclaimer_registered = true;
GRPC_CHTTP2_REF_TRANSPORT(t, "benign_reclaimer");
grpc_resource_user_post_reclaimer(exec_ctx,
grpc_endpoint_get_resource_user(t->ep),
false, &t->benign_reclaimer);
}
}
static void post_destructive_reclaimer(grpc_exec_ctx *exec_ctx,
grpc_chttp2_transport *t) {
if (!t->destructive_reclaimer_registered) {
t->destructive_reclaimer_registered = true;
GRPC_CHTTP2_REF_TRANSPORT(t, "destructive_reclaimer");
grpc_resource_user_post_reclaimer(exec_ctx,
grpc_endpoint_get_resource_user(t->ep),
true, &t->destructive_reclaimer);
}
}
static void benign_reclaimer(grpc_exec_ctx *exec_ctx, void *arg,
grpc_error *error) {
grpc_chttp2_transport *t = arg;
grpc_combiner_execute(exec_ctx, t->combiner, &t->benign_reclaimer_locked,
GRPC_ERROR_REF(error), false);
}
static void destructive_reclaimer(grpc_exec_ctx *exec_ctx, void *arg,
grpc_error *error) {
grpc_chttp2_transport *t = arg;
grpc_combiner_execute(exec_ctx, t->combiner, &t->destructive_reclaimer_locked,
GRPC_ERROR_REF(error), false);
}
static void benign_reclaimer_locked(grpc_exec_ctx *exec_ctx, void *arg,
grpc_error *error) {
grpc_chttp2_transport *t = arg;
if (error == GRPC_ERROR_NONE &&
grpc_chttp2_stream_map_size(&t->stream_map) == 0) {
/* Channel with no active streams: send a goaway to try and make it
* disconnect cleanly */
if (grpc_resource_quota_trace) {
gpr_log(GPR_DEBUG, "HTTP2: %s - send goaway to free memory",
t->peer_string);
}
send_goaway(exec_ctx, t, GRPC_CHTTP2_ENHANCE_YOUR_CALM,
gpr_slice_from_static_string("Buffers full"));
} else if (error == GRPC_ERROR_NONE && grpc_resource_quota_trace) {
gpr_log(GPR_DEBUG,
"HTTP2: %s - skip benign reclamation, there are still %" PRIdPTR
" streams",
t->peer_string, grpc_chttp2_stream_map_size(&t->stream_map));
}
t->benign_reclaimer_registered = false;
if (error != GRPC_ERROR_CANCELLED) {
grpc_resource_user_finish_reclamation(
exec_ctx, grpc_endpoint_get_resource_user(t->ep));
}
GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "benign_reclaimer");
}
static void destructive_reclaimer_locked(grpc_exec_ctx *exec_ctx, void *arg,
grpc_error *error) {
grpc_chttp2_transport *t = arg;
size_t n = grpc_chttp2_stream_map_size(&t->stream_map);
t->destructive_reclaimer_registered = false;
if (error == GRPC_ERROR_NONE && n > 0) {
grpc_chttp2_stream *s = grpc_chttp2_stream_map_rand(&t->stream_map);
if (grpc_resource_quota_trace) {
gpr_log(GPR_DEBUG, "HTTP2: %s - abandon stream id %d", t->peer_string,
s->id);
}
grpc_chttp2_cancel_stream(
exec_ctx, t, s, grpc_error_set_int(GRPC_ERROR_CREATE("Buffers full"),
GRPC_ERROR_INT_HTTP2_ERROR,
GRPC_CHTTP2_ENHANCE_YOUR_CALM));
if (n > 1) {
/* Since we cancel one stream per destructive reclamation, if
there are more streams left, we can immediately post a new
reclaimer in case the resource quota needs to free more
memory */
post_destructive_reclaimer(exec_ctx, t);
}
}
if (error != GRPC_ERROR_CANCELLED) {
grpc_resource_user_finish_reclamation(
exec_ctx, grpc_endpoint_get_resource_user(t->ep));
}
GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "destructive_reclaimer");
}
/*******************************************************************************
* TRACING
*/

@ -138,6 +138,12 @@ typedef enum {
GRPC_NUM_SETTING_SETS
} grpc_chttp2_setting_set;
typedef enum {
GRPC_CHTTP2_NO_GOAWAY_SEND,
GRPC_CHTTP2_GOAWAY_SEND_SCHEDULED,
GRPC_CHTTP2_GOAWAY_SENT,
} grpc_chttp2_sent_goaway_state;
/* Outstanding ping request data */
typedef struct grpc_chttp2_outstanding_ping {
uint8_t id[8];
@ -249,7 +255,7 @@ struct grpc_chttp2_transport {
/** have we seen a goaway */
uint8_t seen_goaway;
/** have we sent a goaway */
uint8_t sent_goaway;
grpc_chttp2_sent_goaway_state sent_goaway_state;
/** are the local settings dirty and need to be sent? */
uint8_t dirtied_local_settings;
@ -320,6 +326,18 @@ struct grpc_chttp2_transport {
/* if non-NULL, close the transport with this error when writes are finished
*/
grpc_error *close_transport_on_writes_finished;
/* buffer pool state */
/** have we scheduled a benign cleanup? */
bool benign_reclaimer_registered;
/** have we scheduled a destructive cleanup? */
bool destructive_reclaimer_registered;
/** benign cleanup closure */
grpc_closure benign_reclaimer;
grpc_closure benign_reclaimer_locked;
/** destructive cleanup closure */
grpc_closure destructive_reclaimer;
grpc_closure destructive_reclaimer_locked;
};
typedef enum {

@ -151,6 +151,17 @@ size_t grpc_chttp2_stream_map_size(grpc_chttp2_stream_map *map) {
return map->count - map->free;
}
void *grpc_chttp2_stream_map_rand(grpc_chttp2_stream_map *map) {
if (map->count == map->free) {
return NULL;
}
if (map->free != 0) {
map->count = compact(map->keys, map->values, map->count);
map->free = 0;
}
return map->values[((size_t)rand()) % map->count];
}
void grpc_chttp2_stream_map_for_each(grpc_chttp2_stream_map *map,
void (*f)(void *user_data, uint32_t key,
void *value),

@ -68,6 +68,9 @@ void *grpc_chttp2_stream_map_delete(grpc_chttp2_stream_map *map, uint32_t key);
/* Return an existing key, or NULL if it does not exist */
void *grpc_chttp2_stream_map_find(grpc_chttp2_stream_map *map, uint32_t key);
/* Return a random entry */
void *grpc_chttp2_stream_map_rand(grpc_chttp2_stream_map *map);
/* How many (populated) entries are in the stream map? */
size_t grpc_chttp2_stream_map_size(grpc_chttp2_stream_map *map);

@ -70,6 +70,7 @@ typedef struct {
grpc_closure done_write;
grpc_closure connected;
grpc_error *overall_error;
grpc_resource_quota *resource_quota;
} internal_request;
static grpc_httpcli_get_override g_get_override = NULL;
@ -117,6 +118,7 @@ static void finish(grpc_exec_ctx *exec_ctx, internal_request *req,
gpr_slice_buffer_destroy(&req->incoming);
gpr_slice_buffer_destroy(&req->outgoing);
GRPC_ERROR_UNREF(req->overall_error);
grpc_resource_quota_internal_unref(exec_ctx, req->resource_quota);
gpr_free(req);
}
@ -223,8 +225,15 @@ static void next_address(grpc_exec_ctx *exec_ctx, internal_request *req,
}
addr = &req->addresses->addrs[req->next_address++];
grpc_closure_init(&req->connected, on_connected, req);
grpc_arg arg;
arg.key = GRPC_ARG_RESOURCE_QUOTA;
arg.type = GRPC_ARG_POINTER;
arg.value.pointer.p = req->resource_quota;
arg.value.pointer.vtable = grpc_resource_quota_arg_vtable();
grpc_channel_args args = {1, &arg};
grpc_tcp_client_connect(exec_ctx, &req->connected, &req->ep,
req->context->pollset_set, addr, req->deadline);
req->context->pollset_set, &args, addr,
req->deadline);
}
static void on_resolved(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
@ -240,6 +249,7 @@ static void on_resolved(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
static void internal_request_begin(grpc_exec_ctx *exec_ctx,
grpc_httpcli_context *context,
grpc_polling_entity *pollent,
grpc_resource_quota *resource_quota,
const grpc_httpcli_request *request,
gpr_timespec deadline, grpc_closure *on_done,
grpc_httpcli_response *response,
@ -255,6 +265,7 @@ static void internal_request_begin(grpc_exec_ctx *exec_ctx,
req->context = context;
req->pollent = pollent;
req->overall_error = GRPC_ERROR_NONE;
req->resource_quota = grpc_resource_quota_internal_ref(resource_quota);
grpc_closure_init(&req->on_read, on_read, req);
grpc_closure_init(&req->done_write, done_write, req);
gpr_slice_buffer_init(&req->incoming);
@ -272,6 +283,7 @@ static void internal_request_begin(grpc_exec_ctx *exec_ctx,
void grpc_httpcli_get(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
grpc_polling_entity *pollent,
grpc_resource_quota *resource_quota,
const grpc_httpcli_request *request,
gpr_timespec deadline, grpc_closure *on_done,
grpc_httpcli_response *response) {
@ -281,14 +293,15 @@ void grpc_httpcli_get(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
return;
}
gpr_asprintf(&name, "HTTP:GET:%s:%s", request->host, request->http.path);
internal_request_begin(exec_ctx, context, pollent, request, deadline, on_done,
response, name,
internal_request_begin(exec_ctx, context, pollent, resource_quota, request,
deadline, on_done, response, name,
grpc_httpcli_format_get_request(request));
gpr_free(name);
}
void grpc_httpcli_post(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
grpc_polling_entity *pollent,
grpc_resource_quota *resource_quota,
const grpc_httpcli_request *request,
const char *body_bytes, size_t body_size,
gpr_timespec deadline, grpc_closure *on_done,
@ -301,7 +314,8 @@ void grpc_httpcli_post(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
}
gpr_asprintf(&name, "HTTP:POST:%s:%s", request->host, request->http.path);
internal_request_begin(
exec_ctx, context, pollent, request, deadline, on_done, response, name,
exec_ctx, context, pollent, resource_quota, request, deadline, on_done,
response, name,
grpc_httpcli_format_post_request(request, body_bytes, body_size));
gpr_free(name);
}

@ -96,6 +96,7 @@ void grpc_httpcli_context_destroy(grpc_httpcli_context *context);
'on_response' is a callback to report results to */
void grpc_httpcli_get(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
grpc_polling_entity *pollent,
grpc_resource_quota *resource_quota,
const grpc_httpcli_request *request,
gpr_timespec deadline, grpc_closure *on_complete,
grpc_httpcli_response *response);
@ -116,6 +117,7 @@ void grpc_httpcli_get(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
Does not support ?var1=val1&var2=val2 in the path. */
void grpc_httpcli_post(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
grpc_polling_entity *pollent,
grpc_resource_quota *resource_quota,
const grpc_httpcli_request *request,
const char *body_bytes, size_t body_size,
gpr_timespec deadline, grpc_closure *on_complete,

@ -69,3 +69,7 @@ char* grpc_endpoint_get_peer(grpc_endpoint* ep) {
grpc_workqueue* grpc_endpoint_get_workqueue(grpc_endpoint* ep) {
return ep->vtable->get_workqueue(ep);
}
grpc_resource_user* grpc_endpoint_get_resource_user(grpc_endpoint* ep) {
return ep->vtable->get_resource_user(ep);
}

@ -39,6 +39,7 @@
#include <grpc/support/time.h>
#include "src/core/lib/iomgr/pollset.h"
#include "src/core/lib/iomgr/pollset_set.h"
#include "src/core/lib/iomgr/resource_quota.h"
/* An endpoint caps a streaming channel between two communicating processes.
Examples may be: a tcp socket, <stdin+stdout>, or some shared memory. */
@ -58,6 +59,7 @@ struct grpc_endpoint_vtable {
grpc_pollset_set *pollset);
void (*shutdown)(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep);
void (*destroy)(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep);
grpc_resource_user *(*get_resource_user)(grpc_endpoint *ep);
char *(*get_peer)(grpc_endpoint *ep);
};
@ -100,6 +102,8 @@ void grpc_endpoint_add_to_pollset_set(grpc_exec_ctx *exec_ctx,
grpc_endpoint *ep,
grpc_pollset_set *pollset_set);
grpc_resource_user *grpc_endpoint_get_resource_user(grpc_endpoint *endpoint);
struct grpc_endpoint {
const grpc_endpoint_vtable *vtable;
};

@ -41,7 +41,8 @@ typedef struct {
grpc_endpoint *server;
} grpc_endpoint_pair;
grpc_endpoint_pair grpc_iomgr_create_endpoint_pair(const char *name,
size_t read_slice_size);
grpc_endpoint_pair grpc_iomgr_create_endpoint_pair(
const char *name, grpc_resource_quota *resource_quota,
size_t read_slice_size);
#endif /* GRPC_CORE_LIB_IOMGR_ENDPOINT_PAIR_H */

@ -62,20 +62,21 @@ static void create_sockets(int sv[2]) {
GPR_ASSERT(grpc_set_socket_no_sigpipe_if_possible(sv[1]) == GRPC_ERROR_NONE);
}
grpc_endpoint_pair grpc_iomgr_create_endpoint_pair(const char *name,
size_t read_slice_size) {
grpc_endpoint_pair grpc_iomgr_create_endpoint_pair(
const char *name, grpc_resource_quota *resource_quota,
size_t read_slice_size) {
int sv[2];
grpc_endpoint_pair p;
char *final_name;
create_sockets(sv);
gpr_asprintf(&final_name, "%s:client", name);
p.client = grpc_tcp_create(grpc_fd_create(sv[1], final_name), read_slice_size,
"socketpair-server");
p.client = grpc_tcp_create(grpc_fd_create(sv[1], final_name), resource_quota,
read_slice_size, "socketpair-server");
gpr_free(final_name);
gpr_asprintf(&final_name, "%s:server", name);
p.server = grpc_tcp_create(grpc_fd_create(sv[0], final_name), read_slice_size,
"socketpair-client");
p.server = grpc_tcp_create(grpc_fd_create(sv[0], final_name), resource_quota,
read_slice_size, "socketpair-client");
gpr_free(final_name);
return p;
}

@ -0,0 +1,714 @@
/*
*
* Copyright 2016, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "src/core/lib/iomgr/resource_quota.h"
#include <string.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
#include <grpc/support/useful.h>
#include "src/core/lib/iomgr/combiner.h"
int grpc_resource_quota_trace = 0;
struct grpc_resource_quota {
/* refcount */
gpr_refcount refs;
/* Master combiner lock: all activity on a quota executes under this combiner
* (so no mutex is needed for this data structure)
*/
grpc_combiner *combiner;
/* Size of the resource quota */
int64_t size;
/* Amount of free memory in the resource quota */
int64_t free_pool;
/* Has rq_step been scheduled to occur? */
bool step_scheduled;
/* Are we currently reclaiming memory */
bool reclaiming;
/* Closure around rq_step */
grpc_closure rq_step_closure;
/* Closure around rq_reclamation_done */
grpc_closure rq_reclamation_done_closure;
/* Roots of all resource user lists */
grpc_resource_user *roots[GRPC_RULIST_COUNT];
char *name;
};
/*******************************************************************************
* list management
*/
static void rulist_add_head(grpc_resource_user *resource_user,
grpc_rulist list) {
grpc_resource_quota *resource_quota = resource_user->resource_quota;
grpc_resource_user **root = &resource_quota->roots[list];
if (*root == NULL) {
*root = resource_user;
resource_user->links[list].next = resource_user->links[list].prev =
resource_user;
} else {
resource_user->links[list].next = *root;
resource_user->links[list].prev = (*root)->links[list].prev;
resource_user->links[list].next->links[list].prev =
resource_user->links[list].prev->links[list].next = resource_user;
*root = resource_user;
}
}
static void rulist_add_tail(grpc_resource_user *resource_user,
grpc_rulist list) {
grpc_resource_quota *resource_quota = resource_user->resource_quota;
grpc_resource_user **root = &resource_quota->roots[list];
if (*root == NULL) {
*root = resource_user;
resource_user->links[list].next = resource_user->links[list].prev =
resource_user;
} else {
resource_user->links[list].next = (*root)->links[list].next;
resource_user->links[list].prev = *root;
resource_user->links[list].next->links[list].prev =
resource_user->links[list].prev->links[list].next = resource_user;
}
}
static bool rulist_empty(grpc_resource_quota *resource_quota,
grpc_rulist list) {
return resource_quota->roots[list] == NULL;
}
static grpc_resource_user *rulist_pop_head(grpc_resource_quota *resource_quota,
grpc_rulist list) {
grpc_resource_user **root = &resource_quota->roots[list];
grpc_resource_user *resource_user = *root;
if (resource_user == NULL) {
return NULL;
}
if (resource_user->links[list].next == resource_user) {
*root = NULL;
} else {
resource_user->links[list].next->links[list].prev =
resource_user->links[list].prev;
resource_user->links[list].prev->links[list].next =
resource_user->links[list].next;
*root = resource_user->links[list].next;
}
resource_user->links[list].next = resource_user->links[list].prev = NULL;
return resource_user;
}
static void rulist_remove(grpc_resource_user *resource_user, grpc_rulist list) {
if (resource_user->links[list].next == NULL) return;
grpc_resource_quota *resource_quota = resource_user->resource_quota;
if (resource_quota->roots[list] == resource_user) {
resource_quota->roots[list] = resource_user->links[list].next;
if (resource_quota->roots[list] == resource_user) {
resource_quota->roots[list] = NULL;
}
}
resource_user->links[list].next->links[list].prev =
resource_user->links[list].prev;
resource_user->links[list].prev->links[list].next =
resource_user->links[list].next;
}
/*******************************************************************************
* resource quota state machine
*/
static bool rq_alloc(grpc_exec_ctx *exec_ctx,
grpc_resource_quota *resource_quota);
static bool rq_reclaim_from_per_user_free_pool(
grpc_exec_ctx *exec_ctx, grpc_resource_quota *resource_quota);
static bool rq_reclaim(grpc_exec_ctx *exec_ctx,
grpc_resource_quota *resource_quota, bool destructive);
static void rq_step(grpc_exec_ctx *exec_ctx, void *rq, grpc_error *error) {
grpc_resource_quota *resource_quota = rq;
resource_quota->step_scheduled = false;
do {
if (rq_alloc(exec_ctx, resource_quota)) goto done;
} while (rq_reclaim_from_per_user_free_pool(exec_ctx, resource_quota));
rq_reclaim(exec_ctx, resource_quota, false) ||
rq_reclaim(exec_ctx, resource_quota, true);
done:
grpc_resource_quota_internal_unref(exec_ctx, resource_quota);
}
static void rq_step_sched(grpc_exec_ctx *exec_ctx,
grpc_resource_quota *resource_quota) {
if (resource_quota->step_scheduled) return;
resource_quota->step_scheduled = true;
grpc_resource_quota_internal_ref(resource_quota);
grpc_combiner_execute_finally(exec_ctx, resource_quota->combiner,
&resource_quota->rq_step_closure,
GRPC_ERROR_NONE, false);
}
/* returns true if all allocations are completed */
static bool rq_alloc(grpc_exec_ctx *exec_ctx,
grpc_resource_quota *resource_quota) {
grpc_resource_user *resource_user;
while ((resource_user = rulist_pop_head(resource_quota,
GRPC_RULIST_AWAITING_ALLOCATION))) {
gpr_mu_lock(&resource_user->mu);
if (resource_user->free_pool < 0 &&
-resource_user->free_pool <= resource_quota->free_pool) {
int64_t amt = -resource_user->free_pool;
resource_user->free_pool = 0;
resource_quota->free_pool -= amt;
if (grpc_resource_quota_trace) {
gpr_log(GPR_DEBUG, "RQ %s %s: grant alloc %" PRId64
" bytes; rq_free_pool -> %" PRId64,
resource_quota->name, resource_user->name, amt,
resource_quota->free_pool);
}
} else if (grpc_resource_quota_trace && resource_user->free_pool >= 0) {
gpr_log(GPR_DEBUG, "RQ %s %s: discard already satisfied alloc request",
resource_quota->name, resource_user->name);
}
if (resource_user->free_pool >= 0) {
resource_user->allocating = false;
grpc_exec_ctx_enqueue_list(exec_ctx, &resource_user->on_allocated, NULL);
gpr_mu_unlock(&resource_user->mu);
} else {
rulist_add_head(resource_user, GRPC_RULIST_AWAITING_ALLOCATION);
gpr_mu_unlock(&resource_user->mu);
return false;
}
}
return true;
}
/* returns true if any memory could be reclaimed from buffers */
static bool rq_reclaim_from_per_user_free_pool(
grpc_exec_ctx *exec_ctx, grpc_resource_quota *resource_quota) {
grpc_resource_user *resource_user;
while ((resource_user = rulist_pop_head(resource_quota,
GRPC_RULIST_NON_EMPTY_FREE_POOL))) {
gpr_mu_lock(&resource_user->mu);
if (resource_user->free_pool > 0) {
int64_t amt = resource_user->free_pool;
resource_user->free_pool = 0;
resource_quota->free_pool += amt;
if (grpc_resource_quota_trace) {
gpr_log(GPR_DEBUG, "RQ %s %s: reclaim_from_per_user_free_pool %" PRId64
" bytes; rq_free_pool -> %" PRId64,
resource_quota->name, resource_user->name, amt,
resource_quota->free_pool);
}
gpr_mu_unlock(&resource_user->mu);
return true;
} else {
gpr_mu_unlock(&resource_user->mu);
}
}
return false;
}
/* returns true if reclamation is proceeding */
static bool rq_reclaim(grpc_exec_ctx *exec_ctx,
grpc_resource_quota *resource_quota, bool destructive) {
if (resource_quota->reclaiming) return true;
grpc_rulist list = destructive ? GRPC_RULIST_RECLAIMER_DESTRUCTIVE
: GRPC_RULIST_RECLAIMER_BENIGN;
grpc_resource_user *resource_user = rulist_pop_head(resource_quota, list);
if (resource_user == NULL) return false;
if (grpc_resource_quota_trace) {
gpr_log(GPR_DEBUG, "RQ %s %s: initiate %s reclamation",
resource_quota->name, resource_user->name,
destructive ? "destructive" : "benign");
}
resource_quota->reclaiming = true;
grpc_resource_quota_internal_ref(resource_quota);
grpc_closure *c = resource_user->reclaimers[destructive];
resource_user->reclaimers[destructive] = NULL;
grpc_closure_run(exec_ctx, c, GRPC_ERROR_NONE);
return true;
}
/*******************************************************************************
* ru_slice: a slice implementation that is backed by a grpc_resource_user
*/
typedef struct {
gpr_slice_refcount base;
gpr_refcount refs;
grpc_resource_user *resource_user;
size_t size;
} ru_slice_refcount;
static void ru_slice_ref(void *p) {
ru_slice_refcount *rc = p;
gpr_ref(&rc->refs);
}
static void ru_slice_unref(void *p) {
ru_slice_refcount *rc = p;
if (gpr_unref(&rc->refs)) {
/* TODO(ctiller): this is dangerous, but I think safe for now:
we have no guarantee here that we're at a safe point for creating an
execution context, but we have no way of writing this code otherwise.
In the future: consider lifting gpr_slice to grpc, and offering an
internal_{ref,unref} pair that is execution context aware.
Alternatively,
make exec_ctx be thread local and 'do the right thing' (whatever that
is)
if NULL */
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_free(&exec_ctx, rc->resource_user, rc->size);
grpc_exec_ctx_finish(&exec_ctx);
gpr_free(rc);
}
}
static gpr_slice ru_slice_create(grpc_resource_user *resource_user,
size_t size) {
ru_slice_refcount *rc = gpr_malloc(sizeof(ru_slice_refcount) + size);
rc->base.ref = ru_slice_ref;
rc->base.unref = ru_slice_unref;
gpr_ref_init(&rc->refs, 1);
rc->resource_user = resource_user;
rc->size = size;
gpr_slice slice;
slice.refcount = &rc->base;
slice.data.refcounted.bytes = (uint8_t *)(rc + 1);
slice.data.refcounted.length = size;
return slice;
}
/*******************************************************************************
* grpc_resource_quota internal implementation: resource user manipulation under
* the combiner
*/
static void ru_allocate(grpc_exec_ctx *exec_ctx, void *ru, grpc_error *error) {
grpc_resource_user *resource_user = ru;
if (rulist_empty(resource_user->resource_quota,
GRPC_RULIST_AWAITING_ALLOCATION)) {
rq_step_sched(exec_ctx, resource_user->resource_quota);
}
rulist_add_tail(resource_user, GRPC_RULIST_AWAITING_ALLOCATION);
}
static void ru_add_to_free_pool(grpc_exec_ctx *exec_ctx, void *ru,
grpc_error *error) {
grpc_resource_user *resource_user = ru;
if (!rulist_empty(resource_user->resource_quota,
GRPC_RULIST_AWAITING_ALLOCATION) &&
rulist_empty(resource_user->resource_quota,
GRPC_RULIST_NON_EMPTY_FREE_POOL)) {
rq_step_sched(exec_ctx, resource_user->resource_quota);
}
rulist_add_tail(resource_user, GRPC_RULIST_NON_EMPTY_FREE_POOL);
}
static void ru_post_benign_reclaimer(grpc_exec_ctx *exec_ctx, void *ru,
grpc_error *error) {
grpc_resource_user *resource_user = ru;
if (!rulist_empty(resource_user->resource_quota,
GRPC_RULIST_AWAITING_ALLOCATION) &&
rulist_empty(resource_user->resource_quota,
GRPC_RULIST_NON_EMPTY_FREE_POOL) &&
rulist_empty(resource_user->resource_quota,
GRPC_RULIST_RECLAIMER_BENIGN)) {
rq_step_sched(exec_ctx, resource_user->resource_quota);
}
rulist_add_tail(resource_user, GRPC_RULIST_RECLAIMER_BENIGN);
}
static void ru_post_destructive_reclaimer(grpc_exec_ctx *exec_ctx, void *ru,
grpc_error *error) {
grpc_resource_user *resource_user = ru;
if (!rulist_empty(resource_user->resource_quota,
GRPC_RULIST_AWAITING_ALLOCATION) &&
rulist_empty(resource_user->resource_quota,
GRPC_RULIST_NON_EMPTY_FREE_POOL) &&
rulist_empty(resource_user->resource_quota,
GRPC_RULIST_RECLAIMER_BENIGN) &&
rulist_empty(resource_user->resource_quota,
GRPC_RULIST_RECLAIMER_DESTRUCTIVE)) {
rq_step_sched(exec_ctx, resource_user->resource_quota);
}
rulist_add_tail(resource_user, GRPC_RULIST_RECLAIMER_DESTRUCTIVE);
}
static void ru_destroy(grpc_exec_ctx *exec_ctx, void *ru, grpc_error *error) {
grpc_resource_user *resource_user = ru;
GPR_ASSERT(resource_user->allocated == 0);
for (int i = 0; i < GRPC_RULIST_COUNT; i++) {
rulist_remove(resource_user, (grpc_rulist)i);
}
grpc_exec_ctx_sched(exec_ctx, resource_user->reclaimers[0],
GRPC_ERROR_CANCELLED, NULL);
grpc_exec_ctx_sched(exec_ctx, resource_user->reclaimers[1],
GRPC_ERROR_CANCELLED, NULL);
grpc_exec_ctx_sched(exec_ctx, (grpc_closure *)gpr_atm_no_barrier_load(
&resource_user->on_done_destroy_closure),
GRPC_ERROR_NONE, NULL);
if (resource_user->free_pool != 0) {
resource_user->resource_quota->free_pool += resource_user->free_pool;
rq_step_sched(exec_ctx, resource_user->resource_quota);
}
}
static void ru_allocated_slices(grpc_exec_ctx *exec_ctx, void *arg,
grpc_error *error) {
grpc_resource_user_slice_allocator *slice_allocator = arg;
if (error == GRPC_ERROR_NONE) {
for (size_t i = 0; i < slice_allocator->count; i++) {
gpr_slice_buffer_add_indexed(
slice_allocator->dest, ru_slice_create(slice_allocator->resource_user,
slice_allocator->length));
}
}
grpc_closure_run(exec_ctx, &slice_allocator->on_done, GRPC_ERROR_REF(error));
}
/*******************************************************************************
* grpc_resource_quota internal implementation: quota manipulation under the
* combiner
*/
typedef struct {
int64_t size;
grpc_resource_quota *resource_quota;
grpc_closure closure;
} rq_resize_args;
static void rq_resize(grpc_exec_ctx *exec_ctx, void *args, grpc_error *error) {
rq_resize_args *a = args;
int64_t delta = a->size - a->resource_quota->size;
a->resource_quota->size += delta;
a->resource_quota->free_pool += delta;
rq_step_sched(exec_ctx, a->resource_quota);
grpc_resource_quota_internal_unref(exec_ctx, a->resource_quota);
gpr_free(a);
}
static void rq_reclamation_done(grpc_exec_ctx *exec_ctx, void *rq,
grpc_error *error) {
grpc_resource_quota *resource_quota = rq;
resource_quota->reclaiming = false;
rq_step_sched(exec_ctx, resource_quota);
grpc_resource_quota_internal_unref(exec_ctx, resource_quota);
}
/*******************************************************************************
* grpc_resource_quota api
*/
/* Public API */
grpc_resource_quota *grpc_resource_quota_create(const char *name) {
grpc_resource_quota *resource_quota = gpr_malloc(sizeof(*resource_quota));
gpr_ref_init(&resource_quota->refs, 1);
resource_quota->combiner = grpc_combiner_create(NULL);
resource_quota->free_pool = INT64_MAX;
resource_quota->size = INT64_MAX;
resource_quota->step_scheduled = false;
resource_quota->reclaiming = false;
if (name != NULL) {
resource_quota->name = gpr_strdup(name);
} else {
gpr_asprintf(&resource_quota->name, "anonymous_pool_%" PRIxPTR,
(intptr_t)resource_quota);
}
grpc_closure_init(&resource_quota->rq_step_closure, rq_step, resource_quota);
grpc_closure_init(&resource_quota->rq_reclamation_done_closure,
rq_reclamation_done, resource_quota);
for (int i = 0; i < GRPC_RULIST_COUNT; i++) {
resource_quota->roots[i] = NULL;
}
return resource_quota;
}
void grpc_resource_quota_internal_unref(grpc_exec_ctx *exec_ctx,
grpc_resource_quota *resource_quota) {
if (gpr_unref(&resource_quota->refs)) {
grpc_combiner_destroy(exec_ctx, resource_quota->combiner);
gpr_free(resource_quota->name);
gpr_free(resource_quota);
}
}
/* Public API */
void grpc_resource_quota_unref(grpc_resource_quota *resource_quota) {
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
grpc_exec_ctx_finish(&exec_ctx);
}
grpc_resource_quota *grpc_resource_quota_internal_ref(
grpc_resource_quota *resource_quota) {
gpr_ref(&resource_quota->refs);
return resource_quota;
}
/* Public API */
void grpc_resource_quota_ref(grpc_resource_quota *resource_quota) {
grpc_resource_quota_internal_ref(resource_quota);
}
/* Public API */
void grpc_resource_quota_resize(grpc_resource_quota *resource_quota,
size_t size) {
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
rq_resize_args *a = gpr_malloc(sizeof(*a));
a->resource_quota = grpc_resource_quota_internal_ref(resource_quota);
a->size = (int64_t)size;
grpc_closure_init(&a->closure, rq_resize, a);
grpc_combiner_execute(&exec_ctx, resource_quota->combiner, &a->closure,
GRPC_ERROR_NONE, false);
grpc_exec_ctx_finish(&exec_ctx);
}
/*******************************************************************************
* grpc_resource_user channel args api
*/
grpc_resource_quota *grpc_resource_quota_from_channel_args(
const grpc_channel_args *channel_args) {
for (size_t i = 0; i < channel_args->num_args; i++) {
if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) {
if (channel_args->args[i].type == GRPC_ARG_POINTER) {
return grpc_resource_quota_internal_ref(
channel_args->args[i].value.pointer.p);
} else {
gpr_log(GPR_DEBUG, GRPC_ARG_RESOURCE_QUOTA " should be a pointer");
}
}
}
return grpc_resource_quota_create(NULL);
}
static void *rq_copy(void *rq) {
grpc_resource_quota_ref(rq);
return rq;
}
static void rq_destroy(void *rq) { grpc_resource_quota_unref(rq); }
static int rq_cmp(void *a, void *b) { return GPR_ICMP(a, b); }
const grpc_arg_pointer_vtable *grpc_resource_quota_arg_vtable(void) {
static const grpc_arg_pointer_vtable vtable = {rq_copy, rq_destroy, rq_cmp};
return &vtable;
}
/*******************************************************************************
* grpc_resource_user api
*/
void grpc_resource_user_init(grpc_resource_user *resource_user,
grpc_resource_quota *resource_quota,
const char *name) {
resource_user->resource_quota =
grpc_resource_quota_internal_ref(resource_quota);
grpc_closure_init(&resource_user->allocate_closure, &ru_allocate,
resource_user);
grpc_closure_init(&resource_user->add_to_free_pool_closure,
&ru_add_to_free_pool, resource_user);
grpc_closure_init(&resource_user->post_reclaimer_closure[0],
&ru_post_benign_reclaimer, resource_user);
grpc_closure_init(&resource_user->post_reclaimer_closure[1],
&ru_post_destructive_reclaimer, resource_user);
grpc_closure_init(&resource_user->destroy_closure, &ru_destroy,
resource_user);
gpr_mu_init(&resource_user->mu);
resource_user->allocated = 0;
resource_user->free_pool = 0;
grpc_closure_list_init(&resource_user->on_allocated);
resource_user->allocating = false;
resource_user->added_to_free_pool = false;
gpr_atm_no_barrier_store(&resource_user->on_done_destroy_closure, 0);
resource_user->reclaimers[0] = NULL;
resource_user->reclaimers[1] = NULL;
for (int i = 0; i < GRPC_RULIST_COUNT; i++) {
resource_user->links[i].next = resource_user->links[i].prev = NULL;
}
if (name != NULL) {
resource_user->name = gpr_strdup(name);
} else {
gpr_asprintf(&resource_user->name, "anonymous_resource_user_%" PRIxPTR,
(intptr_t)resource_user);
}
}
void grpc_resource_user_shutdown(grpc_exec_ctx *exec_ctx,
grpc_resource_user *resource_user,
grpc_closure *on_done) {
gpr_mu_lock(&resource_user->mu);
GPR_ASSERT(gpr_atm_no_barrier_load(&resource_user->on_done_destroy_closure) ==
0);
gpr_atm_no_barrier_store(&resource_user->on_done_destroy_closure,
(gpr_atm)on_done);
if (resource_user->allocated == 0) {
grpc_combiner_execute(exec_ctx, resource_user->resource_quota->combiner,
&resource_user->destroy_closure, GRPC_ERROR_NONE,
false);
}
gpr_mu_unlock(&resource_user->mu);
}
void grpc_resource_user_destroy(grpc_exec_ctx *exec_ctx,
grpc_resource_user *resource_user) {
grpc_resource_quota_internal_unref(exec_ctx, resource_user->resource_quota);
gpr_mu_destroy(&resource_user->mu);
gpr_free(resource_user->name);
}
void grpc_resource_user_alloc(grpc_exec_ctx *exec_ctx,
grpc_resource_user *resource_user, size_t size,
grpc_closure *optional_on_done) {
gpr_mu_lock(&resource_user->mu);
grpc_closure *on_done_destroy = (grpc_closure *)gpr_atm_no_barrier_load(
&resource_user->on_done_destroy_closure);
if (on_done_destroy != NULL) {
/* already shutdown */
if (grpc_resource_quota_trace) {
gpr_log(GPR_DEBUG, "RQ %s %s: alloc %" PRIdPTR " after shutdown",
resource_user->resource_quota->name, resource_user->name, size);
}
grpc_exec_ctx_sched(
exec_ctx, optional_on_done,
GRPC_ERROR_CREATE("Buffer pool user is already shutdown"), NULL);
gpr_mu_unlock(&resource_user->mu);
return;
}
resource_user->allocated += (int64_t)size;
resource_user->free_pool -= (int64_t)size;
if (grpc_resource_quota_trace) {
gpr_log(GPR_DEBUG, "RQ %s %s: alloc %" PRIdPTR "; allocated -> %" PRId64
", free_pool -> %" PRId64,
resource_user->resource_quota->name, resource_user->name, size,
resource_user->allocated, resource_user->free_pool);
}
if (resource_user->free_pool < 0) {
grpc_closure_list_append(&resource_user->on_allocated, optional_on_done,
GRPC_ERROR_NONE);
if (!resource_user->allocating) {
resource_user->allocating = true;
grpc_combiner_execute(exec_ctx, resource_user->resource_quota->combiner,
&resource_user->allocate_closure, GRPC_ERROR_NONE,
false);
}
} else {
grpc_exec_ctx_sched(exec_ctx, optional_on_done, GRPC_ERROR_NONE, NULL);
}
gpr_mu_unlock(&resource_user->mu);
}
void grpc_resource_user_free(grpc_exec_ctx *exec_ctx,
grpc_resource_user *resource_user, size_t size) {
gpr_mu_lock(&resource_user->mu);
GPR_ASSERT(resource_user->allocated >= (int64_t)size);
bool was_zero_or_negative = resource_user->free_pool <= 0;
resource_user->free_pool += (int64_t)size;
resource_user->allocated -= (int64_t)size;
if (grpc_resource_quota_trace) {
gpr_log(GPR_DEBUG, "RQ %s %s: free %" PRIdPTR "; allocated -> %" PRId64
", free_pool -> %" PRId64,
resource_user->resource_quota->name, resource_user->name, size,
resource_user->allocated, resource_user->free_pool);
}
bool is_bigger_than_zero = resource_user->free_pool > 0;
if (is_bigger_than_zero && was_zero_or_negative &&
!resource_user->added_to_free_pool) {
resource_user->added_to_free_pool = true;
grpc_combiner_execute(exec_ctx, resource_user->resource_quota->combiner,
&resource_user->add_to_free_pool_closure,
GRPC_ERROR_NONE, false);
}
grpc_closure *on_done_destroy = (grpc_closure *)gpr_atm_no_barrier_load(
&resource_user->on_done_destroy_closure);
if (on_done_destroy != NULL && resource_user->allocated == 0) {
grpc_combiner_execute(exec_ctx, resource_user->resource_quota->combiner,
&resource_user->destroy_closure, GRPC_ERROR_NONE,
false);
}
gpr_mu_unlock(&resource_user->mu);
}
void grpc_resource_user_post_reclaimer(grpc_exec_ctx *exec_ctx,
grpc_resource_user *resource_user,
bool destructive,
grpc_closure *closure) {
if (gpr_atm_acq_load(&resource_user->on_done_destroy_closure) == 0) {
GPR_ASSERT(resource_user->reclaimers[destructive] == NULL);
resource_user->reclaimers[destructive] = closure;
grpc_combiner_execute(exec_ctx, resource_user->resource_quota->combiner,
&resource_user->post_reclaimer_closure[destructive],
GRPC_ERROR_NONE, false);
} else {
grpc_exec_ctx_sched(exec_ctx, closure, GRPC_ERROR_CANCELLED, NULL);
}
}
void grpc_resource_user_finish_reclamation(grpc_exec_ctx *exec_ctx,
grpc_resource_user *resource_user) {
if (grpc_resource_quota_trace) {
gpr_log(GPR_DEBUG, "RQ %s %s: reclamation complete",
resource_user->resource_quota->name, resource_user->name);
}
grpc_combiner_execute(
exec_ctx, resource_user->resource_quota->combiner,
&resource_user->resource_quota->rq_reclamation_done_closure,
GRPC_ERROR_NONE, false);
}
void grpc_resource_user_slice_allocator_init(
grpc_resource_user_slice_allocator *slice_allocator,
grpc_resource_user *resource_user, grpc_iomgr_cb_func cb, void *p) {
grpc_closure_init(&slice_allocator->on_allocated, ru_allocated_slices,
slice_allocator);
grpc_closure_init(&slice_allocator->on_done, cb, p);
slice_allocator->resource_user = resource_user;
}
void grpc_resource_user_alloc_slices(
grpc_exec_ctx *exec_ctx,
grpc_resource_user_slice_allocator *slice_allocator, size_t length,
size_t count, gpr_slice_buffer *dest) {
slice_allocator->length = length;
slice_allocator->count = count;
slice_allocator->dest = dest;
grpc_resource_user_alloc(exec_ctx, slice_allocator->resource_user,
count * length, &slice_allocator->on_allocated);
}

@ -0,0 +1,224 @@
/*
*
* Copyright 2016, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef GRPC_CORE_LIB_IOMGR_RESOURCE_QUOTA_H
#define GRPC_CORE_LIB_IOMGR_RESOURCE_QUOTA_H
#include <grpc/grpc.h>
#include "src/core/lib/iomgr/exec_ctx.h"
/** \file Tracks resource usage against a pool.
The current implementation tracks only memory usage, but in the future
this may be extended to (for example) threads and file descriptors.
A grpc_resource_quota represents the pooled resources, and
grpc_resource_user instances attach to the quota and consume those
resources. They also offer a vector for reclamation: if we become
resource constrained, grpc_resource_user instances are asked (in turn) to
free up whatever they can so that the system as a whole can make progress.
There are three kinds of reclamation that take place, in order of increasing
invasiveness:
- an internal reclamation, where cached resource at the resource user level
is returned to the quota
- a benign reclamation phase, whereby resources that are in use but are not
helping anything make progress are reclaimed
- a destructive reclamation, whereby resources that are helping something
make progress may be enacted so that at least one part of the system can
complete.
Only one reclamation will be outstanding for a given quota at a given time.
On each reclamation attempt, the kinds of reclamation are tried in order of
increasing invasiveness, stopping at the first one that succeeds. Thus, on a
given reclamation attempt, if internal and benign reclamation both fail, it
will wind up doing a destructive reclamation. However, the next reclamation
attempt may then be able to get what it needs via internal or benign
reclamation, due to resources that may have been freed up by the destructive
reclamation in the previous attempt.
Future work will be to expose the current resource pressure so that back
pressure can be applied to avoid reclamation phases starting.
Resource users own references to resource quotas, and resource quotas
maintain lists of users (which users arrange to leave before they are
destroyed) */
extern int grpc_resource_quota_trace;
grpc_resource_quota *grpc_resource_quota_internal_ref(
grpc_resource_quota *resource_quota);
void grpc_resource_quota_internal_unref(grpc_exec_ctx *exec_ctx,
grpc_resource_quota *resource_quota);
grpc_resource_quota *grpc_resource_quota_from_channel_args(
const grpc_channel_args *channel_args);
/* Resource users are kept in (potentially) several intrusive linked lists
at once. These are the list names. */
typedef enum {
/* Resource users that are waiting for an allocation */
GRPC_RULIST_AWAITING_ALLOCATION,
/* Resource users that have free memory available for internal reclamation */
GRPC_RULIST_NON_EMPTY_FREE_POOL,
/* Resource users that have published a benign reclamation is available */
GRPC_RULIST_RECLAIMER_BENIGN,
/* Resource users that have published a destructive reclamation is
available */
GRPC_RULIST_RECLAIMER_DESTRUCTIVE,
/* Number of lists: must be last */
GRPC_RULIST_COUNT
} grpc_rulist;
typedef struct grpc_resource_user grpc_resource_user;
/* Internal linked list pointers for a resource user */
typedef struct {
grpc_resource_user *next;
grpc_resource_user *prev;
} grpc_resource_user_link;
struct grpc_resource_user {
/* The quota this resource user consumes from */
grpc_resource_quota *resource_quota;
/* Closure to schedule an allocation under the resource quota combiner lock */
grpc_closure allocate_closure;
/* Closure to publish a non empty free pool under the resource quota combiner
lock */
grpc_closure add_to_free_pool_closure;
gpr_mu mu;
/* Total allocated memory outstanding by this resource user in bytes;
always positive */
int64_t allocated;
/* The amount of memory (in bytes) this user has cached for its own use: to
avoid quota contention, each resource user can keep some memory in
addition to what it is immediately using (e.g., for caching), and the quota
can pull it back under memory pressure.
This value can become negative if more memory has been requested than
existed in the free pool, at which point the quota is consulted to bring
this value non-negative (asynchronously). */
int64_t free_pool;
/* A list of closures to call once free_pool becomes non-negative - ie when
all outstanding allocations have been granted. */
grpc_closure_list on_allocated;
/* True if we are currently trying to allocate from the quota, false if not */
bool allocating;
/* True if we are currently trying to add ourselves to the non-free quota
list, false otherwise */
bool added_to_free_pool;
/* Reclaimers: index 0 is the benign reclaimer, 1 is the destructive reclaimer
*/
grpc_closure *reclaimers[2];
/* Trampoline closures to finish reclamation and re-enter the quota combiner
lock */
grpc_closure post_reclaimer_closure[2];
/* Closure to execute under the quota combiner to de-register and shutdown the
resource user */
grpc_closure destroy_closure;
/* User supplied closure to call once the user has finished shutting down AND
all outstanding allocations have been freed. Real type is grpc_closure*,
but it's stored as an atomic to avoid a mutex on some fast paths. */
gpr_atm on_done_destroy_closure;
/* Links in the various grpc_rulist lists */
grpc_resource_user_link links[GRPC_RULIST_COUNT];
/* The name of this resource user, for debugging/tracing */
char *name;
};
void grpc_resource_user_init(grpc_resource_user *resource_user,
grpc_resource_quota *resource_quota,
const char *name);
void grpc_resource_user_shutdown(grpc_exec_ctx *exec_ctx,
grpc_resource_user *resource_user,
grpc_closure *on_done);
void grpc_resource_user_destroy(grpc_exec_ctx *exec_ctx,
grpc_resource_user *resource_user);
/* Allocate from the resource user (and its quota).
If optional_on_done is NULL, then allocate immediately. This may push the
quota over-limit, at which point reclamation will kick in.
If optional_on_done is non-NULL, it will be scheduled when the allocation has
been granted by the quota. */
void grpc_resource_user_alloc(grpc_exec_ctx *exec_ctx,
grpc_resource_user *resource_user, size_t size,
grpc_closure *optional_on_done);
/* Release memory back to the quota */
void grpc_resource_user_free(grpc_exec_ctx *exec_ctx,
grpc_resource_user *resource_user, size_t size);
/* Post a memory reclaimer to the resource user. Only one benign and one
destructive reclaimer can be posted at once. When executed, the reclaimer
MUST call grpc_resource_user_finish_reclamation before it completes, to
return control to the resource quota. */
void grpc_resource_user_post_reclaimer(grpc_exec_ctx *exec_ctx,
grpc_resource_user *resource_user,
bool destructive, grpc_closure *closure);
/* Finish a reclamation step */
void grpc_resource_user_finish_reclamation(grpc_exec_ctx *exec_ctx,
grpc_resource_user *resource_user);
/* Helper to allocate slices from a resource user */
typedef struct grpc_resource_user_slice_allocator {
/* Closure for when a resource user allocation completes */
grpc_closure on_allocated;
/* Closure to call when slices have been allocated */
grpc_closure on_done;
/* Length of slices to allocate on the current request */
size_t length;
/* Number of slices to allocate on the current request */
size_t count;
/* Destination for slices to allocate on the current request */
gpr_slice_buffer *dest;
/* Parent resource user */
grpc_resource_user *resource_user;
} grpc_resource_user_slice_allocator;
/* Initialize a slice allocator.
When an allocation is completed, calls \a cb with arg \p. */
void grpc_resource_user_slice_allocator_init(
grpc_resource_user_slice_allocator *slice_allocator,
grpc_resource_user *resource_user, grpc_iomgr_cb_func cb, void *p);
/* Allocate \a count slices of length \a length into \a dest. Only one request
can be outstanding at a time. */
void grpc_resource_user_alloc_slices(
grpc_exec_ctx *exec_ctx,
grpc_resource_user_slice_allocator *slice_allocator, size_t length,
size_t count, gpr_slice_buffer *dest);
#endif /* GRPC_CORE_LIB_IOMGR_RESOURCE_QUOTA_H */

@ -39,6 +39,10 @@
#include "src/core/lib/iomgr/pollset_set.h"
#include "src/core/lib/iomgr/resolve_address.h"
/* Channel arg (integer) setting how large a slice to try and read from the wire
each time recvmsg (or equivalent) is called */
#define GRPC_ARG_TCP_READ_CHUNK_SIZE "grpc.experimental.tcp_read_chunk_size"
/* Asynchronously connect to an address (specified as (addr, len)), and call
cb with arg and the completed connection when done (or call cb with arg and
NULL on failure).
@ -47,6 +51,7 @@
void grpc_tcp_client_connect(grpc_exec_ctx *exec_ctx, grpc_closure *on_connect,
grpc_endpoint **endpoint,
grpc_pollset_set *interested_parties,
const grpc_channel_args *channel_args,
const grpc_resolved_address *addr,
gpr_timespec deadline);

@ -35,7 +35,7 @@
#ifdef GRPC_POSIX_SOCKET
#include "src/core/lib/iomgr/tcp_client.h"
#include "src/core/lib/iomgr/tcp_client_posix.h"
#include <errno.h>
#include <netinet/in.h>
@ -47,6 +47,7 @@
#include <grpc/support/string_util.h>
#include <grpc/support/time.h>
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/iomgr/ev_posix.h"
#include "src/core/lib/iomgr/iomgr_posix.h"
#include "src/core/lib/iomgr/sockaddr_utils.h"
@ -69,6 +70,7 @@ typedef struct {
char *addr_str;
grpc_endpoint **ep;
grpc_closure *closure;
grpc_channel_args *channel_args;
} async_connect;
static grpc_error *prepare_socket(const grpc_resolved_address *addr, int fd) {
@ -114,10 +116,39 @@ static void tc_on_alarm(grpc_exec_ctx *exec_ctx, void *acp, grpc_error *error) {
if (done) {
gpr_mu_destroy(&ac->mu);
gpr_free(ac->addr_str);
grpc_channel_args_destroy(ac->channel_args);
gpr_free(ac);
}
}
grpc_endpoint *grpc_tcp_client_create_from_fd(
grpc_exec_ctx *exec_ctx, grpc_fd *fd, const grpc_channel_args *channel_args,
const char *addr_str) {
size_t tcp_read_chunk_size = GRPC_TCP_DEFAULT_READ_SLICE_SIZE;
grpc_resource_quota *resource_quota = grpc_resource_quota_create(NULL);
if (channel_args != NULL) {
for (size_t i = 0; i < channel_args->num_args; i++) {
if (0 ==
strcmp(channel_args->args[i].key, GRPC_ARG_TCP_READ_CHUNK_SIZE)) {
grpc_integer_options options = {(int)tcp_read_chunk_size, 1,
8 * 1024 * 1024};
tcp_read_chunk_size = (size_t)grpc_channel_arg_get_integer(
&channel_args->args[i], options);
} else if (0 ==
strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) {
grpc_resource_quota_internal_unref(exec_ctx, resource_quota);
resource_quota = grpc_resource_quota_internal_ref(
channel_args->args[i].value.pointer.p);
}
}
}
grpc_endpoint *ep =
grpc_tcp_create(fd, resource_quota, tcp_read_chunk_size, addr_str);
grpc_resource_quota_internal_unref(exec_ctx, resource_quota);
return ep;
}
static void on_writable(grpc_exec_ctx *exec_ctx, void *acp, grpc_error *error) {
async_connect *ac = acp;
int so_error = 0;
@ -165,7 +196,8 @@ static void on_writable(grpc_exec_ctx *exec_ctx, void *acp, grpc_error *error) {
switch (so_error) {
case 0:
grpc_pollset_set_del_fd(exec_ctx, ac->interested_parties, fd);
*ep = grpc_tcp_create(fd, GRPC_TCP_DEFAULT_READ_SLICE_SIZE, ac->addr_str);
*ep = grpc_tcp_client_create_from_fd(exec_ctx, fd, ac->channel_args,
ac->addr_str);
fd = NULL;
break;
case ENOBUFS:
@ -215,6 +247,7 @@ finish:
if (done) {
gpr_mu_destroy(&ac->mu);
gpr_free(ac->addr_str);
grpc_channel_args_destroy(ac->channel_args);
gpr_free(ac);
}
grpc_exec_ctx_sched(exec_ctx, closure, error, NULL);
@ -223,6 +256,7 @@ finish:
static void tcp_client_connect_impl(grpc_exec_ctx *exec_ctx,
grpc_closure *closure, grpc_endpoint **ep,
grpc_pollset_set *interested_parties,
const grpc_channel_args *channel_args,
const grpc_resolved_address *addr,
gpr_timespec deadline) {
int fd;
@ -270,7 +304,8 @@ static void tcp_client_connect_impl(grpc_exec_ctx *exec_ctx,
fdobj = grpc_fd_create(fd, name);
if (err >= 0) {
*ep = grpc_tcp_create(fdobj, GRPC_TCP_DEFAULT_READ_SLICE_SIZE, addr_str);
*ep =
grpc_tcp_client_create_from_fd(exec_ctx, fdobj, channel_args, addr_str);
grpc_exec_ctx_sched(exec_ctx, closure, GRPC_ERROR_NONE, NULL);
goto done;
}
@ -295,6 +330,7 @@ static void tcp_client_connect_impl(grpc_exec_ctx *exec_ctx,
ac->refs = 2;
ac->write_closure.cb = on_writable;
ac->write_closure.cb_arg = ac;
ac->channel_args = grpc_channel_args_copy(channel_args);
if (grpc_tcp_trace) {
gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: asynchronously connecting",
@ -316,16 +352,18 @@ done:
// overridden by api_fuzzer.c
void (*grpc_tcp_client_connect_impl)(
grpc_exec_ctx *exec_ctx, grpc_closure *closure, grpc_endpoint **ep,
grpc_pollset_set *interested_parties, const grpc_resolved_address *addr,
grpc_pollset_set *interested_parties, const grpc_channel_args *channel_args,
const grpc_resolved_address *addr,
gpr_timespec deadline) = tcp_client_connect_impl;
void grpc_tcp_client_connect(grpc_exec_ctx *exec_ctx, grpc_closure *closure,
grpc_endpoint **ep,
grpc_pollset_set *interested_parties,
const grpc_channel_args *channel_args,
const grpc_resolved_address *addr,
gpr_timespec deadline) {
grpc_tcp_client_connect_impl(exec_ctx, closure, ep, interested_parties, addr,
deadline);
grpc_tcp_client_connect_impl(exec_ctx, closure, ep, interested_parties,
channel_args, addr, deadline);
}
#endif

@ -0,0 +1,45 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef GRPC_CORE_LIB_IOMGR_TCP_CLIENT_POSIX_H
#define GRPC_CORE_LIB_IOMGR_TCP_CLIENT_POSIX_H
#include "src/core/lib/iomgr/endpoint.h"
#include "src/core/lib/iomgr/ev_posix.h"
#include "src/core/lib/iomgr/tcp_client.h"
grpc_endpoint *grpc_tcp_client_create_from_fd(
grpc_exec_ctx *exec_ctx, grpc_fd *fd, const grpc_channel_args *channel_args,
const char *addr_str);
#endif /* GRPC_CORE_LIB_IOMGR_TCP_CLIENT_POSIX_H */

@ -80,6 +80,7 @@ typedef struct {
msg_iovlen_type iov_size; /* Number of slices to allocate per read attempt */
size_t slice_size;
gpr_refcount refcount;
gpr_atm shutdown_count;
/* garbage after the last read */
gpr_slice_buffer last_read_buffer;
@ -100,15 +101,29 @@ typedef struct {
grpc_closure write_closure;
char *peer_string;
grpc_resource_user resource_user;
grpc_resource_user_slice_allocator slice_allocator;
} grpc_tcp;
static void tcp_handle_read(grpc_exec_ctx *exec_ctx, void *arg /* grpc_tcp */,
grpc_error *error);
static void tcp_handle_write(grpc_exec_ctx *exec_ctx, void *arg /* grpc_tcp */,
grpc_error *error);
static void tcp_unref_closure(grpc_exec_ctx *exec_ctx, void *arg /* grpc_tcp */,
grpc_error *error);
static void tcp_maybe_shutdown_resource_user(grpc_exec_ctx *exec_ctx,
grpc_tcp *tcp) {
if (gpr_atm_full_fetch_add(&tcp->shutdown_count, 1) == 0) {
grpc_resource_user_shutdown(exec_ctx, &tcp->resource_user,
grpc_closure_create(tcp_unref_closure, tcp));
}
}
static void tcp_shutdown(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) {
grpc_tcp *tcp = (grpc_tcp *)ep;
tcp_maybe_shutdown_resource_user(exec_ctx, tcp);
grpc_fd_shutdown(exec_ctx, tcp->em_fd);
}
@ -116,6 +131,7 @@ static void tcp_free(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) {
grpc_fd_orphan(exec_ctx, tcp->em_fd, tcp->release_fd_cb, tcp->release_fd,
"tcp_unref_orphan");
gpr_slice_buffer_destroy(&tcp->last_read_buffer);
grpc_resource_user_destroy(exec_ctx, &tcp->resource_user);
gpr_free(tcp->peer_string);
gpr_free(tcp);
}
@ -152,9 +168,16 @@ static void tcp_unref(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) {
static void tcp_ref(grpc_tcp *tcp) { gpr_ref(&tcp->refcount); }
#endif
static void tcp_unref_closure(grpc_exec_ctx *exec_ctx, void *arg,
grpc_error *error) {
TCP_UNREF(exec_ctx, arg, "resource_user");
}
static void tcp_destroy(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) {
grpc_network_status_unregister_endpoint(ep);
grpc_tcp *tcp = (grpc_tcp *)ep;
tcp_maybe_shutdown_resource_user(exec_ctx, tcp);
gpr_slice_buffer_reset_and_unref(&tcp->last_read_buffer);
TCP_UNREF(exec_ctx, tcp, "destroy");
}
@ -181,7 +204,7 @@ static void call_read_cb(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp,
}
#define MAX_READ_IOVEC 4
static void tcp_continue_read(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) {
static void tcp_do_read(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) {
struct msghdr msg;
struct iovec iov[MAX_READ_IOVEC];
ssize_t read_bytes;
@ -192,10 +215,6 @@ static void tcp_continue_read(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) {
GPR_ASSERT(tcp->incoming_buffer->count <= MAX_READ_IOVEC);
GPR_TIMER_BEGIN("tcp_continue_read", 0);
while (tcp->incoming_buffer->count < (size_t)tcp->iov_size) {
gpr_slice_buffer_add_indexed(tcp->incoming_buffer,
gpr_slice_malloc(tcp->slice_size));
}
for (i = 0; i < tcp->incoming_buffer->count; i++) {
iov[i].iov_base = GPR_SLICE_START_PTR(tcp->incoming_buffer->slices[i]);
iov[i].iov_len = GPR_SLICE_LENGTH(tcp->incoming_buffer->slices[i]);
@ -232,7 +251,7 @@ static void tcp_continue_read(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) {
} else if (read_bytes == 0) {
/* 0 read size ==> end of stream */
gpr_slice_buffer_reset_and_unref(tcp->incoming_buffer);
call_read_cb(exec_ctx, tcp, GRPC_ERROR_CREATE("EOF"));
call_read_cb(exec_ctx, tcp, GRPC_ERROR_CREATE("Socket closed"));
TCP_UNREF(exec_ctx, tcp, "read");
} else {
GPR_ASSERT((size_t)read_bytes <= tcp->incoming_buffer->length);
@ -252,6 +271,30 @@ static void tcp_continue_read(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) {
GPR_TIMER_END("tcp_continue_read", 0);
}
static void tcp_read_allocation_done(grpc_exec_ctx *exec_ctx, void *tcpp,
grpc_error *error) {
grpc_tcp *tcp = tcpp;
if (error != GRPC_ERROR_NONE) {
gpr_slice_buffer_reset_and_unref(tcp->incoming_buffer);
gpr_slice_buffer_reset_and_unref(&tcp->last_read_buffer);
call_read_cb(exec_ctx, tcp, GRPC_ERROR_REF(error));
TCP_UNREF(exec_ctx, tcp, "read");
} else {
tcp_do_read(exec_ctx, tcp);
}
}
static void tcp_continue_read(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) {
if (tcp->incoming_buffer->count < (size_t)tcp->iov_size) {
grpc_resource_user_alloc_slices(
exec_ctx, &tcp->slice_allocator, tcp->slice_size,
(size_t)tcp->iov_size - tcp->incoming_buffer->count,
tcp->incoming_buffer);
} else {
tcp_do_read(exec_ctx, tcp);
}
}
static void tcp_handle_read(grpc_exec_ctx *exec_ctx, void *arg /* grpc_tcp */,
grpc_error *error) {
grpc_tcp *tcp = (grpc_tcp *)arg;
@ -259,6 +302,7 @@ static void tcp_handle_read(grpc_exec_ctx *exec_ctx, void *arg /* grpc_tcp */,
if (error != GRPC_ERROR_NONE) {
gpr_slice_buffer_reset_and_unref(tcp->incoming_buffer);
gpr_slice_buffer_reset_and_unref(&tcp->last_read_buffer);
call_read_cb(exec_ctx, tcp, GRPC_ERROR_REF(error));
TCP_UNREF(exec_ctx, tcp, "read");
} else {
@ -469,6 +513,11 @@ static grpc_workqueue *tcp_get_workqueue(grpc_endpoint *ep) {
return grpc_fd_get_workqueue(tcp->em_fd);
}
static grpc_resource_user *tcp_get_resource_user(grpc_endpoint *ep) {
grpc_tcp *tcp = (grpc_tcp *)ep;
return &tcp->resource_user;
}
static const grpc_endpoint_vtable vtable = {tcp_read,
tcp_write,
tcp_get_workqueue,
@ -476,10 +525,12 @@ static const grpc_endpoint_vtable vtable = {tcp_read,
tcp_add_to_pollset_set,
tcp_shutdown,
tcp_destroy,
tcp_get_resource_user,
tcp_get_peer};
grpc_endpoint *grpc_tcp_create(grpc_fd *em_fd, size_t slice_size,
const char *peer_string) {
grpc_endpoint *grpc_tcp_create(grpc_fd *em_fd,
grpc_resource_quota *resource_quota,
size_t slice_size, const char *peer_string) {
grpc_tcp *tcp = (grpc_tcp *)gpr_malloc(sizeof(grpc_tcp));
tcp->base.vtable = &vtable;
tcp->peer_string = gpr_strdup(peer_string);
@ -492,14 +543,20 @@ grpc_endpoint *grpc_tcp_create(grpc_fd *em_fd, size_t slice_size,
tcp->slice_size = slice_size;
tcp->iov_size = 1;
tcp->finished_edge = true;
/* paired with unref in grpc_tcp_destroy */
gpr_ref_init(&tcp->refcount, 1);
/* paired with unref in grpc_tcp_destroy, and with the shutdown for our
* resource_user */
gpr_ref_init(&tcp->refcount, 2);
gpr_atm_no_barrier_store(&tcp->shutdown_count, 0);
tcp->em_fd = em_fd;
tcp->read_closure.cb = tcp_handle_read;
tcp->read_closure.cb_arg = tcp;
tcp->write_closure.cb = tcp_handle_write;
tcp->write_closure.cb_arg = tcp;
gpr_slice_buffer_init(&tcp->last_read_buffer);
grpc_resource_user_init(&tcp->resource_user, resource_quota, peer_string);
grpc_resource_user_slice_allocator_init(&tcp->slice_allocator,
&tcp->resource_user,
tcp_read_allocation_done, tcp);
/* Tell network status tracker about new endpoint */
grpc_network_status_register_endpoint(&tcp->base);
@ -514,10 +571,13 @@ int grpc_tcp_fd(grpc_endpoint *ep) {
void grpc_tcp_destroy_and_release_fd(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
int *fd, grpc_closure *done) {
grpc_network_status_unregister_endpoint(ep);
grpc_tcp *tcp = (grpc_tcp *)ep;
GPR_ASSERT(ep->vtable == &vtable);
tcp->release_fd = fd;
tcp->release_fd_cb = done;
tcp_maybe_shutdown_resource_user(exec_ctx, tcp);
gpr_slice_buffer_reset_and_unref(&tcp->last_read_buffer);
TCP_UNREF(exec_ctx, tcp, "destroy");
}

@ -53,8 +53,8 @@ extern int grpc_tcp_trace;
/* Create a tcp endpoint given a file desciptor and a read slice size.
Takes ownership of fd. */
grpc_endpoint *grpc_tcp_create(grpc_fd *fd, size_t read_slice_size,
const char *peer_string);
grpc_endpoint *grpc_tcp_create(grpc_fd *fd, grpc_resource_quota *resource_quota,
size_t read_slice_size, const char *peer_string);
/* Return the tcp endpoint's fd, or -1 if this is not available. Does not
release the fd.

@ -61,7 +61,8 @@ typedef void (*grpc_tcp_server_cb)(grpc_exec_ctx *exec_ctx, void *arg,
/* Create a server, initially not bound to any ports. The caller owns one ref.
If shutdown_complete is not NULL, it will be used by
grpc_tcp_server_unref() when the ref count reaches zero. */
grpc_error *grpc_tcp_server_create(grpc_closure *shutdown_complete,
grpc_error *grpc_tcp_server_create(grpc_exec_ctx *exec_ctx,
grpc_closure *shutdown_complete,
const grpc_channel_args *args,
grpc_tcp_server **server);

@ -134,6 +134,8 @@ struct grpc_tcp_server {
/* next pollset to assign a channel to */
gpr_atm next_pollset_to_assign;
grpc_resource_quota *resource_quota;
};
static gpr_once check_init = GPR_ONCE_INIT;
@ -150,23 +152,37 @@ static void init(void) {
#endif
}
grpc_error *grpc_tcp_server_create(grpc_closure *shutdown_complete,
grpc_error *grpc_tcp_server_create(grpc_exec_ctx *exec_ctx,
grpc_closure *shutdown_complete,
const grpc_channel_args *args,
grpc_tcp_server **server) {
gpr_once_init(&check_init, init);
grpc_tcp_server *s = gpr_malloc(sizeof(grpc_tcp_server));
s->so_reuseport = has_so_reuseport;
s->resource_quota = grpc_resource_quota_create(NULL);
for (size_t i = 0; i < (args == NULL ? 0 : args->num_args); i++) {
if (0 == strcmp(GRPC_ARG_ALLOW_REUSEPORT, args->args[i].key)) {
if (args->args[i].type == GRPC_ARG_INTEGER) {
s->so_reuseport =
has_so_reuseport && (args->args[i].value.integer != 0);
} else {
grpc_resource_quota_internal_unref(exec_ctx, s->resource_quota);
gpr_free(s);
return GRPC_ERROR_CREATE(GRPC_ARG_ALLOW_REUSEPORT
" must be an integer");
}
} else if (0 == strcmp(GRPC_ARG_RESOURCE_QUOTA, args->args[i].key)) {
if (args->args[i].type == GRPC_ARG_POINTER) {
grpc_resource_quota_internal_unref(exec_ctx, s->resource_quota);
s->resource_quota =
grpc_resource_quota_internal_ref(args->args[i].value.pointer.p);
} else {
grpc_resource_quota_internal_unref(exec_ctx, s->resource_quota);
gpr_free(s);
return GRPC_ERROR_CREATE(GRPC_ARG_RESOURCE_QUOTA
" must be a pointer to a buffer pool");
}
}
}
gpr_ref_init(&s->refs, 1);
@ -203,6 +219,8 @@ static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) {
gpr_free(sp);
}
grpc_resource_quota_internal_unref(exec_ctx, s->resource_quota);
gpr_free(s);
}
@ -419,7 +437,8 @@ static void on_read(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *err) {
sp->server->on_accept_cb(
exec_ctx, sp->server->on_accept_cb_arg,
grpc_tcp_create(fdobj, GRPC_TCP_DEFAULT_READ_SLICE_SIZE, addr_str),
grpc_tcp_create(fdobj, sp->server->resource_quota,
GRPC_TCP_DEFAULT_READ_SLICE_SIZE, addr_str),
read_notifier_pollset, &acceptor);
gpr_free(name);

@ -124,11 +124,14 @@ static int is_stack_running_on_compute_engine(void) {
grpc_httpcli_context_init(&context);
grpc_resource_quota *resource_quota =
grpc_resource_quota_create("google_default_credentials");
grpc_httpcli_get(
&exec_ctx, &context, &detector.pollent, &request,
&exec_ctx, &context, &detector.pollent, resource_quota, &request,
gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), max_detection_delay),
grpc_closure_create(on_compute_engine_detection_http_response, &detector),
&detector.response);
grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
grpc_exec_ctx_flush(&exec_ctx);

@ -657,11 +657,17 @@ static void on_openid_config_retrieved(grpc_exec_ctx *exec_ctx, void *user_data,
*(req.host + (req.http.path - jwks_uri)) = '\0';
}
/* TODO(ctiller): Carry the resource_quota in ctx and share it with the host
channel. This would allow us to cancel an authentication query when under
extreme memory pressure. */
grpc_resource_quota *resource_quota =
grpc_resource_quota_create("jwt_verifier");
grpc_httpcli_get(
exec_ctx, &ctx->verifier->http_ctx, &ctx->pollent, &req,
exec_ctx, &ctx->verifier->http_ctx, &ctx->pollent, resource_quota, &req,
gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_max_delay),
grpc_closure_create(on_keys_retrieved, ctx),
&ctx->responses[HTTP_RESPONSE_KEYS]);
grpc_resource_quota_internal_unref(exec_ctx, resource_quota);
grpc_json_destroy(json);
gpr_free(req.host);
return;
@ -764,10 +770,16 @@ static void retrieve_key_and_verify(grpc_exec_ctx *exec_ctx,
rsp_idx = HTTP_RESPONSE_OPENID;
}
/* TODO(ctiller): Carry the resource_quota in ctx and share it with the host
channel. This would allow us to cancel an authentication query when under
extreme memory pressure. */
grpc_resource_quota *resource_quota =
grpc_resource_quota_create("jwt_verifier");
grpc_httpcli_get(
exec_ctx, &ctx->verifier->http_ctx, &ctx->pollent, &req,
exec_ctx, &ctx->verifier->http_ctx, &ctx->pollent, resource_quota, &req,
gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_max_delay),
http_cb, &ctx->responses[rsp_idx]);
grpc_resource_quota_internal_unref(exec_ctx, resource_quota);
gpr_free(req.host);
gpr_free(req.http.path);
return;

@ -307,9 +307,15 @@ static void compute_engine_fetch_oauth2(
request.http.path = GRPC_COMPUTE_ENGINE_METADATA_TOKEN_PATH;
request.http.hdr_count = 1;
request.http.hdrs = &header;
grpc_httpcli_get(exec_ctx, httpcli_context, pollent, &request, deadline,
grpc_closure_create(response_cb, metadata_req),
/* TODO(ctiller): Carry the resource_quota in ctx and share it with the host
channel. This would allow us to cancel an authentication query when under
extreme memory pressure. */
grpc_resource_quota *resource_quota =
grpc_resource_quota_create("oauth2_credentials");
grpc_httpcli_get(exec_ctx, httpcli_context, pollent, resource_quota, &request,
deadline, grpc_closure_create(response_cb, metadata_req),
&metadata_req->response);
grpc_resource_quota_internal_unref(exec_ctx, resource_quota);
}
grpc_call_credentials *grpc_google_compute_engine_credentials_create(
@ -357,10 +363,16 @@ static void refresh_token_fetch_oauth2(
request.http.hdr_count = 1;
request.http.hdrs = &header;
request.handshaker = &grpc_httpcli_ssl;
grpc_httpcli_post(exec_ctx, httpcli_context, pollent, &request, body,
strlen(body), deadline,
/* TODO(ctiller): Carry the resource_quota in ctx and share it with the host
channel. This would allow us to cancel an authentication query when under
extreme memory pressure. */
grpc_resource_quota *resource_quota =
grpc_resource_quota_create("oauth2_credentials_refresh");
grpc_httpcli_post(exec_ctx, httpcli_context, pollent, resource_quota,
&request, body, strlen(body), deadline,
grpc_closure_create(response_cb, metadata_req),
&metadata_req->response);
grpc_resource_quota_internal_unref(exec_ctx, resource_quota);
gpr_free(body);
}

@ -370,6 +370,12 @@ static grpc_workqueue *endpoint_get_workqueue(grpc_endpoint *secure_ep) {
return grpc_endpoint_get_workqueue(ep->wrapped_ep);
}
static grpc_resource_user *endpoint_get_resource_user(
grpc_endpoint *secure_ep) {
secure_endpoint *ep = (secure_endpoint *)secure_ep;
return grpc_endpoint_get_resource_user(ep->wrapped_ep);
}
static const grpc_endpoint_vtable vtable = {endpoint_read,
endpoint_write,
endpoint_get_workqueue,
@ -377,6 +383,7 @@ static const grpc_endpoint_vtable vtable = {endpoint_read,
endpoint_add_to_pollset_set,
endpoint_shutdown,
endpoint_destroy,
endpoint_get_resource_user,
endpoint_get_peer};
grpc_endpoint *grpc_secure_endpoint_create(

@ -1516,8 +1516,10 @@ static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx,
call, STATUS_FROM_API_OVERRIDE,
GRPC_MDSTR_REF(call->send_extra_metadata[1].md->value));
}
set_status_code(call, STATUS_FROM_API_OVERRIDE,
(uint32_t)op->data.send_status_from_server.status);
if (op->data.send_status_from_server.status != GRPC_STATUS_OK) {
set_status_code(call, STATUS_FROM_API_OVERRIDE,
(uint32_t)op->data.send_status_from_server.status);
}
if (!prepare_application_metadata(
call,
(int)op->data.send_status_from_server.trailing_metadata_count,

@ -52,6 +52,7 @@
#include "src/core/lib/iomgr/combiner.h"
#include "src/core/lib/iomgr/executor.h"
#include "src/core/lib/iomgr/iomgr.h"
#include "src/core/lib/iomgr/resource_quota.h"
#include "src/core/lib/profiling/timers.h"
#include "src/core/lib/surface/api_trace.h"
#include "src/core/lib/surface/call.h"
@ -191,6 +192,7 @@ void grpc_init(void) {
// Default timeout trace to 1
grpc_cq_event_timeout_trace = 1;
grpc_register_tracer("op_failure", &grpc_trace_operation_failures);
grpc_register_tracer("resource_quota", &grpc_resource_quota_trace);
#ifndef NDEBUG
grpc_register_tracer("pending_tags", &grpc_trace_pending_tags);
#endif

@ -34,6 +34,7 @@
#include <sstream>
#include <grpc++/resource_quota.h>
#include <grpc/impl/codegen/grpc_types.h>
#include <grpc/support/log.h>
#include "src/core/lib/channel/channel_args.h"
@ -113,6 +114,13 @@ void ChannelArguments::SetUserAgentPrefix(
}
}
void ChannelArguments::SetResourceQuota(
const grpc::ResourceQuota& resource_quota) {
SetPointerWithVtable(GRPC_ARG_RESOURCE_QUOTA,
resource_quota.c_resource_quota(),
grpc_resource_quota_arg_vtable());
}
void ChannelArguments::SetInt(const grpc::string& key, int value) {
grpc_arg arg;
arg.type = GRPC_ARG_INTEGER;
@ -127,12 +135,18 @@ void ChannelArguments::SetPointer(const grpc::string& key, void* value) {
static const grpc_arg_pointer_vtable vtable = {
&PointerVtableMembers::Copy, &PointerVtableMembers::Destroy,
&PointerVtableMembers::Compare};
SetPointerWithVtable(key, value, &vtable);
}
void ChannelArguments::SetPointerWithVtable(
const grpc::string& key, void* value,
const grpc_arg_pointer_vtable* vtable) {
grpc_arg arg;
arg.type = GRPC_ARG_POINTER;
strings_.push_back(key);
arg.key = const_cast<char*>(strings_.back().c_str());
arg.value.pointer.p = value;
arg.value.pointer.vtable = &vtable;
arg.value.pointer.vtable = vtable;
args_.push_back(arg);
}

@ -0,0 +1,51 @@
/*
*
* Copyright 2016, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <grpc++/resource_quota.h>
#include <grpc/grpc.h>
namespace grpc {
ResourceQuota::ResourceQuota() : impl_(grpc_resource_quota_create(nullptr)) {}
ResourceQuota::ResourceQuota(const grpc::string& name)
: impl_(grpc_resource_quota_create(name.c_str())) {}
ResourceQuota::~ResourceQuota() { grpc_resource_quota_unref(impl_); }
ResourceQuota& ResourceQuota::Resize(size_t new_size) {
grpc_resource_quota_resize(impl_, new_size);
return *this;
}
} // namespace grpc

@ -34,6 +34,7 @@
#include <grpc++/server_builder.h>
#include <grpc++/impl/service_type.h>
#include <grpc++/resource_quota.h>
#include <grpc++/server.h>
#include <grpc/support/log.h>
#include <grpc/support/useful.h>
@ -54,6 +55,7 @@ static void do_plugin_list_init(void) {
ServerBuilder::ServerBuilder()
: max_receive_message_size_(-1),
max_send_message_size_(-1),
resource_quota_(nullptr),
generic_service_(nullptr) {
gpr_once_init(&once_init_plugin_list, do_plugin_list_init);
for (auto it = g_plugin_factory_list->begin();
@ -70,6 +72,12 @@ ServerBuilder::ServerBuilder()
sizeof(maybe_default_compression_algorithm_));
}
ServerBuilder::~ServerBuilder() {
if (resource_quota_ != nullptr) {
grpc_resource_quota_unref(resource_quota_);
}
}
std::unique_ptr<ServerCompletionQueue> ServerBuilder::AddCompletionQueue(
bool is_frequently_polled) {
ServerCompletionQueue* cq = new ServerCompletionQueue(is_frequently_polled);
@ -130,6 +138,16 @@ ServerBuilder& ServerBuilder::SetDefaultCompressionAlgorithm(
return *this;
}
ServerBuilder& ServerBuilder::SetResourceQuota(
const grpc::ResourceQuota& resource_quota) {
if (resource_quota_ != nullptr) {
grpc_resource_quota_unref(resource_quota_);
}
resource_quota_ = resource_quota.c_resource_quota();
grpc_resource_quota_ref(resource_quota_);
return *this;
}
ServerBuilder& ServerBuilder::AddListeningPort(
const grpc::string& addr, std::shared_ptr<ServerCredentials> creds,
int* selected_port) {
@ -178,6 +196,10 @@ std::unique_ptr<Server> ServerBuilder::BuildAndStart() {
args.SetInt(GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM,
maybe_default_compression_algorithm_.algorithm);
}
if (resource_quota_ != nullptr) {
args.SetPointerWithVtable(GRPC_ARG_RESOURCE_QUOTA, resource_quota_,
grpc_resource_quota_arg_vtable());
}
std::unique_ptr<Server> server(new Server(thread_pool.release(), true,
max_receive_message_size_, &args));
ServerInitializer* initializer = server->initializer();

@ -137,6 +137,11 @@ message ServerConfig {
// If we use an OTHER_SERVER client_type, this string gives more detail
string other_server_api = 11;
// c++-only options (for now) --------------------------------
// Buffer pool size (no buffer pool specified if unset)
int32 resource_quota_size = 1001;
}
message ServerArgs {
@ -213,6 +218,10 @@ message ScenarioResultSummary
double latency_95 = 9;
double latency_99 = 10;
double latency_999 = 11;
// Number of requests that succeeded/failed
double successful_requests_per_second = 12;
double failed_requests_per_second = 13;
}
// Results of a single benchmark scenario.
@ -232,4 +241,6 @@ message ScenarioResult {
// Information on success or failure of each worker
repeated bool client_success = 7;
repeated bool server_success = 8;
// Number of failed requests (one row per status code seen)
repeated RequestResultCount request_results = 9;
}

@ -59,6 +59,11 @@ message HistogramData {
double count = 6;
}
message RequestResultCount {
int32 status_code = 1;
int64 count = 2;
}
message ClientStats {
// Latency histogram. Data points are in nanoseconds.
HistogramData latencies = 1;
@ -67,4 +72,7 @@ message ClientStats {
double time_elapsed = 2;
double time_user = 3;
double time_system = 4;
// Number of failed requests (one row per status code seen)
repeated RequestResultCount request_results = 5;
}

@ -122,6 +122,7 @@ CORE_SOURCE_FILES = [
'src/core/lib/iomgr/resolve_address_posix.c',
'src/core/lib/iomgr/resolve_address_uv.c',
'src/core/lib/iomgr/resolve_address_windows.c',
'src/core/lib/iomgr/resource_quota.c',
'src/core/lib/iomgr/sockaddr_utils.c',
'src/core/lib/iomgr/socket_utils_common_posix.c',
'src/core/lib/iomgr/socket_utils_linux.c',

@ -132,6 +132,11 @@ grpc_header_key_is_legal_type grpc_header_key_is_legal_import;
grpc_header_nonbin_value_is_legal_type grpc_header_nonbin_value_is_legal_import;
grpc_is_binary_header_type grpc_is_binary_header_import;
grpc_call_error_to_string_type grpc_call_error_to_string_import;
grpc_resource_quota_create_type grpc_resource_quota_create_import;
grpc_resource_quota_ref_type grpc_resource_quota_ref_import;
grpc_resource_quota_unref_type grpc_resource_quota_unref_import;
grpc_resource_quota_resize_type grpc_resource_quota_resize_import;
grpc_resource_quota_arg_vtable_type grpc_resource_quota_arg_vtable_import;
grpc_insecure_channel_create_from_fd_type grpc_insecure_channel_create_from_fd_import;
grpc_server_add_insecure_channel_from_fd_type grpc_server_add_insecure_channel_from_fd_import;
grpc_use_signal_type grpc_use_signal_import;
@ -401,6 +406,11 @@ void grpc_rb_load_imports(HMODULE library) {
grpc_header_nonbin_value_is_legal_import = (grpc_header_nonbin_value_is_legal_type) GetProcAddress(library, "grpc_header_nonbin_value_is_legal");
grpc_is_binary_header_import = (grpc_is_binary_header_type) GetProcAddress(library, "grpc_is_binary_header");
grpc_call_error_to_string_import = (grpc_call_error_to_string_type) GetProcAddress(library, "grpc_call_error_to_string");
grpc_resource_quota_create_import = (grpc_resource_quota_create_type) GetProcAddress(library, "grpc_resource_quota_create");
grpc_resource_quota_ref_import = (grpc_resource_quota_ref_type) GetProcAddress(library, "grpc_resource_quota_ref");
grpc_resource_quota_unref_import = (grpc_resource_quota_unref_type) GetProcAddress(library, "grpc_resource_quota_unref");
grpc_resource_quota_resize_import = (grpc_resource_quota_resize_type) GetProcAddress(library, "grpc_resource_quota_resize");
grpc_resource_quota_arg_vtable_import = (grpc_resource_quota_arg_vtable_type) GetProcAddress(library, "grpc_resource_quota_arg_vtable");
grpc_insecure_channel_create_from_fd_import = (grpc_insecure_channel_create_from_fd_type) GetProcAddress(library, "grpc_insecure_channel_create_from_fd");
grpc_server_add_insecure_channel_from_fd_import = (grpc_server_add_insecure_channel_from_fd_type) GetProcAddress(library, "grpc_server_add_insecure_channel_from_fd");
grpc_use_signal_import = (grpc_use_signal_type) GetProcAddress(library, "grpc_use_signal");

@ -347,6 +347,21 @@ extern grpc_is_binary_header_type grpc_is_binary_header_import;
typedef const char *(*grpc_call_error_to_string_type)(grpc_call_error error);
extern grpc_call_error_to_string_type grpc_call_error_to_string_import;
#define grpc_call_error_to_string grpc_call_error_to_string_import
typedef grpc_resource_quota *(*grpc_resource_quota_create_type)(const char *trace_name);
extern grpc_resource_quota_create_type grpc_resource_quota_create_import;
#define grpc_resource_quota_create grpc_resource_quota_create_import
typedef void(*grpc_resource_quota_ref_type)(grpc_resource_quota *resource_quota);
extern grpc_resource_quota_ref_type grpc_resource_quota_ref_import;
#define grpc_resource_quota_ref grpc_resource_quota_ref_import
typedef void(*grpc_resource_quota_unref_type)(grpc_resource_quota *resource_quota);
extern grpc_resource_quota_unref_type grpc_resource_quota_unref_import;
#define grpc_resource_quota_unref grpc_resource_quota_unref_import
typedef void(*grpc_resource_quota_resize_type)(grpc_resource_quota *resource_quota, size_t new_size);
extern grpc_resource_quota_resize_type grpc_resource_quota_resize_import;
#define grpc_resource_quota_resize grpc_resource_quota_resize_import
typedef const grpc_arg_pointer_vtable *(*grpc_resource_quota_arg_vtable_type)(void);
extern grpc_resource_quota_arg_vtable_type grpc_resource_quota_arg_vtable_import;
#define grpc_resource_quota_arg_vtable grpc_resource_quota_arg_vtable_import
typedef grpc_channel *(*grpc_insecure_channel_create_from_fd_type)(const char *target, int fd, const grpc_channel_args *args);
extern grpc_insecure_channel_create_from_fd_type grpc_insecure_channel_create_from_fd_import;
#define grpc_insecure_channel_create_from_fd grpc_insecure_channel_create_from_fd_import

@ -114,7 +114,10 @@ void grpc_run_bad_client_test(
grpc_init();
/* Create endpoints */
sfd = grpc_iomgr_create_endpoint_pair("fixture", 65536);
grpc_resource_quota *resource_quota =
grpc_resource_quota_create("bad_client_test");
sfd = grpc_iomgr_create_endpoint_pair("fixture", resource_quota, 65536);
grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
/* Create server, completion events */
a.server = grpc_server_create(NULL, NULL);

@ -111,6 +111,8 @@ extern void request_with_flags(grpc_end2end_test_config config);
extern void request_with_flags_pre_init(void);
extern void request_with_payload(grpc_end2end_test_config config);
extern void request_with_payload_pre_init(void);
extern void resource_quota_server(grpc_end2end_test_config config);
extern void resource_quota_server_pre_init(void);
extern void server_finishes_request(grpc_end2end_test_config config);
extern void server_finishes_request_pre_init(void);
extern void shutdown_finishes_calls(grpc_end2end_test_config config);
@ -167,6 +169,7 @@ void grpc_end2end_tests_pre_init(void) {
registered_call_pre_init();
request_with_flags_pre_init();
request_with_payload_pre_init();
resource_quota_server_pre_init();
server_finishes_request_pre_init();
shutdown_finishes_calls_pre_init();
shutdown_finishes_tags_pre_init();
@ -219,6 +222,7 @@ void grpc_end2end_tests(int argc, char **argv,
registered_call(config);
request_with_flags(config);
request_with_payload(config);
resource_quota_server(config);
server_finishes_request(config);
shutdown_finishes_calls(config);
shutdown_finishes_tags(config);
@ -368,6 +372,10 @@ void grpc_end2end_tests(int argc, char **argv,
request_with_payload(config);
continue;
}
if (0 == strcmp("resource_quota_server", argv[i])) {
resource_quota_server(config);
continue;
}
if (0 == strcmp("server_finishes_request", argv[i])) {
server_finishes_request(config);
continue;

@ -113,6 +113,8 @@ extern void request_with_flags(grpc_end2end_test_config config);
extern void request_with_flags_pre_init(void);
extern void request_with_payload(grpc_end2end_test_config config);
extern void request_with_payload_pre_init(void);
extern void resource_quota_server(grpc_end2end_test_config config);
extern void resource_quota_server_pre_init(void);
extern void server_finishes_request(grpc_end2end_test_config config);
extern void server_finishes_request_pre_init(void);
extern void shutdown_finishes_calls(grpc_end2end_test_config config);
@ -170,6 +172,7 @@ void grpc_end2end_tests_pre_init(void) {
registered_call_pre_init();
request_with_flags_pre_init();
request_with_payload_pre_init();
resource_quota_server_pre_init();
server_finishes_request_pre_init();
shutdown_finishes_calls_pre_init();
shutdown_finishes_tags_pre_init();
@ -223,6 +226,7 @@ void grpc_end2end_tests(int argc, char **argv,
registered_call(config);
request_with_flags(config);
request_with_payload(config);
resource_quota_server(config);
server_finishes_request(config);
shutdown_finishes_calls(config);
shutdown_finishes_tags(config);
@ -376,6 +380,10 @@ void grpc_end2end_tests(int argc, char **argv,
request_with_payload(config);
continue;
}
if (0 == strcmp("resource_quota_server", argv[i])) {
resource_quota_server(config);
continue;
}
if (0 == strcmp("server_finishes_request", argv[i])) {
server_finishes_request(config);
continue;

@ -96,7 +96,9 @@ static grpc_end2end_test_fixture chttp2_create_fixture_socketpair(
f.fixture_data = sfd;
f.cq = grpc_completion_queue_create(NULL);
*sfd = grpc_iomgr_create_endpoint_pair("fixture", 65536);
grpc_resource_quota *resource_quota = grpc_resource_quota_create("fixture");
*sfd = grpc_iomgr_create_endpoint_pair("fixture", resource_quota, 65536);
grpc_resource_quota_unref(resource_quota);
return f;
}

@ -90,7 +90,9 @@ static grpc_end2end_test_fixture chttp2_create_fixture_socketpair(
f.fixture_data = sfd;
f.cq = grpc_completion_queue_create(NULL);
*sfd = grpc_iomgr_create_endpoint_pair("fixture", 65536);
grpc_resource_quota *resource_quota = grpc_resource_quota_create("fixture");
*sfd = grpc_iomgr_create_endpoint_pair("fixture", resource_quota, 65536);
grpc_resource_quota_unref(resource_quota);
return f;
}

@ -90,7 +90,9 @@ static grpc_end2end_test_fixture chttp2_create_fixture_socketpair(
f.fixture_data = sfd;
f.cq = grpc_completion_queue_create(NULL);
*sfd = grpc_iomgr_create_endpoint_pair("fixture", 1);
grpc_resource_quota *resource_quota = grpc_resource_quota_create("fixture");
*sfd = grpc_iomgr_create_endpoint_pair("fixture", resource_quota, 1);
grpc_resource_quota_unref(resource_quota);
return f;
}

@ -359,7 +359,7 @@ static void on_read_request_done(grpc_exec_ctx* exec_ctx, void* arg,
const gpr_timespec deadline = gpr_time_add(
gpr_now(GPR_CLOCK_MONOTONIC), gpr_time_from_seconds(10, GPR_TIMESPAN));
grpc_tcp_client_connect(exec_ctx, &conn->on_server_connect_done,
&conn->server_endpoint, conn->pollset_set,
&conn->server_endpoint, conn->pollset_set, NULL,
&resolved_addresses->addrs[0], deadline);
grpc_resolved_addresses_destroy(resolved_addresses);
}
@ -418,7 +418,8 @@ static void thread_main(void* arg) {
grpc_exec_ctx_finish(&exec_ctx);
}
grpc_end2end_http_proxy* grpc_end2end_http_proxy_create() {
grpc_end2end_http_proxy* grpc_end2end_http_proxy_create(void) {
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_end2end_http_proxy* proxy = gpr_malloc(sizeof(*proxy));
memset(proxy, 0, sizeof(*proxy));
// Construct proxy address.
@ -427,8 +428,8 @@ grpc_end2end_http_proxy* grpc_end2end_http_proxy_create() {
gpr_log(GPR_INFO, "Proxy address: %s", proxy->proxy_name);
// Create TCP server.
proxy->channel_args = grpc_channel_args_copy(NULL);
grpc_error* error =
grpc_tcp_server_create(NULL, proxy->channel_args, &proxy->server);
grpc_error* error = grpc_tcp_server_create(
&exec_ctx, NULL, proxy->channel_args, &proxy->server);
GPR_ASSERT(error == GRPC_ERROR_NONE);
// Bind to port.
grpc_resolved_address resolved_addr;
@ -443,7 +444,6 @@ grpc_end2end_http_proxy* grpc_end2end_http_proxy_create() {
// Start server.
proxy->pollset = gpr_malloc(grpc_pollset_size());
grpc_pollset_init(proxy->pollset, &proxy->mu);
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_tcp_server_start(&exec_ctx, proxy->server, &proxy->pollset, 1, on_accept,
proxy);
grpc_exec_ctx_finish(&exec_ctx);

@ -173,6 +173,7 @@ static bool is_eof(input_stream *inp) { return inp->cur == inp->end; }
static gpr_timespec g_now;
static grpc_server *g_server;
static grpc_channel *g_channel;
static grpc_resource_quota *g_resource_quota;
extern gpr_timespec (*gpr_now_impl)(gpr_clock_type clock_type);
@ -231,8 +232,8 @@ void my_resolve_address(grpc_exec_ctx *exec_ctx, const char *addr,
// defined in tcp_client_posix.c
extern void (*grpc_tcp_client_connect_impl)(
grpc_exec_ctx *exec_ctx, grpc_closure *closure, grpc_endpoint **ep,
grpc_pollset_set *interested_parties, const grpc_resolved_address *addr,
gpr_timespec deadline);
grpc_pollset_set *interested_parties, const grpc_channel_args *channel_args,
const grpc_resolved_address *addr, gpr_timespec deadline);
static void sched_connect(grpc_exec_ctx *exec_ctx, grpc_closure *closure,
grpc_endpoint **ep, gpr_timespec deadline);
@ -252,7 +253,7 @@ static void do_connect(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
} else if (g_server != NULL) {
grpc_endpoint *client;
grpc_endpoint *server;
grpc_passthru_endpoint_create(&client, &server);
grpc_passthru_endpoint_create(&client, &server, g_resource_quota);
*fc->ep = client;
grpc_transport *transport =
@ -289,6 +290,7 @@ static void sched_connect(grpc_exec_ctx *exec_ctx, grpc_closure *closure,
static void my_tcp_client_connect(grpc_exec_ctx *exec_ctx,
grpc_closure *closure, grpc_endpoint **ep,
grpc_pollset_set *interested_parties,
const grpc_channel_args *channel_args,
const grpc_resolved_address *addr,
gpr_timespec deadline) {
sched_connect(exec_ctx, closure, ep, deadline);
@ -520,6 +522,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
int pending_pings = 0;
g_active_call = new_call(NULL, ROOT);
g_resource_quota = grpc_resource_quota_create("api_fuzzer");
grpc_completion_queue *cq = grpc_completion_queue_create(NULL);
@ -939,6 +942,11 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
}
break;
}
// resize the buffer pool
case 21: {
grpc_resource_quota_resize(g_resource_quota, read_uint22(&inp));
break;
}
}
}
@ -954,6 +962,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
.type == GRPC_QUEUE_SHUTDOWN);
grpc_completion_queue_destroy(cq);
grpc_resource_quota_unref(g_resource_quota);
grpc_shutdown();
return 0;
}

@ -58,7 +58,11 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
grpc_init();
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_endpoint *mock_endpoint = grpc_mock_endpoint_create(discard_write);
grpc_resource_quota *resource_quota =
grpc_resource_quota_create("client_fuzzer");
grpc_endpoint *mock_endpoint =
grpc_mock_endpoint_create(discard_write, resource_quota);
grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
grpc_completion_queue *cq = grpc_completion_queue_create(NULL);
grpc_transport *transport =

@ -56,7 +56,11 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
grpc_init();
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_endpoint *mock_endpoint = grpc_mock_endpoint_create(discard_write);
grpc_resource_quota *resource_quota =
grpc_resource_quota_create("server_fuzzer");
grpc_endpoint *mock_endpoint =
grpc_mock_endpoint_create(discard_write, resource_quota);
grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
grpc_mock_endpoint_put_read(
&exec_ctx, mock_endpoint,
gpr_slice_from_copied_buffer((const char *)data, size));

@ -39,9 +39,9 @@ import hashlib
FixtureOptions = collections.namedtuple(
'FixtureOptions',
'fullstack includes_proxy dns_resolver secure platforms ci_mac tracing exclude_configs exclude_iomgrs')
'fullstack includes_proxy dns_resolver secure platforms ci_mac tracing exclude_configs exclude_iomgrs large_writes')
default_unsecure_fixture_options = FixtureOptions(
True, False, True, False, ['windows', 'linux', 'mac', 'posix'], True, False, [], [])
True, False, True, False, ['windows', 'linux', 'mac', 'posix'], True, False, [], [], True)
socketpair_unsecure_fixture_options = default_unsecure_fixture_options._replace(fullstack=False, dns_resolver=False)
default_secure_fixture_options = default_unsecure_fixture_options._replace(secure=True)
uds_fixture_options = default_unsecure_fixture_options._replace(dns_resolver=False, platforms=['linux', 'mac', 'posix'], exclude_iomgrs=['uv'])
@ -69,11 +69,12 @@ END2END_FIXTURES = {
'h2_proxy': default_unsecure_fixture_options._replace(
includes_proxy=True, ci_mac=False, exclude_iomgrs=['uv']),
'h2_sockpair_1byte': socketpair_unsecure_fixture_options._replace(
ci_mac=False, exclude_configs=['msan'], exclude_iomgrs=['uv']),
ci_mac=False, exclude_configs=['msan'], large_writes=False,
exclude_iomgrs=['uv']),
'h2_sockpair': socketpair_unsecure_fixture_options._replace(
ci_mac=False, exclude_iomgrs=['uv']),
'h2_sockpair+trace': socketpair_unsecure_fixture_options._replace(
ci_mac=False, tracing=True, exclude_iomgrs=['uv']),
ci_mac=False, tracing=True, large_writes=False, exclude_iomgrs=['uv']),
'h2_ssl': default_secure_fixture_options,
'h2_ssl_cert': default_secure_fixture_options,
'h2_ssl_proxy': default_secure_fixture_options._replace(
@ -83,8 +84,8 @@ END2END_FIXTURES = {
TestOptions = collections.namedtuple(
'TestOptions',
'needs_fullstack needs_dns proxyable secure traceable cpu_cost exclude_iomgrs')
default_test_options = TestOptions(False, False, True, False, True, 1.0, [])
'needs_fullstack needs_dns proxyable secure traceable cpu_cost exclude_iomgrs large_writes flaky')
default_test_options = TestOptions(False, False, True, False, True, 1.0, [], False, False)
connectivity_test_options = default_test_options._replace(needs_fullstack=True)
LOWCPU = 0.1
@ -93,6 +94,8 @@ LOWCPU = 0.1
END2END_TESTS = {
'bad_hostname': default_test_options,
'binary_metadata': default_test_options,
'resource_quota_server': default_test_options._replace(large_writes=True,
proxyable=False),
'call_creds': default_test_options._replace(secure=True),
'cancel_after_accept': default_test_options._replace(cpu_cost=LOWCPU),
'cancel_after_client_done': default_test_options,
@ -105,7 +108,7 @@ END2END_TESTS = {
proxyable=False, cpu_cost=LOWCPU, exclude_iomgrs=['uv']),
'default_host': default_test_options._replace(needs_fullstack=True,
needs_dns=True),
'disappearing_server': connectivity_test_options,
'disappearing_server': connectivity_test_options._replace(flaky=True),
'empty_batch': default_test_options,
'filter_causes_close': default_test_options,
'filter_call_init_fails': default_test_options,
@ -155,6 +158,9 @@ def compatible(f, t):
if not END2END_TESTS[t].traceable:
if END2END_FIXTURES[f].tracing:
return False
if END2END_TESTS[t].large_writes:
if not END2END_FIXTURES[f].large_writes:
return False
return True
@ -257,7 +263,7 @@ def main():
'ci_platforms': (END2END_FIXTURES[f].platforms
if END2END_FIXTURES[f].ci_mac else without(
END2END_FIXTURES[f].platforms, 'mac')),
'flaky': False,
'flaky': END2END_TESTS[t].flaky,
'language': 'c',
'cpu_cost': END2END_TESTS[t].cpu_cost,
}
@ -274,7 +280,7 @@ def main():
'ci_platforms': (END2END_FIXTURES[f].platforms
if END2END_FIXTURES[f].ci_mac else without(
END2END_FIXTURES[f].platforms, 'mac')),
'flaky': False,
'flaky': END2END_TESTS[t].flaky,
'language': 'c',
'cpu_cost': END2END_TESTS[t].cpu_cost,
}

@ -402,7 +402,6 @@ static void test_max_message_length_on_response(grpc_end2end_test_config config,
GPR_ASSERT(0 == strcmp(call_details.method, "/service/method"));
GPR_ASSERT(0 == strcmp(call_details.host, "foo.test.google.fr:1234"));
GPR_ASSERT(was_cancelled == 0);
GPR_ASSERT(status == GRPC_STATUS_INVALID_ARGUMENT);
GPR_ASSERT(strcmp(details,

@ -213,7 +213,6 @@ static void test_invoke_network_status_change(grpc_end2end_test_config config) {
GPR_ASSERT(status == GRPC_STATUS_UNAVAILABLE);
GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
GPR_ASSERT(0 == strcmp(call_details.host, "foo.test.google.fr"));
GPR_ASSERT(was_cancelled == 0);
gpr_free(details);
grpc_metadata_array_destroy(&initial_metadata_recv);

@ -0,0 +1,359 @@
/*
*
* Copyright 2016, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "test/core/end2end/end2end_tests.h"
#include <stdio.h>
#include <string.h>
#include <grpc/byte_buffer.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/time.h>
#include <grpc/support/useful.h>
#include "test/core/end2end/cq_verifier.h"
static void *tag(intptr_t t) { return (void *)t; }
static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
const char *test_name,
grpc_channel_args *client_args,
grpc_channel_args *server_args) {
grpc_end2end_test_fixture f;
gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
f = config.create_fixture(client_args, server_args);
config.init_server(&f, server_args);
config.init_client(&f, client_args, NULL);
return f;
}
static gpr_timespec n_seconds_time(int n) {
return GRPC_TIMEOUT_SECONDS_TO_DEADLINE(n);
}
static gpr_timespec five_seconds_time(void) { return n_seconds_time(5); }
static void drain_cq(grpc_completion_queue *cq) {
grpc_event ev;
do {
ev = grpc_completion_queue_next(cq, five_seconds_time(), NULL);
} while (ev.type != GRPC_QUEUE_SHUTDOWN);
}
static void shutdown_server(grpc_end2end_test_fixture *f) {
if (!f->server) return;
grpc_server_shutdown_and_notify(f->server, f->cq, tag(1000));
GPR_ASSERT(grpc_completion_queue_pluck(
f->cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5), NULL)
.type == GRPC_OP_COMPLETE);
grpc_server_destroy(f->server);
f->server = NULL;
}
static void shutdown_client(grpc_end2end_test_fixture *f) {
if (!f->client) return;
grpc_channel_destroy(f->client);
f->client = NULL;
}
static void end_test(grpc_end2end_test_fixture *f) {
shutdown_server(f);
shutdown_client(f);
grpc_completion_queue_shutdown(f->cq);
drain_cq(f->cq);
grpc_completion_queue_destroy(f->cq);
}
/* Creates and returns a gpr_slice containing random alphanumeric characters. */
static gpr_slice generate_random_slice() {
size_t i;
static const char chars[] = "abcdefghijklmnopqrstuvwxyz1234567890";
char output[1024 * 1024];
for (i = 0; i < GPR_ARRAY_SIZE(output) - 1; ++i) {
output[i] = chars[rand() % (int)(sizeof(chars) - 1)];
}
output[GPR_ARRAY_SIZE(output) - 1] = '\0';
return gpr_slice_from_copied_string(output);
}
void resource_quota_server(grpc_end2end_test_config config) {
grpc_resource_quota *resource_quota =
grpc_resource_quota_create("test_server");
grpc_resource_quota_resize(resource_quota, 5 * 1024 * 1024);
#define NUM_CALLS 100
#define CLIENT_BASE_TAG 1000
#define SERVER_START_BASE_TAG 2000
#define SERVER_RECV_BASE_TAG 3000
#define SERVER_END_BASE_TAG 4000
grpc_arg arg;
arg.key = GRPC_ARG_RESOURCE_QUOTA;
arg.type = GRPC_ARG_POINTER;
arg.value.pointer.p = resource_quota;
arg.value.pointer.vtable = grpc_resource_quota_arg_vtable();
grpc_channel_args args = {1, &arg};
grpc_end2end_test_fixture f =
begin_test(config, "resource_quota_server", NULL, &args);
/* Create large request and response bodies. These are big enough to require
* multiple round trips to deliver to the peer, and their exact contents of
* will be verified on completion. */
gpr_slice request_payload_slice = generate_random_slice();
grpc_call *client_calls[NUM_CALLS];
grpc_call *server_calls[NUM_CALLS];
grpc_metadata_array initial_metadata_recv[NUM_CALLS];
grpc_metadata_array trailing_metadata_recv[NUM_CALLS];
grpc_metadata_array request_metadata_recv[NUM_CALLS];
grpc_call_details call_details[NUM_CALLS];
grpc_status_code status[NUM_CALLS];
char *details[NUM_CALLS];
size_t details_capacity[NUM_CALLS];
grpc_byte_buffer *request_payload_recv[NUM_CALLS];
int was_cancelled[NUM_CALLS];
grpc_call_error error;
int pending_client_calls = 0;
int pending_server_start_calls = 0;
int pending_server_recv_calls = 0;
int pending_server_end_calls = 0;
int cancelled_calls_on_client = 0;
int cancelled_calls_on_server = 0;
grpc_byte_buffer *request_payload =
grpc_raw_byte_buffer_create(&request_payload_slice, 1);
grpc_op ops[6];
grpc_op *op;
for (int i = 0; i < NUM_CALLS; i++) {
grpc_metadata_array_init(&initial_metadata_recv[i]);
grpc_metadata_array_init(&trailing_metadata_recv[i]);
grpc_metadata_array_init(&request_metadata_recv[i]);
grpc_call_details_init(&call_details[i]);
details[i] = NULL;
details_capacity[i] = 0;
request_payload_recv[i] = NULL;
was_cancelled[i] = 0;
}
for (int i = 0; i < NUM_CALLS; i++) {
error = grpc_server_request_call(
f.server, &server_calls[i], &call_details[i], &request_metadata_recv[i],
f.cq, f.cq, tag(SERVER_START_BASE_TAG + i));
GPR_ASSERT(GRPC_CALL_OK == error);
pending_server_start_calls++;
}
for (int i = 0; i < NUM_CALLS; i++) {
client_calls[i] = grpc_channel_create_call(
f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
"foo.test.google.fr", n_seconds_time(60), NULL);
memset(ops, 0, sizeof(ops));
op = ops;
op->op = GRPC_OP_SEND_INITIAL_METADATA;
op->data.send_initial_metadata.count = 0;
op->flags = 0;
op->reserved = NULL;
op++;
op->op = GRPC_OP_SEND_MESSAGE;
op->data.send_message = request_payload;
op->flags = 0;
op->reserved = NULL;
op++;
op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
op->flags = 0;
op->reserved = NULL;
op++;
op->op = GRPC_OP_RECV_INITIAL_METADATA;
op->data.recv_initial_metadata = &initial_metadata_recv[i];
op->flags = 0;
op->reserved = NULL;
op++;
op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
op->data.recv_status_on_client.trailing_metadata =
&trailing_metadata_recv[i];
op->data.recv_status_on_client.status = &status[i];
op->data.recv_status_on_client.status_details = &details[i];
op->data.recv_status_on_client.status_details_capacity =
&details_capacity[i];
op->flags = 0;
op->reserved = NULL;
op++;
error = grpc_call_start_batch(client_calls[i], ops, (size_t)(op - ops),
tag(CLIENT_BASE_TAG + i), NULL);
GPR_ASSERT(GRPC_CALL_OK == error);
pending_client_calls++;
}
while (pending_client_calls + pending_server_recv_calls +
pending_server_end_calls >
0) {
grpc_event ev = grpc_completion_queue_next(f.cq, n_seconds_time(10), NULL);
GPR_ASSERT(ev.type == GRPC_OP_COMPLETE);
int ev_tag = (int)(intptr_t)ev.tag;
if (ev_tag < CLIENT_BASE_TAG) {
abort(); /* illegal tag */
} else if (ev_tag < SERVER_START_BASE_TAG) {
/* client call finished */
int call_id = ev_tag - CLIENT_BASE_TAG;
GPR_ASSERT(call_id >= 0);
GPR_ASSERT(call_id < NUM_CALLS);
switch (status[call_id]) {
case GRPC_STATUS_RESOURCE_EXHAUSTED:
cancelled_calls_on_client++;
break;
case GRPC_STATUS_OK:
break;
default:
gpr_log(GPR_ERROR, "Unexpected status code: %d", status[call_id]);
abort();
}
GPR_ASSERT(pending_client_calls > 0);
grpc_metadata_array_destroy(&initial_metadata_recv[call_id]);
grpc_metadata_array_destroy(&trailing_metadata_recv[call_id]);
grpc_call_destroy(client_calls[call_id]);
gpr_free(details[call_id]);
pending_client_calls--;
} else if (ev_tag < SERVER_RECV_BASE_TAG) {
/* new incoming call to the server */
int call_id = ev_tag - SERVER_START_BASE_TAG;
GPR_ASSERT(call_id >= 0);
GPR_ASSERT(call_id < NUM_CALLS);
memset(ops, 0, sizeof(ops));
op = ops;
op->op = GRPC_OP_SEND_INITIAL_METADATA;
op->data.send_initial_metadata.count = 0;
op->flags = 0;
op->reserved = NULL;
op++;
op->op = GRPC_OP_RECV_MESSAGE;
op->data.recv_message = &request_payload_recv[call_id];
op->flags = 0;
op->reserved = NULL;
op++;
error =
grpc_call_start_batch(server_calls[call_id], ops, (size_t)(op - ops),
tag(SERVER_RECV_BASE_TAG + call_id), NULL);
GPR_ASSERT(GRPC_CALL_OK == error);
GPR_ASSERT(pending_server_start_calls > 0);
pending_server_start_calls--;
pending_server_recv_calls++;
grpc_call_details_destroy(&call_details[call_id]);
grpc_metadata_array_destroy(&request_metadata_recv[call_id]);
} else if (ev_tag < SERVER_END_BASE_TAG) {
/* finished read on the server */
int call_id = ev_tag - SERVER_RECV_BASE_TAG;
GPR_ASSERT(call_id >= 0);
GPR_ASSERT(call_id < NUM_CALLS);
if (ev.success) {
if (request_payload_recv[call_id] != NULL) {
grpc_byte_buffer_destroy(request_payload_recv[call_id]);
request_payload_recv[call_id] = NULL;
}
} else {
GPR_ASSERT(request_payload_recv[call_id] == NULL);
}
memset(ops, 0, sizeof(ops));
op = ops;
op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
op->data.recv_close_on_server.cancelled = &was_cancelled[call_id];
op->flags = 0;
op->reserved = NULL;
op++;
op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
op->data.send_status_from_server.trailing_metadata_count = 0;
op->data.send_status_from_server.status = GRPC_STATUS_OK;
op->data.send_status_from_server.status_details = "xyz";
op->flags = 0;
op->reserved = NULL;
op++;
error =
grpc_call_start_batch(server_calls[call_id], ops, (size_t)(op - ops),
tag(SERVER_END_BASE_TAG + call_id), NULL);
GPR_ASSERT(GRPC_CALL_OK == error);
GPR_ASSERT(pending_server_recv_calls > 0);
pending_server_recv_calls--;
pending_server_end_calls++;
} else {
int call_id = ev_tag - SERVER_END_BASE_TAG;
GPR_ASSERT(call_id >= 0);
GPR_ASSERT(call_id < NUM_CALLS);
if (was_cancelled[call_id]) {
cancelled_calls_on_server++;
}
GPR_ASSERT(pending_server_end_calls > 0);
pending_server_end_calls--;
grpc_call_destroy(server_calls[call_id]);
}
}
gpr_log(
GPR_INFO,
"Done. %d total calls: %d cancelled at server, %d cancelled at client.",
NUM_CALLS, cancelled_calls_on_server, cancelled_calls_on_client);
/* The call may be cancelled after the server has sent its status but before
* the client has received it. This means that we should see strictly more
* failures on the client than on the server. */
GPR_ASSERT(cancelled_calls_on_client >= cancelled_calls_on_server);
/* However, we shouldn't see radically more... 0.9 is a guessed bound on what
* we'd want that ratio to be... to at least trigger some investigation should
* that ratio become much higher. */
GPR_ASSERT(cancelled_calls_on_server >= 0.9 * cancelled_calls_on_client);
grpc_byte_buffer_destroy(request_payload);
gpr_slice_unref(request_payload_slice);
grpc_resource_quota_unref(resource_quota);
end_test(&f);
config.tear_down_data(&f);
}
void resource_quota_server_pre_init(void) {}

@ -89,8 +89,11 @@ static void test_get(int port) {
grpc_http_response response;
memset(&response, 0, sizeof(response));
grpc_httpcli_get(&exec_ctx, &g_context, &g_pops, &req, n_seconds_time(15),
grpc_resource_quota *resource_quota = grpc_resource_quota_create("test_get");
grpc_httpcli_get(&exec_ctx, &g_context, &g_pops, resource_quota, &req,
n_seconds_time(15),
grpc_closure_create(on_finish, &response), &response);
grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
gpr_mu_lock(g_mu);
while (!g_done) {
grpc_pollset_worker *worker = NULL;
@ -126,9 +129,11 @@ static void test_post(int port) {
grpc_http_response response;
memset(&response, 0, sizeof(response));
grpc_httpcli_post(&exec_ctx, &g_context, &g_pops, &req, "hello", 5,
n_seconds_time(15),
grpc_resource_quota *resource_quota = grpc_resource_quota_create("test_post");
grpc_httpcli_post(&exec_ctx, &g_context, &g_pops, resource_quota, &req,
"hello", 5, n_seconds_time(15),
grpc_closure_create(on_finish, &response), &response);
grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
gpr_mu_lock(g_mu);
while (!g_done) {
grpc_pollset_worker *worker = NULL;

@ -90,8 +90,11 @@ static void test_get(int port) {
grpc_http_response response;
memset(&response, 0, sizeof(response));
grpc_httpcli_get(&exec_ctx, &g_context, &g_pops, &req, n_seconds_time(15),
grpc_resource_quota *resource_quota = grpc_resource_quota_create("test_get");
grpc_httpcli_get(&exec_ctx, &g_context, &g_pops, resource_quota, &req,
n_seconds_time(15),
grpc_closure_create(on_finish, &response), &response);
grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
gpr_mu_lock(g_mu);
while (!g_done) {
grpc_pollset_worker *worker = NULL;
@ -128,9 +131,11 @@ static void test_post(int port) {
grpc_http_response response;
memset(&response, 0, sizeof(response));
grpc_httpcli_post(&exec_ctx, &g_context, &g_pops, &req, "hello", 5,
n_seconds_time(15),
grpc_resource_quota *resource_quota = grpc_resource_quota_create("test_post");
grpc_httpcli_post(&exec_ctx, &g_context, &g_pops, resource_quota, &req,
"hello", 5, n_seconds_time(15),
grpc_closure_create(on_finish, &response), &response);
grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
gpr_mu_lock(g_mu);
while (!g_done) {
grpc_pollset_worker *worker = NULL;

@ -84,6 +84,7 @@ static void test_code(void) {
grpc_endpoint_add_to_pollset_set,
grpc_endpoint_shutdown,
grpc_endpoint_destroy,
grpc_endpoint_get_resource_user,
grpc_endpoint_get_peer};
endpoint.vtable = &vtable;

@ -49,7 +49,11 @@ static grpc_endpoint_test_fixture create_fixture_endpoint_pair(
size_t slice_size) {
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_endpoint_test_fixture f;
grpc_endpoint_pair p = grpc_iomgr_create_endpoint_pair("test", slice_size);
grpc_resource_quota *resource_quota =
grpc_resource_quota_create("endpoint_pair_test");
grpc_endpoint_pair p =
grpc_iomgr_create_endpoint_pair("test", resource_quota, slice_size);
grpc_resource_quota_unref(resource_quota);
f.client_ep = p.client;
f.server_ep = p.server;

@ -52,15 +52,19 @@ int main(int argc, char **argv) {
of descriptors */
rlim.rlim_cur = rlim.rlim_max = 10;
GPR_ASSERT(0 == setrlimit(RLIMIT_NOFILE, &rlim));
grpc_resource_quota *resource_quota =
grpc_resource_quota_create("fd_conservation_posix_test");
for (i = 0; i < 100; i++) {
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
p = grpc_iomgr_create_endpoint_pair("test", 1);
p = grpc_iomgr_create_endpoint_pair("test", resource_quota, 1);
grpc_endpoint_destroy(&exec_ctx, p.client);
grpc_endpoint_destroy(&exec_ctx, p.server);
grpc_exec_ctx_finish(&exec_ctx);
}
grpc_resource_quota_unref(resource_quota);
grpc_iomgr_shutdown();
return 0;
}

@ -0,0 +1,749 @@
/*
*
* Copyright 2016, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "src/core/lib/iomgr/resource_quota.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include "test/core/util/test_config.h"
static void inc_int_cb(grpc_exec_ctx *exec_ctx, void *a, grpc_error *error) {
++*(int *)a;
}
static void set_bool_cb(grpc_exec_ctx *exec_ctx, void *a, grpc_error *error) {
*(bool *)a = true;
}
grpc_closure *set_bool(bool *p) { return grpc_closure_create(set_bool_cb, p); }
typedef struct {
size_t size;
grpc_resource_user *resource_user;
grpc_closure *then;
} reclaimer_args;
static void reclaimer_cb(grpc_exec_ctx *exec_ctx, void *args,
grpc_error *error) {
GPR_ASSERT(error == GRPC_ERROR_NONE);
reclaimer_args *a = args;
grpc_resource_user_free(exec_ctx, a->resource_user, a->size);
grpc_resource_user_finish_reclamation(exec_ctx, a->resource_user);
grpc_closure_run(exec_ctx, a->then, GRPC_ERROR_NONE);
gpr_free(a);
}
grpc_closure *make_reclaimer(grpc_resource_user *resource_user, size_t size,
grpc_closure *then) {
reclaimer_args *a = gpr_malloc(sizeof(*a));
a->size = size;
a->resource_user = resource_user;
a->then = then;
return grpc_closure_create(reclaimer_cb, a);
}
static void unused_reclaimer_cb(grpc_exec_ctx *exec_ctx, void *arg,
grpc_error *error) {
GPR_ASSERT(error == GRPC_ERROR_CANCELLED);
grpc_closure_run(exec_ctx, arg, GRPC_ERROR_NONE);
}
grpc_closure *make_unused_reclaimer(grpc_closure *then) {
return grpc_closure_create(unused_reclaimer_cb, then);
}
static void destroy_user(grpc_resource_user *usr) {
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
bool done = false;
grpc_resource_user_shutdown(&exec_ctx, usr, set_bool(&done));
grpc_exec_ctx_flush(&exec_ctx);
GPR_ASSERT(done);
grpc_resource_user_destroy(&exec_ctx, usr);
grpc_exec_ctx_finish(&exec_ctx);
}
static void test_no_op(void) {
gpr_log(GPR_INFO, "** test_no_op **");
grpc_resource_quota_unref(grpc_resource_quota_create("test_no_op"));
}
static void test_resize_then_destroy(void) {
gpr_log(GPR_INFO, "** test_resize_then_destroy **");
grpc_resource_quota *q =
grpc_resource_quota_create("test_resize_then_destroy");
grpc_resource_quota_resize(q, 1024 * 1024);
grpc_resource_quota_unref(q);
}
static void test_resource_user_no_op(void) {
gpr_log(GPR_INFO, "** test_resource_user_no_op **");
grpc_resource_quota *q =
grpc_resource_quota_create("test_resource_user_no_op");
grpc_resource_user usr;
grpc_resource_user_init(&usr, q, "usr");
grpc_resource_quota_unref(q);
destroy_user(&usr);
}
static void test_instant_alloc_then_free(void) {
gpr_log(GPR_INFO, "** test_instant_alloc_then_free **");
grpc_resource_quota *q =
grpc_resource_quota_create("test_instant_alloc_then_free");
grpc_resource_quota_resize(q, 1024 * 1024);
grpc_resource_user usr;
grpc_resource_user_init(&usr, q, "usr");
{
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_alloc(&exec_ctx, &usr, 1024, NULL);
grpc_exec_ctx_finish(&exec_ctx);
}
{
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_free(&exec_ctx, &usr, 1024);
grpc_exec_ctx_finish(&exec_ctx);
}
grpc_resource_quota_unref(q);
destroy_user(&usr);
}
static void test_instant_alloc_free_pair(void) {
gpr_log(GPR_INFO, "** test_instant_alloc_free_pair **");
grpc_resource_quota *q =
grpc_resource_quota_create("test_instant_alloc_free_pair");
grpc_resource_quota_resize(q, 1024 * 1024);
grpc_resource_user usr;
grpc_resource_user_init(&usr, q, "usr");
{
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_alloc(&exec_ctx, &usr, 1024, NULL);
grpc_resource_user_free(&exec_ctx, &usr, 1024);
grpc_exec_ctx_finish(&exec_ctx);
}
grpc_resource_quota_unref(q);
destroy_user(&usr);
}
static void test_simple_async_alloc(void) {
gpr_log(GPR_INFO, "** test_simple_async_alloc **");
grpc_resource_quota *q =
grpc_resource_quota_create("test_simple_async_alloc");
grpc_resource_quota_resize(q, 1024 * 1024);
grpc_resource_user usr;
grpc_resource_user_init(&usr, q, "usr");
{
bool done = false;
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_alloc(&exec_ctx, &usr, 1024, set_bool(&done));
grpc_exec_ctx_finish(&exec_ctx);
GPR_ASSERT(done);
}
{
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_free(&exec_ctx, &usr, 1024);
grpc_exec_ctx_finish(&exec_ctx);
}
grpc_resource_quota_unref(q);
destroy_user(&usr);
}
static void test_async_alloc_blocked_by_size(void) {
gpr_log(GPR_INFO, "** test_async_alloc_blocked_by_size **");
grpc_resource_quota *q =
grpc_resource_quota_create("test_async_alloc_blocked_by_size");
grpc_resource_quota_resize(q, 1);
grpc_resource_user usr;
grpc_resource_user_init(&usr, q, "usr");
bool done = false;
{
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_alloc(&exec_ctx, &usr, 1024, set_bool(&done));
grpc_exec_ctx_finish(&exec_ctx);
GPR_ASSERT(!done);
}
grpc_resource_quota_resize(q, 1024);
GPR_ASSERT(done);
{
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_free(&exec_ctx, &usr, 1024);
grpc_exec_ctx_finish(&exec_ctx);
}
grpc_resource_quota_unref(q);
destroy_user(&usr);
}
static void test_scavenge(void) {
gpr_log(GPR_INFO, "** test_scavenge **");
grpc_resource_quota *q = grpc_resource_quota_create("test_scavenge");
grpc_resource_quota_resize(q, 1024);
grpc_resource_user usr1;
grpc_resource_user usr2;
grpc_resource_user_init(&usr1, q, "usr1");
grpc_resource_user_init(&usr2, q, "usr2");
{
bool done = false;
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_alloc(&exec_ctx, &usr1, 1024, set_bool(&done));
grpc_exec_ctx_finish(&exec_ctx);
GPR_ASSERT(done);
}
{
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_free(&exec_ctx, &usr1, 1024);
grpc_exec_ctx_finish(&exec_ctx);
}
{
bool done = false;
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_alloc(&exec_ctx, &usr2, 1024, set_bool(&done));
grpc_exec_ctx_finish(&exec_ctx);
GPR_ASSERT(done);
}
{
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_free(&exec_ctx, &usr2, 1024);
grpc_exec_ctx_finish(&exec_ctx);
}
grpc_resource_quota_unref(q);
destroy_user(&usr1);
destroy_user(&usr2);
}
static void test_scavenge_blocked(void) {
gpr_log(GPR_INFO, "** test_scavenge_blocked **");
grpc_resource_quota *q = grpc_resource_quota_create("test_scavenge_blocked");
grpc_resource_quota_resize(q, 1024);
grpc_resource_user usr1;
grpc_resource_user usr2;
grpc_resource_user_init(&usr1, q, "usr1");
grpc_resource_user_init(&usr2, q, "usr2");
bool done;
{
done = false;
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_alloc(&exec_ctx, &usr1, 1024, set_bool(&done));
grpc_exec_ctx_finish(&exec_ctx);
GPR_ASSERT(done);
}
{
done = false;
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_alloc(&exec_ctx, &usr2, 1024, set_bool(&done));
grpc_exec_ctx_finish(&exec_ctx);
GPR_ASSERT(!done);
}
{
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_free(&exec_ctx, &usr1, 1024);
grpc_exec_ctx_finish(&exec_ctx);
GPR_ASSERT(done);
}
{
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_free(&exec_ctx, &usr2, 1024);
grpc_exec_ctx_finish(&exec_ctx);
}
grpc_resource_quota_unref(q);
destroy_user(&usr1);
destroy_user(&usr2);
}
static void test_blocked_until_scheduled_reclaim(void) {
gpr_log(GPR_INFO, "** test_blocked_until_scheduled_reclaim **");
grpc_resource_quota *q =
grpc_resource_quota_create("test_blocked_until_scheduled_reclaim");
grpc_resource_quota_resize(q, 1024);
grpc_resource_user usr;
grpc_resource_user_init(&usr, q, "usr");
{
bool done = false;
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_alloc(&exec_ctx, &usr, 1024, set_bool(&done));
grpc_exec_ctx_finish(&exec_ctx);
GPR_ASSERT(done);
}
bool reclaim_done = false;
{
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_post_reclaimer(
&exec_ctx, &usr, false,
make_reclaimer(&usr, 1024, set_bool(&reclaim_done)));
grpc_exec_ctx_finish(&exec_ctx);
}
{
bool done = false;
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_alloc(&exec_ctx, &usr, 1024, set_bool(&done));
grpc_exec_ctx_finish(&exec_ctx);
GPR_ASSERT(reclaim_done);
GPR_ASSERT(done);
}
{
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_free(&exec_ctx, &usr, 1024);
grpc_exec_ctx_finish(&exec_ctx);
}
grpc_resource_quota_unref(q);
destroy_user(&usr);
}
static void test_blocked_until_scheduled_reclaim_and_scavenge(void) {
gpr_log(GPR_INFO, "** test_blocked_until_scheduled_reclaim_and_scavenge **");
grpc_resource_quota *q = grpc_resource_quota_create(
"test_blocked_until_scheduled_reclaim_and_scavenge");
grpc_resource_quota_resize(q, 1024);
grpc_resource_user usr1;
grpc_resource_user usr2;
grpc_resource_user_init(&usr1, q, "usr1");
grpc_resource_user_init(&usr2, q, "usr2");
{
bool done = false;
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_alloc(&exec_ctx, &usr1, 1024, set_bool(&done));
grpc_exec_ctx_finish(&exec_ctx);
GPR_ASSERT(done);
}
bool reclaim_done = false;
{
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_post_reclaimer(
&exec_ctx, &usr1, false,
make_reclaimer(&usr1, 1024, set_bool(&reclaim_done)));
grpc_exec_ctx_finish(&exec_ctx);
}
{
bool done = false;
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_alloc(&exec_ctx, &usr2, 1024, set_bool(&done));
grpc_exec_ctx_finish(&exec_ctx);
GPR_ASSERT(reclaim_done);
GPR_ASSERT(done);
}
{
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_free(&exec_ctx, &usr2, 1024);
grpc_exec_ctx_finish(&exec_ctx);
}
grpc_resource_quota_unref(q);
destroy_user(&usr1);
destroy_user(&usr2);
}
static void test_blocked_until_scheduled_destructive_reclaim(void) {
gpr_log(GPR_INFO, "** test_blocked_until_scheduled_destructive_reclaim **");
grpc_resource_quota *q = grpc_resource_quota_create(
"test_blocked_until_scheduled_destructive_reclaim");
grpc_resource_quota_resize(q, 1024);
grpc_resource_user usr;
grpc_resource_user_init(&usr, q, "usr");
{
bool done = false;
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_alloc(&exec_ctx, &usr, 1024, set_bool(&done));
grpc_exec_ctx_finish(&exec_ctx);
GPR_ASSERT(done);
}
bool reclaim_done = false;
{
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_post_reclaimer(
&exec_ctx, &usr, true,
make_reclaimer(&usr, 1024, set_bool(&reclaim_done)));
grpc_exec_ctx_finish(&exec_ctx);
}
{
bool done = false;
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_alloc(&exec_ctx, &usr, 1024, set_bool(&done));
grpc_exec_ctx_finish(&exec_ctx);
GPR_ASSERT(reclaim_done);
GPR_ASSERT(done);
}
{
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_free(&exec_ctx, &usr, 1024);
grpc_exec_ctx_finish(&exec_ctx);
}
grpc_resource_quota_unref(q);
destroy_user(&usr);
}
static void test_unused_reclaim_is_cancelled(void) {
gpr_log(GPR_INFO, "** test_unused_reclaim_is_cancelled **");
grpc_resource_quota *q =
grpc_resource_quota_create("test_unused_reclaim_is_cancelled");
grpc_resource_quota_resize(q, 1024);
grpc_resource_user usr;
grpc_resource_user_init(&usr, q, "usr");
bool benign_done = false;
bool destructive_done = false;
{
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_post_reclaimer(
&exec_ctx, &usr, false, make_unused_reclaimer(set_bool(&benign_done)));
grpc_resource_user_post_reclaimer(
&exec_ctx, &usr, true,
make_unused_reclaimer(set_bool(&destructive_done)));
grpc_exec_ctx_finish(&exec_ctx);
GPR_ASSERT(!benign_done);
GPR_ASSERT(!destructive_done);
}
grpc_resource_quota_unref(q);
destroy_user(&usr);
GPR_ASSERT(benign_done);
GPR_ASSERT(destructive_done);
}
static void test_benign_reclaim_is_preferred(void) {
gpr_log(GPR_INFO, "** test_benign_reclaim_is_preferred **");
grpc_resource_quota *q =
grpc_resource_quota_create("test_benign_reclaim_is_preferred");
grpc_resource_quota_resize(q, 1024);
grpc_resource_user usr;
grpc_resource_user_init(&usr, q, "usr");
bool benign_done = false;
bool destructive_done = false;
{
bool done = false;
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_alloc(&exec_ctx, &usr, 1024, set_bool(&done));
grpc_exec_ctx_finish(&exec_ctx);
GPR_ASSERT(done);
}
{
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_post_reclaimer(
&exec_ctx, &usr, false,
make_reclaimer(&usr, 1024, set_bool(&benign_done)));
grpc_resource_user_post_reclaimer(
&exec_ctx, &usr, true,
make_unused_reclaimer(set_bool(&destructive_done)));
grpc_exec_ctx_finish(&exec_ctx);
GPR_ASSERT(!benign_done);
GPR_ASSERT(!destructive_done);
}
{
bool done = false;
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_alloc(&exec_ctx, &usr, 1024, set_bool(&done));
grpc_exec_ctx_finish(&exec_ctx);
GPR_ASSERT(benign_done);
GPR_ASSERT(!destructive_done);
GPR_ASSERT(done);
}
{
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_free(&exec_ctx, &usr, 1024);
grpc_exec_ctx_finish(&exec_ctx);
}
grpc_resource_quota_unref(q);
destroy_user(&usr);
GPR_ASSERT(benign_done);
GPR_ASSERT(destructive_done);
}
static void test_multiple_reclaims_can_be_triggered(void) {
gpr_log(GPR_INFO, "** test_multiple_reclaims_can_be_triggered **");
grpc_resource_quota *q =
grpc_resource_quota_create("test_multiple_reclaims_can_be_triggered");
grpc_resource_quota_resize(q, 1024);
grpc_resource_user usr;
grpc_resource_user_init(&usr, q, "usr");
bool benign_done = false;
bool destructive_done = false;
{
bool done = false;
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_alloc(&exec_ctx, &usr, 1024, set_bool(&done));
grpc_exec_ctx_finish(&exec_ctx);
GPR_ASSERT(done);
}
{
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_post_reclaimer(
&exec_ctx, &usr, false,
make_reclaimer(&usr, 512, set_bool(&benign_done)));
grpc_resource_user_post_reclaimer(
&exec_ctx, &usr, true,
make_reclaimer(&usr, 512, set_bool(&destructive_done)));
grpc_exec_ctx_finish(&exec_ctx);
GPR_ASSERT(!benign_done);
GPR_ASSERT(!destructive_done);
}
{
bool done = false;
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_alloc(&exec_ctx, &usr, 1024, set_bool(&done));
grpc_exec_ctx_finish(&exec_ctx);
GPR_ASSERT(benign_done);
GPR_ASSERT(destructive_done);
GPR_ASSERT(done);
}
{
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_free(&exec_ctx, &usr, 1024);
grpc_exec_ctx_finish(&exec_ctx);
}
grpc_resource_quota_unref(q);
destroy_user(&usr);
GPR_ASSERT(benign_done);
GPR_ASSERT(destructive_done);
}
static void test_resource_user_stays_allocated_until_memory_released(void) {
gpr_log(GPR_INFO,
"** test_resource_user_stays_allocated_until_memory_released **");
grpc_resource_quota *q = grpc_resource_quota_create(
"test_resource_user_stays_allocated_until_memory_released");
grpc_resource_quota_resize(q, 1024 * 1024);
grpc_resource_user usr;
grpc_resource_user_init(&usr, q, "usr");
bool done = false;
{
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_alloc(&exec_ctx, &usr, 1024, NULL);
grpc_exec_ctx_finish(&exec_ctx);
}
{
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_quota_unref(q);
grpc_resource_user_shutdown(&exec_ctx, &usr, set_bool(&done));
grpc_exec_ctx_finish(&exec_ctx);
GPR_ASSERT(!done);
}
{
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_free(&exec_ctx, &usr, 1024);
grpc_exec_ctx_finish(&exec_ctx);
GPR_ASSERT(done);
}
{
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_destroy(&exec_ctx, &usr);
grpc_exec_ctx_finish(&exec_ctx);
}
}
static void
test_resource_user_stays_allocated_and_reclaimers_unrun_until_memory_released(
void) {
gpr_log(GPR_INFO,
"** "
"test_resource_user_stays_allocated_and_reclaimers_unrun_until_"
"memory_released **");
grpc_resource_quota *q = grpc_resource_quota_create(
"test_resource_user_stays_allocated_and_reclaimers_unrun_until_memory_"
"released");
grpc_resource_quota_resize(q, 1024);
for (int i = 0; i < 10; i++) {
grpc_resource_user usr;
grpc_resource_user_init(&usr, q, "usr");
bool done = false;
bool reclaimer_cancelled = false;
{
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_post_reclaimer(
&exec_ctx, &usr, false,
make_unused_reclaimer(set_bool(&reclaimer_cancelled)));
grpc_exec_ctx_finish(&exec_ctx);
GPR_ASSERT(!reclaimer_cancelled);
}
{
bool allocated = false;
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_alloc(&exec_ctx, &usr, 1024, set_bool(&allocated));
grpc_exec_ctx_finish(&exec_ctx);
GPR_ASSERT(allocated);
GPR_ASSERT(!reclaimer_cancelled);
}
{
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_shutdown(&exec_ctx, &usr, set_bool(&done));
grpc_exec_ctx_finish(&exec_ctx);
GPR_ASSERT(!done);
GPR_ASSERT(!reclaimer_cancelled);
}
{
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_free(&exec_ctx, &usr, 1024);
grpc_exec_ctx_finish(&exec_ctx);
GPR_ASSERT(done);
GPR_ASSERT(reclaimer_cancelled);
}
{
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_destroy(&exec_ctx, &usr);
grpc_exec_ctx_finish(&exec_ctx);
}
}
grpc_resource_quota_unref(q);
}
static void test_reclaimers_can_be_posted_repeatedly(void) {
gpr_log(GPR_INFO, "** test_reclaimers_can_be_posted_repeatedly **");
grpc_resource_quota *q =
grpc_resource_quota_create("test_reclaimers_can_be_posted_repeatedly");
grpc_resource_quota_resize(q, 1024);
grpc_resource_user usr;
grpc_resource_user_init(&usr, q, "usr");
{
bool allocated = false;
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_alloc(&exec_ctx, &usr, 1024, set_bool(&allocated));
grpc_exec_ctx_finish(&exec_ctx);
GPR_ASSERT(allocated);
}
for (int i = 0; i < 10; i++) {
bool reclaimer_done = false;
{
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_post_reclaimer(
&exec_ctx, &usr, false,
make_reclaimer(&usr, 1024, set_bool(&reclaimer_done)));
grpc_exec_ctx_finish(&exec_ctx);
GPR_ASSERT(!reclaimer_done);
}
{
bool allocated = false;
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_alloc(&exec_ctx, &usr, 1024, set_bool(&allocated));
grpc_exec_ctx_finish(&exec_ctx);
GPR_ASSERT(allocated);
GPR_ASSERT(reclaimer_done);
}
}
{
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_free(&exec_ctx, &usr, 1024);
grpc_exec_ctx_finish(&exec_ctx);
}
destroy_user(&usr);
grpc_resource_quota_unref(q);
}
static void test_one_slice(void) {
gpr_log(GPR_INFO, "** test_one_slice **");
grpc_resource_quota *q = grpc_resource_quota_create("test_one_slice");
grpc_resource_quota_resize(q, 1024);
grpc_resource_user usr;
grpc_resource_user_init(&usr, q, "usr");
grpc_resource_user_slice_allocator alloc;
int num_allocs = 0;
grpc_resource_user_slice_allocator_init(&alloc, &usr, inc_int_cb,
&num_allocs);
gpr_slice_buffer buffer;
gpr_slice_buffer_init(&buffer);
{
const int start_allocs = num_allocs;
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_alloc_slices(&exec_ctx, &alloc, 1024, 1, &buffer);
grpc_exec_ctx_finish(&exec_ctx);
GPR_ASSERT(num_allocs == start_allocs + 1);
}
gpr_slice_buffer_destroy(&buffer);
destroy_user(&usr);
grpc_resource_quota_unref(q);
}
static void test_one_slice_deleted_late(void) {
gpr_log(GPR_INFO, "** test_one_slice_deleted_late **");
grpc_resource_quota *q =
grpc_resource_quota_create("test_one_slice_deleted_late");
grpc_resource_quota_resize(q, 1024);
grpc_resource_user usr;
grpc_resource_user_init(&usr, q, "usr");
grpc_resource_user_slice_allocator alloc;
int num_allocs = 0;
grpc_resource_user_slice_allocator_init(&alloc, &usr, inc_int_cb,
&num_allocs);
gpr_slice_buffer buffer;
gpr_slice_buffer_init(&buffer);
{
const int start_allocs = num_allocs;
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_alloc_slices(&exec_ctx, &alloc, 1024, 1, &buffer);
grpc_exec_ctx_finish(&exec_ctx);
GPR_ASSERT(num_allocs == start_allocs + 1);
}
bool done = false;
{
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_shutdown(&exec_ctx, &usr, set_bool(&done));
grpc_exec_ctx_finish(&exec_ctx);
GPR_ASSERT(!done);
}
grpc_resource_quota_unref(q);
gpr_slice_buffer_destroy(&buffer);
GPR_ASSERT(done);
{
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_resource_user_destroy(&exec_ctx, &usr);
grpc_exec_ctx_finish(&exec_ctx);
}
}
int main(int argc, char **argv) {
grpc_test_init(argc, argv);
grpc_init();
test_no_op();
test_resize_then_destroy();
test_resource_user_no_op();
test_instant_alloc_then_free();
test_instant_alloc_free_pair();
test_simple_async_alloc();
test_async_alloc_blocked_by_size();
test_scavenge();
test_scavenge_blocked();
test_blocked_until_scheduled_reclaim();
test_blocked_until_scheduled_reclaim_and_scavenge();
test_blocked_until_scheduled_destructive_reclaim();
test_unused_reclaim_is_cancelled();
test_benign_reclaim_is_preferred();
test_multiple_reclaims_can_be_triggered();
test_resource_user_stays_allocated_until_memory_released();
test_resource_user_stays_allocated_and_reclaimers_unrun_until_memory_released();
test_reclaimers_can_be_posted_repeatedly();
test_one_slice();
test_one_slice_deleted_late();
grpc_shutdown();
return 0;
}

@ -114,7 +114,7 @@ void test_succeeds(void) {
GPR_ASSERT(getsockname(svr_fd, (struct sockaddr *)addr,
(socklen_t *)&resolved_addr.len) == 0);
grpc_closure_init(&done, must_succeed, NULL);
grpc_tcp_client_connect(&exec_ctx, &done, &g_connecting, g_pollset_set,
grpc_tcp_client_connect(&exec_ctx, &done, &g_connecting, g_pollset_set, NULL,
&resolved_addr, gpr_inf_future(GPR_CLOCK_REALTIME));
/* await the connection */
@ -164,7 +164,7 @@ void test_fails(void) {
/* connect to a broken address */
grpc_closure_init(&done, must_fail, NULL);
grpc_tcp_client_connect(&exec_ctx, &done, &g_connecting, g_pollset_set,
grpc_tcp_client_connect(&exec_ctx, &done, &g_connecting, g_pollset_set, NULL,
&resolved_addr, gpr_inf_future(GPR_CLOCK_REALTIME));
gpr_mu_lock(g_mu);

@ -181,7 +181,10 @@ static void read_test(size_t num_bytes, size_t slice_size) {
create_sockets(sv);
ep = grpc_tcp_create(grpc_fd_create(sv[1], "read_test"), slice_size, "test");
grpc_resource_quota *resource_quota = grpc_resource_quota_create("read_test");
ep = grpc_tcp_create(grpc_fd_create(sv[1], "read_test"), resource_quota,
slice_size, "test");
grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
grpc_endpoint_add_to_pollset(&exec_ctx, ep, g_pollset);
written_bytes = fill_socket_partial(sv[0], num_bytes);
@ -228,8 +231,11 @@ static void large_read_test(size_t slice_size) {
create_sockets(sv);
ep = grpc_tcp_create(grpc_fd_create(sv[1], "large_read_test"), slice_size,
"test");
grpc_resource_quota *resource_quota =
grpc_resource_quota_create("large_read_test");
ep = grpc_tcp_create(grpc_fd_create(sv[1], "large_read_test"), resource_quota,
slice_size, "test");
grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
grpc_endpoint_add_to_pollset(&exec_ctx, ep, g_pollset);
written_bytes = fill_socket(sv[0]);
@ -364,8 +370,11 @@ static void write_test(size_t num_bytes, size_t slice_size) {
create_sockets(sv);
ep = grpc_tcp_create(grpc_fd_create(sv[1], "write_test"),
grpc_resource_quota *resource_quota =
grpc_resource_quota_create("write_test");
ep = grpc_tcp_create(grpc_fd_create(sv[1], "write_test"), resource_quota,
GRPC_TCP_DEFAULT_READ_SLICE_SIZE, "test");
grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
grpc_endpoint_add_to_pollset(&exec_ctx, ep, g_pollset);
state.ep = ep;
@ -428,8 +437,12 @@ static void release_fd_test(size_t num_bytes, size_t slice_size) {
create_sockets(sv);
ep = grpc_tcp_create(grpc_fd_create(sv[1], "read_test"), slice_size, "test");
grpc_resource_quota *resource_quota =
grpc_resource_quota_create("release_fd_test");
ep = grpc_tcp_create(grpc_fd_create(sv[1], "read_test"), resource_quota,
slice_size, "test");
GPR_ASSERT(grpc_tcp_fd(ep) == sv[1] && sv[1] >= 0);
grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
grpc_endpoint_add_to_pollset(&exec_ctx, ep, g_pollset);
written_bytes = fill_socket_partial(sv[0], num_bytes);
@ -450,8 +463,10 @@ static void release_fd_test(size_t num_bytes, size_t slice_size) {
"pollset_work",
grpc_pollset_work(&exec_ctx, g_pollset, &worker,
gpr_now(GPR_CLOCK_MONOTONIC), deadline)));
gpr_log(GPR_DEBUG, "wakeup: read=%" PRIdPTR " target=%" PRIdPTR,
state.read_bytes, state.target_read_bytes);
gpr_mu_unlock(g_mu);
grpc_exec_ctx_finish(&exec_ctx);
grpc_exec_ctx_flush(&exec_ctx);
gpr_mu_lock(g_mu);
}
GPR_ASSERT(state.read_bytes == state.target_read_bytes);
@ -459,6 +474,7 @@ static void release_fd_test(size_t num_bytes, size_t slice_size) {
gpr_slice_buffer_destroy(&state.incoming);
grpc_tcp_destroy_and_release_fd(&exec_ctx, ep, &fd, &fd_released_cb);
grpc_exec_ctx_flush(&exec_ctx);
gpr_mu_lock(g_mu);
while (!fd_released_done) {
grpc_pollset_worker *worker = NULL;
@ -466,6 +482,7 @@ static void release_fd_test(size_t num_bytes, size_t slice_size) {
"pollset_work",
grpc_pollset_work(&exec_ctx, g_pollset, &worker,
gpr_now(GPR_CLOCK_MONOTONIC), deadline)));
gpr_log(GPR_DEBUG, "wakeup: fd_released_done=%d", fd_released_done);
}
gpr_mu_unlock(g_mu);
GPR_ASSERT(fd_released_done == 1);
@ -511,10 +528,13 @@ static grpc_endpoint_test_fixture create_fixture_tcp_socketpair(
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
create_sockets(sv);
grpc_resource_quota *resource_quota =
grpc_resource_quota_create("tcp_posix_test_socketpair");
f.client_ep = grpc_tcp_create(grpc_fd_create(sv[0], "fixture:client"),
slice_size, "test");
resource_quota, slice_size, "test");
f.server_ep = grpc_tcp_create(grpc_fd_create(sv[1], "fixture:server"),
slice_size, "test");
resource_quota, slice_size, "test");
grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
grpc_endpoint_add_to_pollset(&exec_ctx, f.client_ep, g_pollset);
grpc_endpoint_add_to_pollset(&exec_ctx, f.server_ep, g_pollset);

@ -138,7 +138,8 @@ static void on_connect(grpc_exec_ctx *exec_ctx, void *arg, grpc_endpoint *tcp,
static void test_no_op(void) {
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_tcp_server *s;
GPR_ASSERT(GRPC_ERROR_NONE == grpc_tcp_server_create(NULL, NULL, &s));
GPR_ASSERT(GRPC_ERROR_NONE ==
grpc_tcp_server_create(&exec_ctx, NULL, NULL, &s));
grpc_tcp_server_unref(&exec_ctx, s);
grpc_exec_ctx_finish(&exec_ctx);
}
@ -146,7 +147,8 @@ static void test_no_op(void) {
static void test_no_op_with_start(void) {
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
grpc_tcp_server *s;
GPR_ASSERT(GRPC_ERROR_NONE == grpc_tcp_server_create(NULL, NULL, &s));
GPR_ASSERT(GRPC_ERROR_NONE ==
grpc_tcp_server_create(&exec_ctx, NULL, NULL, &s));
LOG_TEST("test_no_op_with_start");
grpc_tcp_server_start(&exec_ctx, s, NULL, 0, on_connect, NULL);
grpc_tcp_server_unref(&exec_ctx, s);
@ -158,7 +160,8 @@ static void test_no_op_with_port(void) {
grpc_resolved_address resolved_addr;
struct sockaddr_in *addr = (struct sockaddr_in *)resolved_addr.addr;
grpc_tcp_server *s;
GPR_ASSERT(GRPC_ERROR_NONE == grpc_tcp_server_create(NULL, NULL, &s));
GPR_ASSERT(GRPC_ERROR_NONE ==
grpc_tcp_server_create(&exec_ctx, NULL, NULL, &s));
LOG_TEST("test_no_op_with_port");
memset(&resolved_addr, 0, sizeof(resolved_addr));
@ -178,7 +181,8 @@ static void test_no_op_with_port_and_start(void) {
grpc_resolved_address resolved_addr;
struct sockaddr_in *addr = (struct sockaddr_in *)resolved_addr.addr;
grpc_tcp_server *s;
GPR_ASSERT(GRPC_ERROR_NONE == grpc_tcp_server_create(NULL, NULL, &s));
GPR_ASSERT(GRPC_ERROR_NONE ==
grpc_tcp_server_create(&exec_ctx, NULL, NULL, &s));
LOG_TEST("test_no_op_with_port_and_start");
int port;
@ -241,7 +245,8 @@ static void test_connect(unsigned n) {
unsigned svr1_fd_count;
int svr1_port;
grpc_tcp_server *s;
GPR_ASSERT(GRPC_ERROR_NONE == grpc_tcp_server_create(NULL, NULL, &s));
GPR_ASSERT(GRPC_ERROR_NONE ==
grpc_tcp_server_create(&exec_ctx, NULL, NULL, &s));
unsigned i;
server_weak_ref weak_ref;
server_weak_ref_init(&weak_ref);

@ -56,7 +56,10 @@ static grpc_endpoint_test_fixture secure_endpoint_create_fixture_tcp_socketpair(
grpc_endpoint_test_fixture f;
grpc_endpoint_pair tcp;
tcp = grpc_iomgr_create_endpoint_pair("fixture", slice_size);
grpc_resource_quota *resource_quota =
grpc_resource_quota_create("secure_endpoint_test");
tcp = grpc_iomgr_create_endpoint_pair("fixture", resource_quota, slice_size);
grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
grpc_endpoint_add_to_pollset(&exec_ctx, tcp.client, g_pollset);
grpc_endpoint_add_to_pollset(&exec_ctx, tcp.server, g_pollset);

@ -120,7 +120,7 @@ void bad_server_thread(void *vargs) {
struct sockaddr_storage *addr = (struct sockaddr_storage *)resolved_addr.addr;
int port;
grpc_tcp_server *s;
grpc_error *error = grpc_tcp_server_create(NULL, NULL, &s);
grpc_error *error = grpc_tcp_server_create(&exec_ctx, NULL, NULL, &s);
GPR_ASSERT(error == GRPC_ERROR_NONE);
memset(&resolved_addr, 0, sizeof(resolved_addr));
addr->ss_family = AF_INET;

@ -33,16 +33,20 @@
#include "test/core/util/mock_endpoint.h"
#include <inttypes.h>
#include <grpc/support/alloc.h>
#include <grpc/support/string_util.h>
typedef struct grpc_mock_endpoint {
grpc_endpoint base;
gpr_mu mu;
int refs;
void (*on_write)(gpr_slice slice);
gpr_slice_buffer read_buffer;
gpr_slice_buffer *on_read_out;
grpc_closure *on_read;
grpc_resource_user resource_user;
} grpc_mock_endpoint;
static void me_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
@ -74,6 +78,24 @@ static void me_add_to_pollset(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
static void me_add_to_pollset_set(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
grpc_pollset_set *pollset) {}
static void unref(grpc_exec_ctx *exec_ctx, grpc_mock_endpoint *m) {
gpr_mu_lock(&m->mu);
if (0 == --m->refs) {
gpr_mu_unlock(&m->mu);
gpr_slice_buffer_destroy(&m->read_buffer);
grpc_resource_user_destroy(exec_ctx, &m->resource_user);
gpr_free(m);
} else {
gpr_mu_unlock(&m->mu);
}
}
static void me_finish_shutdown(grpc_exec_ctx *exec_ctx, void *me,
grpc_error *error) {
grpc_mock_endpoint *m = me;
unref(exec_ctx, m);
}
static void me_shutdown(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) {
grpc_mock_endpoint *m = (grpc_mock_endpoint *)ep;
gpr_mu_lock(&m->mu);
@ -82,19 +104,25 @@ static void me_shutdown(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) {
GRPC_ERROR_CREATE("Endpoint Shutdown"), NULL);
m->on_read = NULL;
}
grpc_resource_user_shutdown(exec_ctx, &m->resource_user,
grpc_closure_create(me_finish_shutdown, m));
gpr_mu_unlock(&m->mu);
}
static void me_destroy(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) {
grpc_mock_endpoint *m = (grpc_mock_endpoint *)ep;
gpr_slice_buffer_destroy(&m->read_buffer);
gpr_free(m);
unref(exec_ctx, m);
}
static char *me_get_peer(grpc_endpoint *ep) {
return gpr_strdup("fake:mock_endpoint");
}
static grpc_resource_user *me_get_resource_user(grpc_endpoint *ep) {
grpc_mock_endpoint *m = (grpc_mock_endpoint *)ep;
return &m->resource_user;
}
static grpc_workqueue *me_get_workqueue(grpc_endpoint *ep) { return NULL; }
static const grpc_endpoint_vtable vtable = {
@ -105,12 +133,19 @@ static const grpc_endpoint_vtable vtable = {
me_add_to_pollset_set,
me_shutdown,
me_destroy,
me_get_resource_user,
me_get_peer,
};
grpc_endpoint *grpc_mock_endpoint_create(void (*on_write)(gpr_slice slice)) {
grpc_endpoint *grpc_mock_endpoint_create(void (*on_write)(gpr_slice slice),
grpc_resource_quota *resource_quota) {
grpc_mock_endpoint *m = gpr_malloc(sizeof(*m));
m->base.vtable = &vtable;
m->refs = 2;
char *name;
gpr_asprintf(&name, "mock_endpoint_%" PRIxPTR, (intptr_t)m);
grpc_resource_user_init(&m->resource_user, resource_quota, name);
gpr_free(name);
gpr_slice_buffer_init(&m->read_buffer);
gpr_mu_init(&m->mu);
m->on_write = on_write;

@ -36,7 +36,8 @@
#include "src/core/lib/iomgr/endpoint.h"
grpc_endpoint *grpc_mock_endpoint_create(void (*on_write)(gpr_slice slice));
grpc_endpoint *grpc_mock_endpoint_create(void (*on_write)(gpr_slice slice),
grpc_resource_quota *resource_quota);
void grpc_mock_endpoint_put_read(grpc_exec_ctx *exec_ctx,
grpc_endpoint *mock_endpoint, gpr_slice slice);

@ -33,6 +33,8 @@
#include "test/core/util/passthru_endpoint.h"
#include <inttypes.h>
#include <grpc/support/alloc.h>
#include <grpc/support/string_util.h>
@ -44,6 +46,7 @@ typedef struct {
gpr_slice_buffer read_buffer;
gpr_slice_buffer *on_read_out;
grpc_closure *on_read;
grpc_resource_user resource_user;
} half;
struct passthru_endpoint {
@ -122,7 +125,8 @@ static void me_shutdown(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) {
gpr_mu_unlock(&m->parent->mu);
}
static void me_destroy(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) {
static void me_really_destroy(grpc_exec_ctx *exec_ctx, void *ep,
grpc_error *error) {
passthru_endpoint *p = ((half *)ep)->parent;
gpr_mu_lock(&p->mu);
if (0 == --p->halves) {
@ -136,12 +140,23 @@ static void me_destroy(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) {
}
}
static void me_destroy(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) {
half *m = (half *)ep;
grpc_resource_user_shutdown(exec_ctx, &m->resource_user,
grpc_closure_create(me_really_destroy, m));
}
static char *me_get_peer(grpc_endpoint *ep) {
return gpr_strdup("fake:mock_endpoint");
}
static grpc_workqueue *me_get_workqueue(grpc_endpoint *ep) { return NULL; }
static grpc_resource_user *me_get_resource_user(grpc_endpoint *ep) {
half *m = (half *)ep;
return &m->resource_user;
}
static const grpc_endpoint_vtable vtable = {
me_read,
me_write,
@ -150,23 +165,32 @@ static const grpc_endpoint_vtable vtable = {
me_add_to_pollset_set,
me_shutdown,
me_destroy,
me_get_resource_user,
me_get_peer,
};
static void half_init(half *m, passthru_endpoint *parent) {
static void half_init(half *m, passthru_endpoint *parent,
grpc_resource_quota *resource_quota,
const char *half_name) {
m->base.vtable = &vtable;
m->parent = parent;
gpr_slice_buffer_init(&m->read_buffer);
m->on_read = NULL;
char *name;
gpr_asprintf(&name, "passthru_endpoint_%s_%" PRIxPTR, half_name,
(intptr_t)parent);
grpc_resource_user_init(&m->resource_user, resource_quota, name);
gpr_free(name);
}
void grpc_passthru_endpoint_create(grpc_endpoint **client,
grpc_endpoint **server) {
grpc_endpoint **server,
grpc_resource_quota *resource_quota) {
passthru_endpoint *m = gpr_malloc(sizeof(*m));
m->halves = 2;
m->shutdown = 0;
half_init(&m->client, m);
half_init(&m->server, m);
half_init(&m->client, m, resource_quota, "client");
half_init(&m->server, m, resource_quota, "server");
gpr_mu_init(&m->mu);
*client = &m->client.base;
*server = &m->server.base;

@ -37,6 +37,7 @@
#include "src/core/lib/iomgr/endpoint.h"
void grpc_passthru_endpoint_create(grpc_endpoint **client,
grpc_endpoint **server);
grpc_endpoint **server,
grpc_resource_quota *resource_quota);
#endif

@ -99,9 +99,12 @@ void grpc_free_port_using_server(char *server, int port) {
req.http.path = path;
grpc_httpcli_context_init(&context);
grpc_httpcli_get(&exec_ctx, &context, &pr.pops, &req,
grpc_resource_quota *resource_quota =
grpc_resource_quota_create("port_server_client/free");
grpc_httpcli_get(&exec_ctx, &context, &pr.pops, resource_quota, &req,
GRPC_TIMEOUT_SECONDS_TO_DEADLINE(10),
grpc_closure_create(freed_port_from_server, &pr), &rsp);
grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
gpr_mu_lock(pr.mu);
while (!pr.done) {
grpc_pollset_worker *worker = NULL;
@ -167,10 +170,13 @@ static void got_port_from_server(grpc_exec_ctx *exec_ctx, void *arg,
req.http.path = "/get";
grpc_http_response_destroy(&pr->response);
memset(&pr->response, 0, sizeof(pr->response));
grpc_httpcli_get(exec_ctx, pr->ctx, &pr->pops, &req,
grpc_resource_quota *resource_quota =
grpc_resource_quota_create("port_server_client/pick_retry");
grpc_httpcli_get(exec_ctx, pr->ctx, &pr->pops, resource_quota, &req,
GRPC_TIMEOUT_SECONDS_TO_DEADLINE(10),
grpc_closure_create(got_port_from_server, pr),
&pr->response);
grpc_resource_quota_internal_unref(exec_ctx, resource_quota);
return;
}
GPR_ASSERT(response);
@ -211,9 +217,13 @@ int grpc_pick_port_using_server(char *server) {
req.http.path = "/get";
grpc_httpcli_context_init(&context);
grpc_httpcli_get(
&exec_ctx, &context, &pr.pops, &req, GRPC_TIMEOUT_SECONDS_TO_DEADLINE(10),
grpc_closure_create(got_port_from_server, &pr), &pr.response);
grpc_resource_quota *resource_quota =
grpc_resource_quota_create("port_server_client/pick");
grpc_httpcli_get(&exec_ctx, &context, &pr.pops, resource_quota, &req,
GRPC_TIMEOUT_SECONDS_TO_DEADLINE(10),
grpc_closure_create(got_port_from_server, &pr),
&pr.response);
grpc_resource_quota_internal_unref(&exec_ctx, resource_quota);
grpc_exec_ctx_finish(&exec_ctx);
gpr_mu_lock(pr.mu);
while (pr.port == -1) {

@ -75,8 +75,8 @@ void test_tcp_server_start(test_tcp_server *server, int port) {
addr->sin_port = htons((uint16_t)port);
memset(&addr->sin_addr, 0, sizeof(addr->sin_addr));
grpc_error *error = grpc_tcp_server_create(&server->shutdown_complete, NULL,
&server->tcp_server);
grpc_error *error = grpc_tcp_server_create(
&exec_ctx, &server->shutdown_complete, NULL, &server->tcp_server);
GPR_ASSERT(error == GRPC_ERROR_NONE);
error =
grpc_tcp_server_add_port(server->tcp_server, &resolved_addr, &port_added);

@ -37,6 +37,7 @@
#include <grpc++/channel.h>
#include <grpc++/client_context.h>
#include <grpc++/create_channel.h>
#include <grpc++/resource_quota.h>
#include <grpc++/security/auth_metadata_processor.h>
#include <grpc++/security/credentials.h>
#include <grpc++/security/server_credentials.h>
@ -240,6 +241,7 @@ class End2endTest : public ::testing::TestWithParam<TestScenario> {
server_address_ << "127.0.0.1:" << port;
// Setup server
ServerBuilder builder;
ConfigureServerBuilder(&builder);
auto server_creds = GetServerCredentials(GetParam().credentials_type);
if (GetParam().credentials_type != kInsecureCredentialsType) {
server_creds->SetAuthMetadataProcessor(processor);
@ -247,13 +249,16 @@ class End2endTest : public ::testing::TestWithParam<TestScenario> {
builder.AddListeningPort(server_address_.str(), server_creds);
builder.RegisterService(&service_);
builder.RegisterService("foo.test.youtube.com", &special_service_);
builder.SetMaxMessageSize(
kMaxMessageSize_); // For testing max message size.
builder.RegisterService(&dup_pkg_service_);
server_ = builder.BuildAndStart();
is_server_started_ = true;
}
virtual void ConfigureServerBuilder(ServerBuilder* builder) {
builder->SetMaxMessageSize(
kMaxMessageSize_); // For testing max message size.
}
void ResetChannel() {
if (!is_server_started_) {
StartServer(std::shared_ptr<AuthMetadataProcessor>());
@ -1476,6 +1481,32 @@ TEST_P(SecureEnd2endTest, ClientAuthContext) {
}
}
class ResourceQuotaEnd2endTest : public End2endTest {
public:
ResourceQuotaEnd2endTest()
: server_resource_quota_("server_resource_quota") {}
virtual void ConfigureServerBuilder(ServerBuilder* builder) GRPC_OVERRIDE {
builder->SetResourceQuota(server_resource_quota_);
}
private:
ResourceQuota server_resource_quota_;
};
TEST_P(ResourceQuotaEnd2endTest, SimpleRequest) {
ResetStub();
EchoRequest request;
EchoResponse response;
request.set_message("Hello");
ClientContext context;
Status s = stub_->Echo(&context, request, &response);
EXPECT_EQ(response.message(), request.message());
EXPECT_TRUE(s.ok());
}
std::vector<TestScenario> CreateTestScenarios(bool use_proxy,
bool test_insecure,
bool test_secure) {
@ -1513,6 +1544,10 @@ INSTANTIATE_TEST_CASE_P(SecureEnd2end, SecureEnd2endTest,
::testing::ValuesIn(CreateTestScenarios(false, false,
true)));
INSTANTIATE_TEST_CASE_P(ResourceQuotaEnd2end, ResourceQuotaEnd2endTest,
::testing::ValuesIn(CreateTestScenarios(false, true,
true)));
} // namespace
} // namespace testing
} // namespace grpc

@ -36,6 +36,7 @@
#include <condition_variable>
#include <mutex>
#include <unordered_map>
#include <vector>
#include <grpc++/channel.h>
@ -114,19 +115,37 @@ class ClientRequestCreator<ByteBuffer> {
class HistogramEntry GRPC_FINAL {
public:
HistogramEntry() : used_(false) {}
bool used() const { return used_; }
HistogramEntry() : value_used_(false), status_used_(false) {}
bool value_used() const { return value_used_; }
double value() const { return value_; }
void set_value(double v) {
used_ = true;
value_used_ = true;
value_ = v;
}
bool status_used() const { return status_used_; }
int status() const { return status_; }
void set_status(int status) {
status_used_ = true;
status_ = status;
}
private:
bool used_;
bool value_used_;
double value_;
bool status_used_;
int status_;
};
typedef std::unordered_map<int, int64_t> StatusHistogram;
inline void MergeStatusHistogram(const StatusHistogram& from,
StatusHistogram* to) {
for (StatusHistogram::const_iterator it = from.begin(); it != from.end();
++it) {
(*to)[it->first] += it->second;
}
}
class Client {
public:
Client()
@ -139,6 +158,7 @@ class Client {
ClientStats Mark(bool reset) {
Histogram latencies;
StatusHistogram statuses;
UsageTimer::Result timer_result;
MaybeStartRequests();
@ -146,27 +166,36 @@ class Client {
// avoid std::vector for old compilers that expect a copy constructor
if (reset) {
Histogram* to_merge = new Histogram[threads_.size()];
StatusHistogram* to_merge_status = new StatusHistogram[threads_.size()];
for (size_t i = 0; i < threads_.size(); i++) {
threads_[i]->BeginSwap(&to_merge[i]);
threads_[i]->BeginSwap(&to_merge[i], &to_merge_status[i]);
}
std::unique_ptr<UsageTimer> timer(new UsageTimer);
timer_.swap(timer);
for (size_t i = 0; i < threads_.size(); i++) {
threads_[i]->EndSwap();
latencies.Merge(to_merge[i]);
MergeStatusHistogram(to_merge_status[i], &statuses);
}
delete[] to_merge;
delete[] to_merge_status;
timer_result = timer->Mark();
} else {
// merge snapshots of each thread histogram
for (size_t i = 0; i < threads_.size(); i++) {
threads_[i]->MergeStatsInto(&latencies);
threads_[i]->MergeStatsInto(&latencies, &statuses);
}
timer_result = timer_->Mark();
}
ClientStats stats;
latencies.FillProto(stats.mutable_latencies());
for (StatusHistogram::const_iterator it = statuses.begin();
it != statuses.end(); ++it) {
RequestResultCount* rrc = stats.add_request_results();
rrc->set_status_code(it->first);
rrc->set_count(it->second);
}
stats.set_time_elapsed(timer_result.wall);
stats.set_time_system(timer_result.system);
stats.set_time_user(timer_result.user);
@ -258,16 +287,16 @@ class Client {
~Thread() { impl_.join(); }
void BeginSwap(Histogram* n) {
void BeginSwap(Histogram* n, StatusHistogram* s) {
std::lock_guard<std::mutex> g(mu_);
n->Swap(&histogram_);
s->swap(statuses_);
}
void EndSwap() {}
void MergeStatsInto(Histogram* hist) {
void MergeStatsInto(Histogram* hist, StatusHistogram* s) {
std::unique_lock<std::mutex> g(mu_);
hist->Merge(histogram_);
MergeStatusHistogram(statuses_, s);
}
private:
@ -288,9 +317,12 @@ class Client {
const bool thread_still_ok = client_->ThreadFunc(&entry, idx_);
// lock, update histogram if needed and see if we're done
std::lock_guard<std::mutex> g(mu_);
if (entry.used()) {
if (entry.value_used()) {
histogram_.Add(entry.value());
}
if (entry.status_used()) {
statuses_[entry.status()]++;
}
if (!thread_still_ok) {
gpr_log(GPR_ERROR, "Finishing client thread due to RPC error");
}
@ -304,6 +336,7 @@ class Client {
std::mutex mu_;
Histogram histogram_;
StatusHistogram statuses_;
Client* client_;
const size_t idx_;
std::thread impl_;

@ -83,7 +83,7 @@ class ClientRpcContextUnaryImpl : public ClientRpcContext {
BenchmarkService::Stub*, grpc::ClientContext*, const RequestType&,
CompletionQueue*)>
start_req,
std::function<void(grpc::Status, ResponseType*)> on_done)
std::function<void(grpc::Status, ResponseType*, HistogramEntry*)> on_done)
: context_(),
stub_(stub),
cq_(nullptr),
@ -113,7 +113,7 @@ class ClientRpcContextUnaryImpl : public ClientRpcContext {
return true;
case State::RESP_DONE:
entry->set_value((UsageTimer::Now() - start_) * 1e9);
callback_(status_, &response_);
callback_(status_, &response_, entry);
next_state_ = State::INVALID;
return false;
default:
@ -135,7 +135,7 @@ class ClientRpcContextUnaryImpl : public ClientRpcContext {
ResponseType response_;
enum State { INVALID, READY, RESP_DONE };
State next_state_;
std::function<void(grpc::Status, ResponseType*)> callback_;
std::function<void(grpc::Status, ResponseType*, HistogramEntry*)> callback_;
std::function<gpr_timespec()> next_issue_;
std::function<std::unique_ptr<grpc::ClientAsyncResponseReader<ResponseType>>(
BenchmarkService::Stub*, grpc::ClientContext*, const RequestType&,
@ -290,7 +290,10 @@ class AsyncUnaryClient GRPC_FINAL
~AsyncUnaryClient() GRPC_OVERRIDE {}
private:
static void CheckDone(grpc::Status s, SimpleResponse* response) {}
static void CheckDone(grpc::Status s, SimpleResponse* response,
HistogramEntry* entry) {
entry->set_status(s.error_code());
}
static std::unique_ptr<grpc::ClientAsyncResponseReader<SimpleResponse>>
StartReq(BenchmarkService::Stub* stub, grpc::ClientContext* ctx,
const SimpleRequest& request, CompletionQueue* cq) {

@ -130,11 +130,8 @@ class SynchronousUnaryClient GRPC_FINAL : public SynchronousClient {
grpc::Status s =
stub->UnaryCall(&context, request_, &responses_[thread_idx]);
entry->set_value((UsageTimer::Now() - start) * 1e9);
if (!s.ok()) {
gpr_log(GPR_ERROR, "RPC error: %d: %s", s.error_code(),
s.error_message().c_str());
}
return s.ok();
entry->set_status(s.error_code());
return true;
}
};

@ -132,7 +132,8 @@ static void postprocess_scenario_result(ScenarioResult* result) {
Histogram histogram;
histogram.MergeProto(result->latencies());
auto qps = histogram.Count() / average(result->client_stats(), WallTime);
auto time_estimate = average(result->client_stats(), WallTime);
auto qps = histogram.Count() / time_estimate;
auto qps_per_server_core = qps / sum(result->server_cores(), Cores);
result->mutable_summary()->set_qps(qps);
@ -157,6 +158,23 @@ static void postprocess_scenario_result(ScenarioResult* result) {
result->mutable_summary()->set_server_user_time(server_user_time);
result->mutable_summary()->set_client_system_time(client_system_time);
result->mutable_summary()->set_client_user_time(client_user_time);
if (result->request_results_size() > 0) {
int64_t successes = 0;
int64_t failures = 0;
for (int i = 0; i < result->request_results_size(); i++) {
RequestResultCount rrc = result->request_results(i);
if (rrc.status_code() == 0) {
successes += rrc.count();
} else {
failures += rrc.count();
}
}
result->mutable_summary()->set_successful_requests_per_second(
successes / time_estimate);
result->mutable_summary()->set_failed_requests_per_second(failures /
time_estimate);
}
}
// Namespace for classes and functions used only in RunScenario
@ -444,6 +462,7 @@ std::unique_ptr<ScenarioResult> RunScenario(
// Finish a run
std::unique_ptr<ScenarioResult> result(new ScenarioResult);
Histogram merged_latencies;
std::unordered_map<int, int64_t> merged_statuses;
gpr_log(GPR_INFO, "Finishing clients");
for (size_t i = 0; i < num_clients; i++) {
@ -462,6 +481,10 @@ std::unique_ptr<ScenarioResult> RunScenario(
gpr_log(GPR_INFO, "Received final status from client %zu", i);
const auto& stats = client_status.stats();
merged_latencies.MergeProto(stats.latencies());
for (int i = 0; i < stats.request_results_size(); i++) {
merged_statuses[stats.request_results(i).status_code()] +=
stats.request_results(i).count();
}
result->add_client_stats()->CopyFrom(stats);
// That final status should be the last message on the client stream
GPR_ASSERT(!client->stream->Read(&client_status));
@ -481,6 +504,12 @@ std::unique_ptr<ScenarioResult> RunScenario(
delete[] clients;
merged_latencies.FillProto(result->mutable_latencies());
for (std::unordered_map<int, int64_t>::iterator it = merged_statuses.begin();
it != merged_statuses.end(); ++it) {
RequestResultCount* rrc = result->add_request_results();
rrc->set_status_code(it->first);
rrc->set_count(it->second);
}
gpr_log(GPR_INFO, "Finishing servers");
for (size_t i = 0; i < num_servers; i++) {

@ -73,6 +73,12 @@ void CompositeReporter::ReportTimes(const ScenarioResult& result) {
void GprLogReporter::ReportQPS(const ScenarioResult& result) {
gpr_log(GPR_INFO, "QPS: %.1f", result.summary().qps());
if (result.summary().failed_requests_per_second() > 0) {
gpr_log(GPR_INFO, "failed requests/second: %.1f",
result.summary().failed_requests_per_second());
gpr_log(GPR_INFO, "successful requests/second: %.1f",
result.summary().successful_requests_per_second());
}
}
void GprLogReporter::ReportQPSPerCore(const ScenarioResult& result) {

@ -38,6 +38,7 @@
#include <thread>
#include <grpc++/generic/async_generic_service.h>
#include <grpc++/resource_quota.h>
#include <grpc++/security/server_credentials.h>
#include <grpc++/server.h>
#include <grpc++/server_builder.h>
@ -95,6 +96,11 @@ class AsyncQpsServerTest GRPC_FINAL : public grpc::testing::Server {
srv_cqs_.emplace_back(builder.AddCompletionQueue());
}
if (config.resource_quota_size() > 0) {
builder.SetResourceQuota(ResourceQuota("AsyncQpsServerTest")
.Resize(config.resource_quota_size()));
}
server_ = builder.BuildAndStart();
using namespace std::placeholders;

@ -31,6 +31,7 @@
*
*/
#include <grpc++/resource_quota.h>
#include <grpc++/security/server_credentials.h>
#include <grpc++/server.h>
#include <grpc++/server_builder.h>
@ -91,6 +92,11 @@ class SynchronousServer GRPC_FINAL : public grpc::testing::Server {
Server::CreateServerCredentials(config));
gpr_free(server_address);
if (config.resource_quota_size() > 0) {
builder.SetResourceQuota(ResourceQuota("AsyncQpsServerTest")
.Resize(config.resource_quota_size()));
}
builder.RegisterService(&service_);
impl_ = builder.BuildAndStart();

@ -787,6 +787,7 @@ include/grpc++/impl/sync_no_cxx11.h \
include/grpc++/impl/thd.h \
include/grpc++/impl/thd_cxx11.h \
include/grpc++/impl/thd_no_cxx11.h \
include/grpc++/resource_quota.h \
include/grpc++/security/auth_context.h \
include/grpc++/security/auth_metadata_processor.h \
include/grpc++/security/credentials.h \

@ -787,6 +787,7 @@ include/grpc++/impl/sync_no_cxx11.h \
include/grpc++/impl/thd.h \
include/grpc++/impl/thd_cxx11.h \
include/grpc++/impl/thd_no_cxx11.h \
include/grpc++/resource_quota.h \
include/grpc++/security/auth_context.h \
include/grpc++/security/auth_metadata_processor.h \
include/grpc++/security/credentials.h \
@ -882,6 +883,7 @@ src/cpp/common/channel_arguments.cc \
src/cpp/common/channel_filter.cc \
src/cpp/common/completion_queue_cc.cc \
src/cpp/common/core_codegen.cc \
src/cpp/common/resource_quota_cc.cc \
src/cpp/common/rpc_method.cc \
src/cpp/server/async_generic_service.cc \
src/cpp/server/create_default_thread_pool.cc \

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

Loading…
Cancel
Save