diff options
41 files changed, 1998 insertions, 549 deletions
@@ -282,6 +282,7 @@ grpc_cc_library( "src/core/ext/census/grpc_filter.c", "src/core/ext/census/grpc_plugin.c", "src/core/ext/census/initialize.c", + "src/core/ext/census/intrusive_hash_map.c", "src/core/ext/census/mlog.c", "src/core/ext/census/operation.c", "src/core/ext/census/placeholders.c", @@ -297,6 +298,8 @@ grpc_cc_library( "src/core/ext/census/gen/census.pb.h", "src/core/ext/census/gen/trace_context.pb.h", "src/core/ext/census/grpc_filter.h", + "src/core/ext/census/intrusive_hash_map.h", + "src/core/ext/census/intrusive_hash_map_internal.h", "src/core/ext/census/mlog.h", "src/core/ext/census/resource.h", "src/core/ext/census/rpc_metric_id.h", diff --git a/CMakeLists.txt b/CMakeLists.txt index 1a23545602..1635d5dc2d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -375,6 +375,7 @@ add_dependencies(buildtests_c bdp_estimator_test) add_dependencies(buildtests_c bin_decoder_test) add_dependencies(buildtests_c bin_encoder_test) add_dependencies(buildtests_c census_context_test) +add_dependencies(buildtests_c census_intrusive_hash_map_test) add_dependencies(buildtests_c census_resource_test) add_dependencies(buildtests_c census_trace_context_test) add_dependencies(buildtests_c channel_create_test) @@ -1157,6 +1158,7 @@ add_library(grpc src/core/ext/census/grpc_filter.c src/core/ext/census/grpc_plugin.c src/core/ext/census/initialize.c + src/core/ext/census/intrusive_hash_map.c src/core/ext/census/mlog.c src/core/ext/census/operation.c src/core/ext/census/placeholders.c @@ -2048,6 +2050,7 @@ add_library(grpc_unsecure src/core/ext/census/grpc_filter.c src/core/ext/census/grpc_plugin.c src/core/ext/census/initialize.c + src/core/ext/census/intrusive_hash_map.c src/core/ext/census/mlog.c src/core/ext/census/operation.c src/core/ext/census/placeholders.c @@ -2659,6 +2662,7 @@ add_library(grpc++_cronet src/core/ext/census/grpc_filter.c src/core/ext/census/grpc_plugin.c src/core/ext/census/initialize.c + src/core/ext/census/intrusive_hash_map.c src/core/ext/census/mlog.c src/core/ext/census/operation.c src/core/ext/census/placeholders.c @@ -4769,6 +4773,37 @@ target_link_libraries(census_context_test endif (gRPC_BUILD_TESTS) if (gRPC_BUILD_TESTS) +add_executable(census_intrusive_hash_map_test + test/core/census/intrusive_hash_map_test.c +) + + +target_include_directories(census_intrusive_hash_map_test + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include + PRIVATE ${BORINGSSL_ROOT_DIR}/include + PRIVATE ${PROTOBUF_ROOT_DIR}/src + PRIVATE ${BENCHMARK_ROOT_DIR}/include + PRIVATE ${ZLIB_ROOT_DIR} + PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib + PRIVATE ${CARES_BUILD_INCLUDE_DIR} + PRIVATE ${CARES_INCLUDE_DIR} + PRIVATE ${CARES_PLATFORM_INCLUDE_DIR} + PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/cares/cares + PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags/include +) + +target_link_libraries(census_intrusive_hash_map_test + ${_gRPC_ALLTARGETS_LIBRARIES} + grpc_test_util + grpc + gpr_test_util + gpr +) + +endif (gRPC_BUILD_TESTS) +if (gRPC_BUILD_TESTS) + add_executable(census_resource_test test/core/census/resource_test.c ) @@ -967,6 +967,7 @@ bdp_estimator_test: $(BINDIR)/$(CONFIG)/bdp_estimator_test bin_decoder_test: $(BINDIR)/$(CONFIG)/bin_decoder_test bin_encoder_test: $(BINDIR)/$(CONFIG)/bin_encoder_test census_context_test: $(BINDIR)/$(CONFIG)/census_context_test +census_intrusive_hash_map_test: $(BINDIR)/$(CONFIG)/census_intrusive_hash_map_test census_resource_test: $(BINDIR)/$(CONFIG)/census_resource_test census_trace_context_test: $(BINDIR)/$(CONFIG)/census_trace_context_test channel_create_test: $(BINDIR)/$(CONFIG)/channel_create_test @@ -1360,6 +1361,7 @@ buildtests_c: privatelibs_c \ $(BINDIR)/$(CONFIG)/bin_decoder_test \ $(BINDIR)/$(CONFIG)/bin_encoder_test \ $(BINDIR)/$(CONFIG)/census_context_test \ + $(BINDIR)/$(CONFIG)/census_intrusive_hash_map_test \ $(BINDIR)/$(CONFIG)/census_resource_test \ $(BINDIR)/$(CONFIG)/census_trace_context_test \ $(BINDIR)/$(CONFIG)/channel_create_test \ @@ -1763,6 +1765,8 @@ test_c: buildtests_c $(Q) $(BINDIR)/$(CONFIG)/bin_encoder_test || ( echo test bin_encoder_test failed ; exit 1 ) $(E) "[RUN] Testing census_context_test" $(Q) $(BINDIR)/$(CONFIG)/census_context_test || ( echo test census_context_test failed ; exit 1 ) + $(E) "[RUN] Testing census_intrusive_hash_map_test" + $(Q) $(BINDIR)/$(CONFIG)/census_intrusive_hash_map_test || ( echo test census_intrusive_hash_map_test failed ; exit 1 ) $(E) "[RUN] Testing census_resource_test" $(Q) $(BINDIR)/$(CONFIG)/census_resource_test || ( echo test census_resource_test failed ; exit 1 ) $(E) "[RUN] Testing census_trace_context_test" @@ -3134,6 +3138,7 @@ LIBGRPC_SRC = \ src/core/ext/census/grpc_filter.c \ src/core/ext/census/grpc_plugin.c \ src/core/ext/census/initialize.c \ + src/core/ext/census/intrusive_hash_map.c \ src/core/ext/census/mlog.c \ src/core/ext/census/operation.c \ src/core/ext/census/placeholders.c \ @@ -3994,6 +3999,7 @@ LIBGRPC_UNSECURE_SRC = \ src/core/ext/census/grpc_filter.c \ src/core/ext/census/grpc_plugin.c \ src/core/ext/census/initialize.c \ + src/core/ext/census/intrusive_hash_map.c \ src/core/ext/census/mlog.c \ src/core/ext/census/operation.c \ src/core/ext/census/placeholders.c \ @@ -4591,6 +4597,7 @@ LIBGRPC++_CRONET_SRC = \ src/core/ext/census/grpc_filter.c \ src/core/ext/census/grpc_plugin.c \ src/core/ext/census/initialize.c \ + src/core/ext/census/intrusive_hash_map.c \ src/core/ext/census/mlog.c \ src/core/ext/census/operation.c \ src/core/ext/census/placeholders.c \ @@ -8687,6 +8694,38 @@ endif endif +CENSUS_INTRUSIVE_HASH_MAP_TEST_SRC = \ + test/core/census/intrusive_hash_map_test.c \ + +CENSUS_INTRUSIVE_HASH_MAP_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CENSUS_INTRUSIVE_HASH_MAP_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/census_intrusive_hash_map_test: openssl_dep_error + +else + + + +$(BINDIR)/$(CONFIG)/census_intrusive_hash_map_test: $(CENSUS_INTRUSIVE_HASH_MAP_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) $(CENSUS_INTRUSIVE_HASH_MAP_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)/census_intrusive_hash_map_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/census/intrusive_hash_map_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + +deps_census_intrusive_hash_map_test: $(CENSUS_INTRUSIVE_HASH_MAP_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(CENSUS_INTRUSIVE_HASH_MAP_TEST_OBJS:.o=.dep) +endif +endif + + CENSUS_RESOURCE_TEST_SRC = \ test/core/census/resource_test.c \ diff --git a/binding.gyp b/binding.gyp index 8aafdaa62b..e617485134 100644 --- a/binding.gyp +++ b/binding.gyp @@ -890,6 +890,7 @@ 'src/core/ext/census/grpc_filter.c', 'src/core/ext/census/grpc_plugin.c', 'src/core/ext/census/initialize.c', + 'src/core/ext/census/intrusive_hash_map.c', 'src/core/ext/census/mlog.c', 'src/core/ext/census/operation.c', 'src/core/ext/census/placeholders.c', diff --git a/build.yaml b/build.yaml index 98401b84c5..d2ea16c8c2 100644 --- a/build.yaml +++ b/build.yaml @@ -27,6 +27,8 @@ filegroups: - src/core/ext/census/gen/census.pb.h - src/core/ext/census/gen/trace_context.pb.h - src/core/ext/census/grpc_filter.h + - src/core/ext/census/intrusive_hash_map.h + - src/core/ext/census/intrusive_hash_map_internal.h - src/core/ext/census/mlog.h - src/core/ext/census/resource.h - src/core/ext/census/rpc_metric_id.h @@ -45,6 +47,7 @@ filegroups: - src/core/ext/census/grpc_filter.c - src/core/ext/census/grpc_plugin.c - src/core/ext/census/initialize.c + - src/core/ext/census/intrusive_hash_map.c - src/core/ext/census/mlog.c - src/core/ext/census/operation.c - src/core/ext/census/placeholders.c @@ -1676,6 +1679,16 @@ targets: - grpc - gpr_test_util - gpr +- name: census_intrusive_hash_map_test + build: test + language: c + src: + - test/core/census/intrusive_hash_map_test.c + deps: + - grpc_test_util + - grpc + - gpr_test_util + - gpr - name: census_resource_test build: test language: c @@ -322,6 +322,7 @@ if test "$PHP_GRPC" != "no"; then src/core/ext/census/grpc_filter.c \ src/core/ext/census/grpc_plugin.c \ src/core/ext/census/initialize.c \ + src/core/ext/census/intrusive_hash_map.c \ src/core/ext/census/mlog.c \ src/core/ext/census/operation.c \ src/core/ext/census/placeholders.c \ diff --git a/config.w32 b/config.w32 index 0d82f9d757..7c407e848a 100644 --- a/config.w32 +++ b/config.w32 @@ -299,6 +299,7 @@ if (PHP_GRPC != "no") { "src\\core\\ext\\census\\grpc_filter.c " + "src\\core\\ext\\census\\grpc_plugin.c " + "src\\core\\ext\\census\\initialize.c " + + "src\\core\\ext\\census\\intrusive_hash_map.c " + "src\\core\\ext\\census\\mlog.c " + "src\\core\\ext\\census\\operation.c " + "src\\core\\ext\\census\\placeholders.c " + diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec index 1176a15f2b..d9de2d78a0 100644 --- a/gRPC-Core.podspec +++ b/gRPC-Core.podspec @@ -463,6 +463,8 @@ Pod::Spec.new do |s| 'src/core/ext/census/gen/census.pb.h', 'src/core/ext/census/gen/trace_context.pb.h', 'src/core/ext/census/grpc_filter.h', + 'src/core/ext/census/intrusive_hash_map.h', + 'src/core/ext/census/intrusive_hash_map_internal.h', 'src/core/ext/census/mlog.h', 'src/core/ext/census/resource.h', 'src/core/ext/census/rpc_metric_id.h', @@ -713,6 +715,7 @@ Pod::Spec.new do |s| 'src/core/ext/census/grpc_filter.c', 'src/core/ext/census/grpc_plugin.c', 'src/core/ext/census/initialize.c', + 'src/core/ext/census/intrusive_hash_map.c', 'src/core/ext/census/mlog.c', 'src/core/ext/census/operation.c', 'src/core/ext/census/placeholders.c', @@ -946,6 +949,8 @@ Pod::Spec.new do |s| 'src/core/ext/census/gen/census.pb.h', 'src/core/ext/census/gen/trace_context.pb.h', 'src/core/ext/census/grpc_filter.h', + 'src/core/ext/census/intrusive_hash_map.h', + 'src/core/ext/census/intrusive_hash_map_internal.h', 'src/core/ext/census/mlog.h', 'src/core/ext/census/resource.h', 'src/core/ext/census/rpc_metric_id.h', diff --git a/grpc.gemspec b/grpc.gemspec index 32c1164456..b334efb0b5 100755 --- a/grpc.gemspec +++ b/grpc.gemspec @@ -379,6 +379,8 @@ Gem::Specification.new do |s| s.files += %w( src/core/ext/census/gen/census.pb.h ) s.files += %w( src/core/ext/census/gen/trace_context.pb.h ) s.files += %w( src/core/ext/census/grpc_filter.h ) + s.files += %w( src/core/ext/census/intrusive_hash_map.h ) + s.files += %w( src/core/ext/census/intrusive_hash_map_internal.h ) s.files += %w( src/core/ext/census/mlog.h ) s.files += %w( src/core/ext/census/resource.h ) s.files += %w( src/core/ext/census/rpc_metric_id.h ) @@ -629,6 +631,7 @@ Gem::Specification.new do |s| s.files += %w( src/core/ext/census/grpc_filter.c ) s.files += %w( src/core/ext/census/grpc_plugin.c ) s.files += %w( src/core/ext/census/initialize.c ) + s.files += %w( src/core/ext/census/intrusive_hash_map.c ) s.files += %w( src/core/ext/census/mlog.c ) s.files += %w( src/core/ext/census/operation.c ) s.files += %w( src/core/ext/census/placeholders.c ) diff --git a/package.xml b/package.xml index 5bcffd6eed..9179ef238c 100644 --- a/package.xml +++ b/package.xml @@ -393,6 +393,8 @@ <file baseinstalldir="/" name="src/core/ext/census/gen/census.pb.h" role="src" /> <file baseinstalldir="/" name="src/core/ext/census/gen/trace_context.pb.h" role="src" /> <file baseinstalldir="/" name="src/core/ext/census/grpc_filter.h" role="src" /> + <file baseinstalldir="/" name="src/core/ext/census/intrusive_hash_map.h" role="src" /> + <file baseinstalldir="/" name="src/core/ext/census/intrusive_hash_map_internal.h" role="src" /> <file baseinstalldir="/" name="src/core/ext/census/mlog.h" role="src" /> <file baseinstalldir="/" name="src/core/ext/census/resource.h" role="src" /> <file baseinstalldir="/" name="src/core/ext/census/rpc_metric_id.h" role="src" /> @@ -643,6 +645,7 @@ <file baseinstalldir="/" name="src/core/ext/census/grpc_filter.c" role="src" /> <file baseinstalldir="/" name="src/core/ext/census/grpc_plugin.c" role="src" /> <file baseinstalldir="/" name="src/core/ext/census/initialize.c" role="src" /> + <file baseinstalldir="/" name="src/core/ext/census/intrusive_hash_map.c" role="src" /> <file baseinstalldir="/" name="src/core/ext/census/mlog.c" role="src" /> <file baseinstalldir="/" name="src/core/ext/census/operation.c" role="src" /> <file baseinstalldir="/" name="src/core/ext/census/placeholders.c" role="src" /> diff --git a/src/core/ext/census/intrusive_hash_map.c b/src/core/ext/census/intrusive_hash_map.c new file mode 100644 index 0000000000..e64e8086ca --- /dev/null +++ b/src/core/ext/census/intrusive_hash_map.c @@ -0,0 +1,302 @@ +/* + * Copyright 2017 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "src/core/ext/census/intrusive_hash_map.h" +#include <string.h> + +extern bool hm_index_compare(const hm_index *A, const hm_index *B); + +/* Simple hashing function that takes lower 32 bits. */ +static inline uint32_t chunked_vector_hasher(uint64_t key) { + return (uint32_t)key; +} + +/* Vector chunks are 1MiB divided by pointer size. */ +static const size_t VECTOR_CHUNK_SIZE = (1 << 20) / sizeof(void *); + +/* Helper functions which return buckets from the chunked vector. */ +static inline void **get_mutable_bucket(const chunked_vector *buckets, + uint32_t index) { + if (index < VECTOR_CHUNK_SIZE) { + return &buckets->first_[index]; + } + size_t rest_index = (index - VECTOR_CHUNK_SIZE) / VECTOR_CHUNK_SIZE; + return &buckets->rest_[rest_index][index % VECTOR_CHUNK_SIZE]; +} + +static inline void *get_bucket(const chunked_vector *buckets, uint32_t index) { + if (index < VECTOR_CHUNK_SIZE) { + return buckets->first_[index]; + } + size_t rest_index = (index - VECTOR_CHUNK_SIZE) / VECTOR_CHUNK_SIZE; + return buckets->rest_[rest_index][index % VECTOR_CHUNK_SIZE]; +} + +/* Helper function. */ +static inline size_t RestSize(const chunked_vector *vec) { + return (vec->size_ <= VECTOR_CHUNK_SIZE) + ? 0 + : (vec->size_ - VECTOR_CHUNK_SIZE - 1) / VECTOR_CHUNK_SIZE + 1; +} + +/* Initialize chunked vector to size of 0. */ +static void chunked_vector_init(chunked_vector *vec) { + vec->size_ = 0; + vec->first_ = NULL; + vec->rest_ = NULL; +} + +/* Clear chunked vector and free all memory that has been allocated then + initialize chunked vector. */ +static void chunked_vector_clear(chunked_vector *vec) { + if (vec->first_ != NULL) { + gpr_free(vec->first_); + } + if (vec->rest_ != NULL) { + size_t rest_size = RestSize(vec); + for (size_t i = 0; i < rest_size; ++i) { + if (vec->rest_[i] != NULL) { + gpr_free(vec->rest_[i]); + } + } + gpr_free(vec->rest_); + } + chunked_vector_init(vec); +} + +/* Clear chunked vector and then resize it to n entries. Allow the first 1MB to + be read w/o an extra cache miss. The rest of the elements are stored in an + array of arrays to avoid large mallocs. */ +static void chunked_vector_reset(chunked_vector *vec, size_t n) { + chunked_vector_clear(vec); + vec->size_ = n; + if (n <= VECTOR_CHUNK_SIZE) { + vec->first_ = (void **)gpr_malloc(sizeof(void *) * n); + memset(vec->first_, 0, sizeof(void *) * n); + } else { + vec->first_ = (void **)gpr_malloc(sizeof(void *) * VECTOR_CHUNK_SIZE); + memset(vec->first_, 0, sizeof(void *) * VECTOR_CHUNK_SIZE); + size_t rest_size = RestSize(vec); + vec->rest_ = (void ***)gpr_malloc(sizeof(void **) * rest_size); + memset(vec->rest_, 0, sizeof(void **) * rest_size); + int i = 0; + n -= VECTOR_CHUNK_SIZE; + while (n > 0) { + size_t this_size = GPR_MIN(n, VECTOR_CHUNK_SIZE); + vec->rest_[i] = (void **)gpr_malloc(sizeof(void *) * this_size); + memset(vec->rest_[i], 0, sizeof(void *) * this_size); + n -= this_size; + ++i; + } + } +} + +void intrusive_hash_map_init(intrusive_hash_map *hash_map, + uint32_t initial_log2_table_size) { + hash_map->log2_num_buckets = initial_log2_table_size; + hash_map->num_items = 0; + uint32_t num_buckets = (uint32_t)1 << hash_map->log2_num_buckets; + hash_map->extend_threshold = num_buckets >> 1; + chunked_vector_init(&hash_map->buckets); + chunked_vector_reset(&hash_map->buckets, num_buckets); + hash_map->hash_mask = num_buckets - 1; +} + +bool intrusive_hash_map_empty(const intrusive_hash_map *hash_map) { + return hash_map->num_items == 0; +} + +size_t intrusive_hash_map_size(const intrusive_hash_map *hash_map) { + return hash_map->num_items; +} + +void intrusive_hash_map_end(const intrusive_hash_map *hash_map, hm_index *idx) { + idx->bucket_index = (uint32_t)hash_map->buckets.size_; + GPR_ASSERT(idx->bucket_index <= UINT32_MAX); + idx->item = NULL; +} + +void intrusive_hash_map_next(const intrusive_hash_map *hash_map, + hm_index *idx) { + idx->item = idx->item->hash_link; + while (idx->item == NULL) { + idx->bucket_index++; + if (idx->bucket_index >= hash_map->buckets.size_) { + /* Reached end of table. */ + idx->item = NULL; + return; + } + idx->item = (hm_item *)get_bucket(&hash_map->buckets, idx->bucket_index); + } +} + +void intrusive_hash_map_begin(const intrusive_hash_map *hash_map, + hm_index *idx) { + for (uint32_t i = 0; i < hash_map->buckets.size_; ++i) { + if (get_bucket(&hash_map->buckets, i) != NULL) { + idx->bucket_index = i; + idx->item = (hm_item *)get_bucket(&hash_map->buckets, i); + return; + } + } + intrusive_hash_map_end(hash_map, idx); +} + +hm_item *intrusive_hash_map_find(const intrusive_hash_map *hash_map, + uint64_t key) { + uint32_t index = chunked_vector_hasher(key) & hash_map->hash_mask; + + hm_item *p = (hm_item *)get_bucket(&hash_map->buckets, index); + while (p != NULL) { + if (key == p->key) { + return p; + } + p = p->hash_link; + } + return NULL; +} + +hm_item *intrusive_hash_map_erase(intrusive_hash_map *hash_map, uint64_t key) { + uint32_t index = chunked_vector_hasher(key) & hash_map->hash_mask; + + hm_item **slot = (hm_item **)get_mutable_bucket(&hash_map->buckets, index); + hm_item *p = *slot; + if (p == NULL) { + return NULL; + } + + if (key == p->key) { + *slot = p->hash_link; + p->hash_link = NULL; + hash_map->num_items--; + return p; + } + + hm_item *prev = p; + p = p->hash_link; + + while (p) { + if (key == p->key) { + prev->hash_link = p->hash_link; + p->hash_link = NULL; + hash_map->num_items--; + return p; + } + prev = p; + p = p->hash_link; + } + return NULL; +} + +/* Insert an hm_item* into the underlying chunked vector. hash_mask is + * array_size-1. Returns true if it is a new hm_item and false if the hm_item + * already existed. + */ +static inline bool intrusive_hash_map_internal_insert(chunked_vector *buckets, + uint32_t hash_mask, + hm_item *item) { + const uint64_t key = item->key; + uint32_t index = chunked_vector_hasher(key) & hash_mask; + hm_item **slot = (hm_item **)get_mutable_bucket(buckets, index); + hm_item *p = *slot; + item->hash_link = p; + + /* Check to see if key already exists. */ + while (p) { + if (p->key == key) { + return false; + } + p = p->hash_link; + } + + /* Otherwise add new entry. */ + *slot = item; + return true; +} + +/* Extend the allocated number of elements in the hash map by a factor of 2. */ +void intrusive_hash_map_extend(intrusive_hash_map *hash_map) { + uint32_t new_log2_num_buckets = 1 + hash_map->log2_num_buckets; + uint32_t new_num_buckets = (uint32_t)1 << new_log2_num_buckets; + GPR_ASSERT(new_num_buckets <= UINT32_MAX && new_num_buckets > 0); + chunked_vector new_buckets; + chunked_vector_init(&new_buckets); + chunked_vector_reset(&new_buckets, new_num_buckets); + uint32_t new_hash_mask = new_num_buckets - 1; + + hm_index cur_idx; + hm_index end_idx; + intrusive_hash_map_end(hash_map, &end_idx); + intrusive_hash_map_begin(hash_map, &cur_idx); + while (!hm_index_compare(&cur_idx, &end_idx)) { + hm_item *new_item = cur_idx.item; + intrusive_hash_map_next(hash_map, &cur_idx); + intrusive_hash_map_internal_insert(&new_buckets, new_hash_mask, new_item); + } + + /* Set values for new chunked_vector. extend_threshold is set to half of + * new_num_buckets. */ + hash_map->log2_num_buckets = new_log2_num_buckets; + chunked_vector_clear(&hash_map->buckets); + hash_map->buckets = new_buckets; + hash_map->hash_mask = new_hash_mask; + hash_map->extend_threshold = new_num_buckets >> 1; +} + +/* Insert a hm_item. The hm_item must remain live until it is removed from the + table. This object does not take the ownership of hm_item. The caller must + remove this hm_item from the table and delete it before this table is + deleted. If hm_item exists already num_items is not changed. */ +bool intrusive_hash_map_insert(intrusive_hash_map *hash_map, hm_item *item) { + if (hash_map->num_items >= hash_map->extend_threshold) { + intrusive_hash_map_extend(hash_map); + } + if (intrusive_hash_map_internal_insert(&hash_map->buckets, + hash_map->hash_mask, item)) { + hash_map->num_items++; + return true; + } + return false; +} + +void intrusive_hash_map_clear(intrusive_hash_map *hash_map, + void (*free_object)(void *)) { + hm_index cur; + hm_index end; + intrusive_hash_map_end(hash_map, &end); + intrusive_hash_map_begin(hash_map, &cur); + + while (!hm_index_compare(&cur, &end)) { + hm_index next = cur; + intrusive_hash_map_next(hash_map, &next); + if (cur.item != NULL) { + hm_item *item = intrusive_hash_map_erase(hash_map, cur.item->key); + (*free_object)((void *)item); + gpr_free(item); + } + cur = next; + } +} + +void intrusive_hash_map_free(intrusive_hash_map *hash_map, + void (*free_object)(void *)) { + intrusive_hash_map_clear(hash_map, (*free_object)); + hash_map->num_items = 0; + hash_map->extend_threshold = 0; + hash_map->log2_num_buckets = 0; + hash_map->hash_mask = 0; + chunked_vector_clear(&hash_map->buckets); +} diff --git a/src/core/ext/census/intrusive_hash_map.h b/src/core/ext/census/intrusive_hash_map.h new file mode 100644 index 0000000000..9a7fd07f20 --- /dev/null +++ b/src/core/ext/census/intrusive_hash_map.h @@ -0,0 +1,150 @@ +/* + * Copyright 2017 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GRPC_CORE_EXT_CENSUS_INTRUSIVE_HASH_MAP_H +#define GRPC_CORE_EXT_CENSUS_INTRUSIVE_HASH_MAP_H + +#include "src/core/ext/census/intrusive_hash_map_internal.h" + +/* intrusive_hash_map is a fast chained hash table. This hash map is faster than + * a dense hash map when the application calls insert and erase more often than + * find. When the workload is dominated by find() a dense hash map may be + * faster. + * + * intrusive_hash_map uses an intrusive header placed within a user defined + * struct. The header field IHM_key MUST be set to a valid value before + * insertion into the hash map or undefined behavior may occur. The header field + * IHM_hash_link MUST to be set to NULL initially. + * + * EXAMPLE USAGE: + * + * typedef struct string_item { + * INTRUSIVE_HASH_MAP_HEADER; + * // User data. + * char *str_buf; + * uint16_t len; + * } string_item; + * + * static string_item *make_string_item(uint64_t key, const char *buf, + * uint16_t len) { + * string_item *item = (string_item *)gpr_malloc(sizeof(string_item)); + * item->IHM_key = key; + * item->IHM_hash_link = NULL; + * item->len = len; + * item->str_buf = (char *)malloc(len); + * memcpy(item->str_buf, buf, len); + * return item; + * } + * + * intrusive_hash_map hash_map; + * intrusive_hash_map_init(&hash_map, 4); + * string_item *new_item1 = make_string_item(10, "test1", 5); + * bool ok = intrusive_hash_map_insert(&hash_map, (hm_item *)new_item1); + * + * string_item *item1 = + * (string_item *)intrusive_hash_map_find(&hash_map, 10); + */ + +/* Hash map item. Stores key and a pointer to the actual object. A user defined + * version of this can be passed in provided the first 2 entries (key and + * hash_link) are the same. These entries must be first in the user defined + * struct. Pointer to struct will need to be cast as (hm_item *) when passed to + * hash map. This allows it to be intrusive. */ +typedef struct hm_item { + uint64_t key; + struct hm_item *hash_link; + /* Optional user defined data after this. */ +} hm_item; + +/* Macro provided for ease of use. This must be first in the user defined + * struct (i.e. uint64_t key and hm_item * must be the first two elements in + * that order). */ +#define INTRUSIVE_HASH_MAP_HEADER \ + uint64_t IHM_key; \ + struct hm_item *IHM_hash_link + +/* Index struct which acts as a pseudo-iterator within the hash map. */ +typedef struct hm_index { + uint32_t bucket_index; // hash map bucket index. + hm_item *item; // Pointer to hm_item within the hash map. +} hm_index; + +/* Returns true if two hm_indices point to the same object within the hash map + * and false otherwise. */ +inline bool hm_index_compare(const hm_index *A, const hm_index *B) { + return (A->item == B->item && A->bucket_index == B->bucket_index); +} + +/* + * Helper functions for iterating over the hash map. + */ + +/* On return idx will contain an invalid index which is always equal to + * hash_map->buckets.size_ */ +void intrusive_hash_map_end(const intrusive_hash_map *hash_map, hm_index *idx); + +/* Iterates index to the next valid entry in the hash map and stores the + * index within idx. If end of table is reached, idx will contain the same + * values as if intrusive_hash_map_end() was called. */ +void intrusive_hash_map_next(const intrusive_hash_map *hash_map, hm_index *idx); + +/* On return, idx will contain the index of the first non-null entry in the hash + * map. If the hash map is empty, idx will contain the same values as if + * intrusive_hash_map_end() was called. */ +void intrusive_hash_map_begin(const intrusive_hash_map *hash_map, + hm_index *idx); + +/* Initialize intrusive hash map data structure. This must be called before + * the hash map can be used. The initial size of an intrusive hash map will be + * 2^initial_log2_map_size (valid range is [0, 31]). */ +void intrusive_hash_map_init(intrusive_hash_map *hash_map, + uint32_t initial_log2_map_size); + +/* Returns true if the hash map is empty and false otherwise. */ +bool intrusive_hash_map_empty(const intrusive_hash_map *hash_map); + +/* Returns the number of elements currently in the hash map. */ +size_t intrusive_hash_map_size(const intrusive_hash_map *hash_map); + +/* Find a hm_item within the hash map by key. Returns NULL if item was not + * found. */ +hm_item *intrusive_hash_map_find(const intrusive_hash_map *hash_map, + uint64_t key); + +/* Erase the hm_item that corresponds with key. If the hm_item is found, return + * the pointer to the hm_item. Else returns NULL. */ +hm_item *intrusive_hash_map_erase(intrusive_hash_map *hash_map, uint64_t key); + +/* Attempts to insert a new hm_item into the hash map. If an element with the + * same key already exists, it will not insert the new item and return false. + * Otherwise, it will insert the new item and return true. */ +bool intrusive_hash_map_insert(intrusive_hash_map *hash_map, hm_item *item); + +/* Clears entire contents of the hash map, but leaves internal data structure + * untouched. Second argument takes a function pointer to a function that will + * free the object designated by the user and pointed to by hash_map->value. */ +void intrusive_hash_map_clear(intrusive_hash_map *hash_map, + void (*free_object)(void *)); + +/* Erase all contents of hash map and free the memory. Hash map is invalid + * after calling this function and cannot be used until it has been + * reinitialized (intrusive_hash_map_init()). This function takes a function + * pointer to a function that will free the object designated by the user and + * pointed to by hash_map->value. */ +void intrusive_hash_map_free(intrusive_hash_map *hash_map, + void (*free_object)(void *)); + +#endif // GRPC_CORE_EXT_CENSUS_INTRUSIVE_HASH_MAP_H diff --git a/src/core/ext/census/intrusive_hash_map_internal.h b/src/core/ext/census/intrusive_hash_map_internal.h new file mode 100644 index 0000000000..c0a3c6f59a --- /dev/null +++ b/src/core/ext/census/intrusive_hash_map_internal.h @@ -0,0 +1,46 @@ +/* + * Copyright 2017 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GRPC_CORE_EXT_CENSUS_INTRUSIVE_HASH_MAP_INTERNAL_H +#define GRPC_CORE_EXT_CENSUS_INTRUSIVE_HASH_MAP_INTERNAL_H + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/useful.h> +#include <stdbool.h> + +/* The chunked vector is a data structure that allocates buckets for use in the + * hash map. ChunkedVector is logically equivalent to T*[N] (cast void* as + * T*). It's internally implemented as an array of 1MB arrays to avoid + * allocating large consecutive memory chunks. This is an internal data + * structure that should never be accessed directly. */ +typedef struct chunked_vector { + size_t size_; + void **first_; + void ***rest_; +} chunked_vector; + +/* Core intrusive hash map data structure. All internal elements are managed by + * functions and should not be altered manually. */ +typedef struct intrusive_hash_map { + uint32_t num_items; + uint32_t extend_threshold; + uint32_t log2_num_buckets; + uint32_t hash_mask; + chunked_vector buckets; +} intrusive_hash_map; + +#endif // GRPC_CORE_EXT_CENSUS_INTRUSIVE_HASH_MAP_INTERNAL_H diff --git a/src/core/lib/http/httpcli.c b/src/core/lib/http/httpcli.c index c3421e1b55..f5588a9a76 100644 --- a/src/core/lib/http/httpcli.c +++ b/src/core/lib/http/httpcli.c @@ -245,7 +245,7 @@ static void next_address(grpc_exec_ctx *exec_ctx, internal_request *req, static void on_resolved(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { internal_request *req = arg; if (error != GRPC_ERROR_NONE) { - finish(exec_ctx, req, error); + finish(exec_ctx, req, GRPC_ERROR_REF(error)); return; } req->next_address = 0; diff --git a/src/core/lib/http/httpcli_security_connector.c b/src/core/lib/http/httpcli_security_connector.c index 76946434f0..ea7c1122c1 100644 --- a/src/core/lib/http/httpcli_security_connector.c +++ b/src/core/lib/http/httpcli_security_connector.c @@ -44,6 +44,7 @@ #include "src/core/lib/slice/slice_internal.h" #include "src/core/lib/support/string.h" #include "src/core/tsi/ssl_transport_security.h" +#include "src/core/tsi/transport_security_adapter.h" typedef struct { grpc_channel_security_connector base; @@ -78,7 +79,8 @@ static void httpcli_ssl_add_handshakers(grpc_exec_ctx *exec_ctx, } grpc_handshake_manager_add( handshake_mgr, - grpc_security_handshaker_create(exec_ctx, handshaker, &sc->base)); + grpc_security_handshaker_create( + exec_ctx, tsi_create_adapter_handshaker(handshaker), &sc->base)); } static void httpcli_ssl_check_peer(grpc_exec_ctx *exec_ctx, diff --git a/src/core/lib/security/transport/security_connector.c b/src/core/lib/security/transport/security_connector.c index 30431a4e4a..416a3bdb35 100644 --- a/src/core/lib/security/transport/security_connector.c +++ b/src/core/lib/security/transport/security_connector.c @@ -56,6 +56,7 @@ #include "src/core/lib/support/string.h" #include "src/core/tsi/fake_transport_security.h" #include "src/core/tsi/ssl_transport_security.h" +#include "src/core/tsi/transport_security_adapter.h" /* -- Constants. -- */ @@ -390,7 +391,8 @@ static void fake_channel_add_handshakers( grpc_handshake_manager_add( handshake_mgr, grpc_security_handshaker_create( - exec_ctx, tsi_create_fake_handshaker(true /* is_client */), + exec_ctx, tsi_create_adapter_handshaker( + tsi_create_fake_handshaker(true /* is_client */)), &sc->base)); } @@ -400,7 +402,8 @@ static void fake_server_add_handshakers(grpc_exec_ctx *exec_ctx, grpc_handshake_manager_add( handshake_mgr, grpc_security_handshaker_create( - exec_ctx, tsi_create_fake_handshaker(false /* is_client */), + exec_ctx, tsi_create_adapter_handshaker( + tsi_create_fake_handshaker(false /* is_client */)), &sc->base)); } @@ -495,8 +498,10 @@ static void ssl_channel_add_handshakers(grpc_exec_ctx *exec_ctx, } // Create handshakers. - grpc_handshake_manager_add(handshake_mgr, grpc_security_handshaker_create( - exec_ctx, tsi_hs, &sc->base)); + grpc_handshake_manager_add( + handshake_mgr, + grpc_security_handshaker_create( + exec_ctx, tsi_create_adapter_handshaker(tsi_hs), &sc->base)); } static void ssl_server_add_handshakers(grpc_exec_ctx *exec_ctx, @@ -515,8 +520,10 @@ static void ssl_server_add_handshakers(grpc_exec_ctx *exec_ctx, } // Create handshakers. - grpc_handshake_manager_add(handshake_mgr, grpc_security_handshaker_create( - exec_ctx, tsi_hs, &sc->base)); + grpc_handshake_manager_add( + handshake_mgr, + grpc_security_handshaker_create( + exec_ctx, tsi_create_adapter_handshaker(tsi_hs), &sc->base)); } static int ssl_host_matches_name(const tsi_peer *peer, const char *peer_name) { diff --git a/src/core/lib/security/transport/security_handshaker.c b/src/core/lib/security/transport/security_handshaker.c index 509b4b556d..3bc113e20f 100644 --- a/src/core/lib/security/transport/security_handshaker.c +++ b/src/core/lib/security/transport/security_handshaker.c @@ -71,12 +71,12 @@ typedef struct { unsigned char *handshake_buffer; size_t handshake_buffer_size; - grpc_slice_buffer left_overs; grpc_slice_buffer outgoing; grpc_closure on_handshake_data_sent_to_peer; grpc_closure on_handshake_data_received_from_peer; grpc_closure on_peer_checked; grpc_auth_context *auth_context; + tsi_handshaker_result *handshaker_result; } security_handshaker; static void security_handshaker_unref(grpc_exec_ctx *exec_ctx, @@ -84,6 +84,7 @@ static void security_handshaker_unref(grpc_exec_ctx *exec_ctx, if (gpr_unref(&h->refs)) { gpr_mu_destroy(&h->mu); tsi_handshaker_destroy(h->handshaker); + tsi_handshaker_result_destroy(h->handshaker_result); if (h->endpoint_to_destroy != NULL) { grpc_endpoint_destroy(exec_ctx, h->endpoint_to_destroy); } @@ -92,7 +93,6 @@ static void security_handshaker_unref(grpc_exec_ctx *exec_ctx, gpr_free(h->read_buffer_to_destroy); } gpr_free(h->handshake_buffer); - grpc_slice_buffer_destroy_internal(exec_ctx, &h->left_overs); grpc_slice_buffer_destroy_internal(exec_ctx, &h->outgoing); GRPC_AUTH_CONTEXT_UNREF(h->auth_context, "handshake"); GRPC_SECURITY_CONNECTOR_UNREF(exec_ctx, h->connector, "handshake"); @@ -150,10 +150,10 @@ static void on_peer_checked(grpc_exec_ctx *exec_ctx, void *arg, security_handshake_failed_locked(exec_ctx, h, GRPC_ERROR_REF(error)); goto done; } - // Get frame protector. + // Create frame protector. tsi_frame_protector *protector; - tsi_result result = - tsi_handshaker_create_frame_protector(h->handshaker, NULL, &protector); + tsi_result result = tsi_handshaker_result_create_frame_protector( + h->handshaker_result, NULL, &protector); if (result != TSI_OK) { error = grpc_set_tsi_error_result( GRPC_ERROR_CREATE_FROM_STATIC_STRING("Frame protector creation failed"), @@ -161,14 +161,25 @@ static void on_peer_checked(grpc_exec_ctx *exec_ctx, void *arg, security_handshake_failed_locked(exec_ctx, h, error); goto done; } - // Success. + // Get unused bytes. + unsigned char *unused_bytes = NULL; + size_t unused_bytes_size = 0; + result = tsi_handshaker_result_get_unused_bytes( + h->handshaker_result, &unused_bytes, &unused_bytes_size); // Create secure endpoint. - h->args->endpoint = grpc_secure_endpoint_create( - protector, h->args->endpoint, h->left_overs.slices, h->left_overs.count); - h->left_overs.count = 0; - h->left_overs.length = 0; - // Clear out the read buffer before it gets passed to the transport, - // since any excess bytes were already copied to h->left_overs. + if (unused_bytes_size > 0) { + grpc_slice slice = + grpc_slice_from_copied_buffer((char *)unused_bytes, unused_bytes_size); + h->args->endpoint = + grpc_secure_endpoint_create(protector, h->args->endpoint, &slice, 1); + grpc_slice_unref_internal(exec_ctx, slice); + } else { + h->args->endpoint = + grpc_secure_endpoint_create(protector, h->args->endpoint, NULL, 0); + } + tsi_handshaker_result_destroy(h->handshaker_result); + h->handshaker_result = NULL; + // Clear out the read buffer before it gets passed to the transport. grpc_slice_buffer_reset_and_unref_internal(exec_ctx, h->args->read_buffer); // Add auth context to channel args. grpc_arg auth_context_arg = grpc_auth_context_to_arg(h->auth_context); @@ -189,7 +200,8 @@ done: static grpc_error *check_peer_locked(grpc_exec_ctx *exec_ctx, security_handshaker *h) { tsi_peer peer; - tsi_result result = tsi_handshaker_extract_peer(h->handshaker, &peer); + tsi_result result = + tsi_handshaker_result_extract_peer(h->handshaker_result, &peer); if (result != TSI_OK) { return grpc_set_tsi_error_result( GRPC_ERROR_CREATE_FROM_STATIC_STRING("Peer extraction failed"), result); @@ -199,34 +211,87 @@ static grpc_error *check_peer_locked(grpc_exec_ctx *exec_ctx, return GRPC_ERROR_NONE; } -static grpc_error *send_handshake_bytes_to_peer_locked(grpc_exec_ctx *exec_ctx, - security_handshaker *h) { - // Get data to send. - tsi_result result = TSI_OK; - size_t offset = 0; - do { - size_t to_send_size = h->handshake_buffer_size - offset; - result = tsi_handshaker_get_bytes_to_send_to_peer( - h->handshaker, h->handshake_buffer + offset, &to_send_size); - offset += to_send_size; - if (result == TSI_INCOMPLETE_DATA) { - h->handshake_buffer_size *= 2; - h->handshake_buffer = - gpr_realloc(h->handshake_buffer, h->handshake_buffer_size); - } - } while (result == TSI_INCOMPLETE_DATA); +static grpc_error *on_handshake_next_done_locked( + grpc_exec_ctx *exec_ctx, security_handshaker *h, tsi_result result, + const unsigned char *bytes_to_send, size_t bytes_to_send_size, + tsi_handshaker_result *handshaker_result) { + grpc_error *error = GRPC_ERROR_NONE; + // Read more if we need to. + if (result == TSI_INCOMPLETE_DATA) { + GPR_ASSERT(bytes_to_send_size == 0); + grpc_endpoint_read(exec_ctx, h->args->endpoint, h->args->read_buffer, + &h->on_handshake_data_received_from_peer); + return error; + } if (result != TSI_OK) { return grpc_set_tsi_error_result( GRPC_ERROR_CREATE_FROM_STATIC_STRING("Handshake failed"), result); } - // Send data. - grpc_slice to_send = - grpc_slice_from_copied_buffer((const char *)h->handshake_buffer, offset); - grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &h->outgoing); - grpc_slice_buffer_add(&h->outgoing, to_send); - grpc_endpoint_write(exec_ctx, h->args->endpoint, &h->outgoing, - &h->on_handshake_data_sent_to_peer); - return GRPC_ERROR_NONE; + // Update handshaker result. + if (handshaker_result != NULL) { + GPR_ASSERT(h->handshaker_result == NULL); + h->handshaker_result = handshaker_result; + } + if (bytes_to_send_size > 0) { + // Send data to peer, if needed. + grpc_slice to_send = grpc_slice_from_copied_buffer( + (const char *)bytes_to_send, bytes_to_send_size); + grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &h->outgoing); + grpc_slice_buffer_add(&h->outgoing, to_send); + grpc_endpoint_write(exec_ctx, h->args->endpoint, &h->outgoing, + &h->on_handshake_data_sent_to_peer); + } else if (handshaker_result == NULL) { + // There is nothing to send, but need to read from peer. + grpc_endpoint_read(exec_ctx, h->args->endpoint, h->args->read_buffer, + &h->on_handshake_data_received_from_peer); + } else { + // Handshake has finished, check peer and so on. + error = check_peer_locked(exec_ctx, h); + } + return error; +} + +static void on_handshake_next_done_grpc_wrapper( + tsi_result result, void *user_data, const unsigned char *bytes_to_send, + size_t bytes_to_send_size, tsi_handshaker_result *handshaker_result) { + security_handshaker *h = user_data; + // This callback will be invoked by TSI in a non-grpc thread, so it's + // safe to create our own exec_ctx here. + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + gpr_mu_lock(&h->mu); + grpc_error *error = + on_handshake_next_done_locked(&exec_ctx, h, result, bytes_to_send, + bytes_to_send_size, handshaker_result); + if (error != GRPC_ERROR_NONE) { + security_handshake_failed_locked(&exec_ctx, h, error); + gpr_mu_unlock(&h->mu); + security_handshaker_unref(&exec_ctx, h); + } else { + gpr_mu_unlock(&h->mu); + } + grpc_exec_ctx_finish(&exec_ctx); +} + +static grpc_error *do_handshaker_next_locked( + grpc_exec_ctx *exec_ctx, security_handshaker *h, + const unsigned char *bytes_received, size_t bytes_received_size) { + // Invoke TSI handshaker. + unsigned char *bytes_to_send = NULL; + size_t bytes_to_send_size = 0; + tsi_handshaker_result *handshaker_result = NULL; + tsi_result result = tsi_handshaker_next( + h->handshaker, bytes_received, bytes_received_size, &bytes_to_send, + &bytes_to_send_size, &handshaker_result, + &on_handshake_next_done_grpc_wrapper, h); + if (result == TSI_ASYNC) { + // Handshaker operating asynchronously. Nothing else to do here; + // callback will be invoked in a TSI thread. + return GRPC_ERROR_NONE; + } + // Handshaker returned synchronously. Invoke callback directly in + // this thread with our existing exec_ctx. + return on_handshake_next_done_locked(exec_ctx, h, result, bytes_to_send, + bytes_to_send_size, handshaker_result); } static void on_handshake_data_received_from_peer(grpc_exec_ctx *exec_ctx, @@ -241,72 +306,34 @@ static void on_handshake_data_received_from_peer(grpc_exec_ctx *exec_ctx, security_handshaker_unref(exec_ctx, h); return; } - // Process received data. - tsi_result result = TSI_OK; - size_t consumed_slice_size = 0; + // Copy all slices received. size_t i; + size_t bytes_received_size = 0; for (i = 0; i < h->args->read_buffer->count; i++) { - consumed_slice_size = GRPC_SLICE_LENGTH(h->args->read_buffer->slices[i]); - result = tsi_handshaker_process_bytes_from_peer( - h->handshaker, GRPC_SLICE_START_PTR(h->args->read_buffer->slices[i]), - &consumed_slice_size); - if (!tsi_handshaker_is_in_progress(h->handshaker)) break; + bytes_received_size += GRPC_SLICE_LENGTH(h->args->read_buffer->slices[i]); } - if (tsi_handshaker_is_in_progress(h->handshaker)) { - /* We may need more data. */ - if (result == TSI_INCOMPLETE_DATA) { - grpc_endpoint_read(exec_ctx, h->args->endpoint, h->args->read_buffer, - &h->on_handshake_data_received_from_peer); - goto done; - } else { - error = send_handshake_bytes_to_peer_locked(exec_ctx, h); - if (error != GRPC_ERROR_NONE) { - security_handshake_failed_locked(exec_ctx, h, error); - gpr_mu_unlock(&h->mu); - security_handshaker_unref(exec_ctx, h); - return; - } - goto done; - } + if (bytes_received_size > h->handshake_buffer_size) { + h->handshake_buffer = gpr_realloc(h->handshake_buffer, bytes_received_size); + h->handshake_buffer_size = bytes_received_size; } - if (result != TSI_OK) { - security_handshake_failed_locked( - exec_ctx, h, - grpc_set_tsi_error_result( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Handshake failed"), result)); - gpr_mu_unlock(&h->mu); - security_handshaker_unref(exec_ctx, h); - return; - } - /* Handshake is done and successful this point. */ - bool has_left_overs_in_current_slice = - (consumed_slice_size < - GRPC_SLICE_LENGTH(h->args->read_buffer->slices[i])); - size_t num_left_overs = (has_left_overs_in_current_slice ? 1 : 0) + - h->args->read_buffer->count - i - 1; - if (num_left_overs > 0) { - /* Put the leftovers in our buffer (ownership transfered). */ - if (has_left_overs_in_current_slice) { - grpc_slice tail = grpc_slice_split_tail(&h->args->read_buffer->slices[i], - consumed_slice_size); - grpc_slice_buffer_add(&h->left_overs, tail); - /* split_tail above increments refcount. */ - grpc_slice_unref_internal(exec_ctx, tail); - } - grpc_slice_buffer_addn( - &h->left_overs, &h->args->read_buffer->slices[i + 1], - num_left_overs - (size_t)has_left_overs_in_current_slice); + size_t offset = 0; + for (i = 0; i < h->args->read_buffer->count; i++) { + size_t slice_size = GPR_SLICE_LENGTH(h->args->read_buffer->slices[i]); + memcpy(h->handshake_buffer + offset, + GRPC_SLICE_START_PTR(h->args->read_buffer->slices[i]), slice_size); + offset += slice_size; } - // Check peer. - error = check_peer_locked(exec_ctx, h); + // Call TSI handshaker. + error = do_handshaker_next_locked(exec_ctx, h, h->handshake_buffer, + bytes_received_size); + if (error != GRPC_ERROR_NONE) { security_handshake_failed_locked(exec_ctx, h, error); gpr_mu_unlock(&h->mu); security_handshaker_unref(exec_ctx, h); - return; + } else { + gpr_mu_unlock(&h->mu); } -done: - gpr_mu_unlock(&h->mu); } static void on_handshake_data_sent_to_peer(grpc_exec_ctx *exec_ctx, void *arg, @@ -321,8 +348,8 @@ static void on_handshake_data_sent_to_peer(grpc_exec_ctx *exec_ctx, void *arg, security_handshaker_unref(exec_ctx, h); return; } - /* We may be done. */ - if (tsi_handshaker_is_in_progress(h->handshaker)) { + // We may be done. + if (h->handshaker_result == NULL) { grpc_endpoint_read(exec_ctx, h->args->endpoint, h->args->read_buffer, &h->on_handshake_data_received_from_peer); } else { @@ -371,7 +398,7 @@ static void security_handshaker_do_handshake(grpc_exec_ctx *exec_ctx, h->args = args; h->on_handshake_done = on_handshake_done; gpr_ref(&h->refs); - grpc_error *error = send_handshake_bytes_to_peer_locked(exec_ctx, h); + grpc_error *error = do_handshaker_next_locked(exec_ctx, h, NULL, 0); if (error != GRPC_ERROR_NONE) { security_handshake_failed_locked(exec_ctx, h, error); gpr_mu_unlock(&h->mu); @@ -404,7 +431,6 @@ static grpc_handshaker *security_handshaker_create( grpc_schedule_on_exec_ctx); grpc_closure_init(&h->on_peer_checked, on_peer_checked, h, grpc_schedule_on_exec_ctx); - grpc_slice_buffer_init(&h->left_overs); grpc_slice_buffer_init(&h->outgoing); return &h->base; } diff --git a/src/node/README.md b/src/node/README.md index 4b906643bc..3b98b97879 100644 --- a/src/node/README.md +++ b/src/node/README.md @@ -2,9 +2,9 @@ # Node.js gRPC Library ## PREREQUISITES -- `node`: This requires `node` to be installed, version `0.12` or above. If you instead have the `nodejs` executable on Debian, you should install the [`nodejs-legacy`](https://packages.debian.org/sid/nodejs-legacy) package. +- `node`: This requires `node` to be installed, version `4.0` or above. If you instead have the `nodejs` executable on Debian, you should install the [`nodejs-legacy`](https://packages.debian.org/sid/nodejs-legacy) package. -- **Note:** If you installed `node` via a package manager and the version is still less than `0.12`, try directly installing it from [nodejs.org](https://nodejs.org). +- **Note:** If you installed `node` via a package manager and the version is still less than `4.0`, try directly installing it from [nodejs.org](https://nodejs.org). ## INSTALLATION @@ -16,7 +16,7 @@ npm install grpc ## BUILD FROM SOURCE 1. Clone [the grpc Git Repository](https://github.com/grpc/grpc). - 2. Run `npm install` from the repository root. + 2. Run `npm install --build-from-source` from the repository root. - **Note:** On Windows, this might fail due to [nodejs issue #4932](https://github.com/nodejs/node/issues/4932) in which case, you will see something like the following in `npm install`'s output (towards the very beginning): @@ -34,61 +34,3 @@ npm install grpc ## TESTING To run the test suite, simply run `npm test` in the install location. - -## API -This library internally uses [ProtoBuf.js](https://github.com/dcodeIO/ProtoBuf.js), and some structures it exports match those exported by that library. - -If you require this module, you will get an object with the following members - -```javascript -function load(filename) -``` - -Takes a filename of a [Protocol Buffer](https://developers.google.com/protocol-buffers/) file, and returns an object representing the structure of the protocol buffer in the following way: - - - Namespaces become maps from the names of their direct members to those member objects - - Service definitions become client constructors for clients for that service. They also have a `service` member that can be used for constructing servers. - - Message definitions become Message constructors like those that ProtoBuf.js would create - - Enum definitions become Enum objects like those that ProtoBuf.js would create - - Anything else becomes the relevant reflection object that ProtoBuf.js would create - - -```javascript -function loadObject(reflectionObject) -``` - -Returns the same structure that `load` returns, but takes a reflection object from `ProtoBuf.js` instead of a file name. - -```javascript -function Server([serverOptions]) -``` - -Constructs a server to which service/implementation pairs can be added. - - -```javascript -status -``` - -An object mapping status names to status code numbers. - - -```javascript -callError -``` - -An object mapping call error names to codes. This is primarily useful for tracking down certain kinds of internal errors. - - -```javascript -Credentials -``` - -An object with factory methods for creating credential objects for clients. - - -```javascript -ServerCredentials -``` - -An object with factory methods for creating credential objects for servers. diff --git a/src/node/index.js b/src/node/index.js index 2da77c3eae..177628e22d 100644 --- a/src/node/index.js +++ b/src/node/index.js @@ -1,5 +1,5 @@ -/* - * +/** + * @license * Copyright 2015, Google Inc. * All rights reserved. * @@ -31,10 +31,6 @@ * */ -/** - * @module - */ - 'use strict'; var path = require('path'); @@ -64,24 +60,30 @@ var constants = require('./src/constants.js'); grpc.setDefaultRootsPem(fs.readFileSync(SSL_ROOTS_PATH, 'ascii')); /** - * Load a ProtoBuf.js object as a gRPC object. The options object can provide - * the following options: - * - binaryAsBase64: deserialize bytes values as base64 strings instead of - * Buffers. Defaults to false - * - longsAsStrings: deserialize long values as strings instead of objects. - * Defaults to true - * - deprecatedArgumentOrder: Use the beta method argument order for client - * methods, with optional arguments after the callback. Defaults to false. - * This option is only a temporary stopgap measure to smooth an API breakage. - * It is deprecated, and new code should not use it. - * - protobufjsVersion: Available values are 5, 6, and 'detect'. 5 and 6 - * respectively indicate that an object from the corresponding version of - * ProtoBuf.js is provided in the value argument. If the option is 'detect', - * gRPC will guess what the version is based on the structure of the value. - * Defaults to 'detect'. + * @namespace grpc + */ + +/** + * Load a ProtoBuf.js object as a gRPC object. + * @memberof grpc + * @alias grpc.loadObject * @param {Object} value The ProtoBuf.js reflection object to load * @param {Object=} options Options to apply to the loaded file - * @return {Object<string, *>} The resulting gRPC object + * @param {bool=} [options.binaryAsBase64=false] deserialize bytes values as + * base64 strings instead of Buffers + * @param {bool=} [options.longsAsStrings=true] deserialize long values as + * strings instead of objects + * @param {bool=} [options.enumsAsStrings=true] deserialize enum values as + * strings instead of numbers. Only works with Protobuf.js 6 values. + * @param {bool=} [options.deprecatedArgumentOrder=false] use the beta method + * argument order for client methods, with optional arguments after the + * callback. This option is only a temporary stopgap measure to smooth an + * API breakage. It is deprecated, and new code should not use it. + * @param {(number|string)=} [options.protobufjsVersion='detect'] 5 and 6 + * respectively indicate that an object from the corresponding version of + * Protobuf.js is provided in the value argument. If the option is 'detect', + * gRPC wll guess what the version is based on the structure of the value. + * @return {Object<string, *>} The resulting gRPC object. */ exports.loadObject = function loadObject(value, options) { options = _.defaults(options, common.defaultGrpcOptions); @@ -112,22 +114,23 @@ exports.loadObject = function loadObject(value, options) { var loadObject = exports.loadObject; /** - * Load a gRPC object from a .proto file. The options object can provide the - * following options: - * - convertFieldsToCamelCase: Load this file with field names in camel case - * instead of their original case - * - binaryAsBase64: deserialize bytes values as base64 strings instead of - * Buffers. Defaults to false - * - longsAsStrings: deserialize long values as strings instead of objects. - * Defaults to true - * - deprecatedArgumentOrder: Use the beta method argument order for client - * methods, with optional arguments after the callback. Defaults to false. - * This option is only a temporary stopgap measure to smooth an API breakage. - * It is deprecated, and new code should not use it. + * Load a gRPC object from a .proto file. + * @memberof grpc + * @alias grpc.load * @param {string|{root: string, file: string}} filename The file to load * @param {string=} format The file format to expect. Must be either 'proto' or * 'json'. Defaults to 'proto' * @param {Object=} options Options to apply to the loaded file + * @param {bool=} [options.convertFieldsToCamelCase=false] Load this file with + * field names in camel case instead of their original case + * @param {bool=} [options.binaryAsBase64=false] deserialize bytes values as + * base64 strings instead of Buffers + * @param {bool=} [options.longsAsStrings=true] deserialize long values as + * strings instead of objects + * @param {bool=} [options.deprecatedArgumentOrder=false] use the beta method + * argument order for client methods, with optional arguments after the + * callback. This option is only a temporary stopgap measure to smooth an + * API breakage. It is deprecated, and new code should not use it. * @return {Object<string, *>} The resulting gRPC object */ exports.load = function load(filename, format, options) { @@ -168,6 +171,8 @@ var log_template = _.template( * called. Note: the output format here is intended to be informational, and * is not guaranteed to stay the same in the future. * Logs will be directed to logger.error. + * @memberof grpc + * @alias grpc.setLogger * @param {Console} logger A Console-like object. */ exports.setLogger = function setLogger(logger) { @@ -187,6 +192,8 @@ exports.setLogger = function setLogger(logger) { /** * Sets the logger verbosity for gRPC module logging. The options are members * of the grpc.logVerbosity map. + * @memberof grpc + * @alias grpc.setLogVerbosity * @param {Number} verbosity The minimum severity to log */ exports.setLogVerbosity = function setLogVerbosity(verbosity) { @@ -194,71 +201,70 @@ exports.setLogVerbosity = function setLogVerbosity(verbosity) { grpc.setLogVerbosity(verbosity); }; -/** - * @see module:src/server.Server - */ exports.Server = server.Server; -/** - * @see module:src/metadata - */ exports.Metadata = Metadata; -/** - * Status name to code number mapping - */ exports.status = constants.status; -/** - * Propagate flag name to number mapping - */ exports.propagate = constants.propagate; -/** - * Call error name to code number mapping - */ exports.callError = constants.callError; -/** - * Write flag name to code number mapping - */ exports.writeFlags = constants.writeFlags; -/** - * Log verbosity setting name to code number mapping - */ exports.logVerbosity = constants.logVerbosity; -/** - * Credentials factories - */ exports.credentials = require('./src/credentials.js'); /** * ServerCredentials factories + * @constructor ServerCredentials + * @memberof grpc */ exports.ServerCredentials = grpc.ServerCredentials; /** - * @see module:src/client.makeClientConstructor + * Create insecure server credentials + * @name grpc.ServerCredentials.createInsecure + * @kind function + * @return grpc.ServerCredentials */ -exports.makeGenericClientConstructor = client.makeClientConstructor; /** - * @see module:src/client.getClientChannel + * A private key and certificate pair + * @typedef {Object} grpc.ServerCredentials~keyCertPair + * @property {Buffer} privateKey The server's private key + * @property {Buffer} certChain The server's certificate chain */ -exports.getClientChannel = client.getClientChannel; /** - * @see module:src/client.waitForClientReady + * Create SSL server credentials + * @name grpc.ServerCredentials.createInsecure + * @kind function + * @param {?Buffer} rootCerts Root CA certificates for validating client + * certificates + * @param {Array<grpc.ServerCredentials~keyCertPair>} keyCertPairs A list of + * private key and certificate chain pairs to be used for authenticating + * the server + * @param {boolean} [checkClientCertificate=false] Indicates that the server + * should request and verify the client's certificates + * @return grpc.ServerCredentials */ + +exports.makeGenericClientConstructor = client.makeClientConstructor; + +exports.getClientChannel = client.getClientChannel; + exports.waitForClientReady = client.waitForClientReady; +/** + * @memberof grpc + * @alias grpc.closeClient + * @param {grpc.Client} client_obj The client to close + */ exports.closeClient = function closeClient(client_obj) { client.Client.prototype.close.apply(client_obj); }; -/** - * @see module:src/client.Client - */ exports.Client = client.Client; diff --git a/src/node/src/client.js b/src/node/src/client.js index 16fe06a54d..cf4c104144 100644 --- a/src/node/src/client.js +++ b/src/node/src/client.js @@ -1,5 +1,5 @@ -/* - * +/** + * @license * Copyright 2015, Google Inc. * All rights reserved. * @@ -43,8 +43,6 @@ * var Client = proto_obj.package.subpackage.ServiceName; * var client = new Client(server_address, client_credentials); * var call = client.unaryMethod(arguments, callback); - * - * @module */ 'use strict'; @@ -70,13 +68,26 @@ var Duplex = stream.Duplex; var util = require('util'); var version = require('../../../package.json').version; +/** + * Initial response metadata sent by the server when it starts processing the + * call + * @event grpc~ClientUnaryCall#metadata + * @type {grpc.Metadata} + */ + +/** + * Status of the call when it has completed. + * @event grpc~ClientUnaryCall#status + * @type grpc~StatusObject + */ + util.inherits(ClientUnaryCall, EventEmitter); /** - * An EventEmitter. Used for unary calls - * @constructor + * An EventEmitter. Used for unary calls. + * @constructor grpc~ClientUnaryCall * @extends external:EventEmitter - * @param {grpc.Call} call The call object associated with the request + * @param {grpc.internal~Call} call The call object associated with the request */ function ClientUnaryCall(call) { EventEmitter.call(this); @@ -88,14 +99,16 @@ util.inherits(ClientWritableStream, Writable); /** * A stream that the client can write to. Used for calls that are streaming from * the client side. - * @constructor + * @constructor grpc~ClientWritableStream * @extends external:Writable - * @borrows module:src/client~ClientUnaryCall#cancel as - * module:src/client~ClientWritableStream#cancel - * @borrows module:src/client~ClientUnaryCall#getPeer as - * module:src/client~ClientWritableStream#getPeer - * @param {grpc.Call} call The call object to send data with - * @param {module:src/common~serialize=} [serialize=identity] Serialization + * @borrows grpc~ClientUnaryCall#cancel as grpc~ClientWritableStream#cancel + * @borrows grpc~ClientUnaryCall#getPeer as grpc~ClientWritableStream#getPeer + * @borrows grpc~ClientUnaryCall#event:metadata as + * grpc~ClientWritableStream#metadata + * @borrows grpc~ClientUnaryCall#event:status as + * grpc~ClientWritableStream#status + * @param {grpc.internal~Call} call The call object to send data with + * @param {grpc~serialize=} [serialize=identity] Serialization * function for writes. */ function ClientWritableStream(call, serialize) { @@ -110,11 +123,24 @@ function ClientWritableStream(call, serialize) { } /** + * Write a message to the request stream. If serializing the argument fails, + * the call will be cancelled and the stream will end with an error. + * @name grpc~ClientWritableStream#write + * @kind function + * @override + * @param {*} message The message to write. Must be a valid argument to the + * serialize function of the corresponding method + * @param {grpc.writeFlags} flags Flags to modify how the message is written + * @param {Function} callback Callback for when this chunk of data is flushed + * @return {boolean} As defined for [Writable]{@link external:Writable} + */ + +/** * Attempt to write the given chunk. Calls the callback when done. This is an * implementation of a method needed for implementing stream.Writable. - * @access private - * @param {Buffer} chunk The chunk to write - * @param {string} encoding Used to pass write flags + * @private + * @param {*} chunk The chunk to write + * @param {grpc.writeFlags} encoding Used to pass write flags * @param {function(Error=)} callback Called when the write is complete */ function _write(chunk, encoding, callback) { @@ -155,14 +181,16 @@ util.inherits(ClientReadableStream, Readable); /** * A stream that the client can read from. Used for calls that are streaming * from the server side. - * @constructor + * @constructor grpc~ClientReadableStream * @extends external:Readable - * @borrows module:src/client~ClientUnaryCall#cancel as - * module:src/client~ClientReadableStream#cancel - * @borrows module:src/client~ClientUnaryCall#getPeer as - * module:src/client~ClientReadableStream#getPeer - * @param {grpc.Call} call The call object to read data with - * @param {module:src/common~deserialize=} [deserialize=identity] + * @borrows grpc~ClientUnaryCall#cancel as grpc~ClientReadableStream#cancel + * @borrows grpc~ClientUnaryCall#getPeer as grpc~ClientReadableStream#getPeer + * @borrows grpc~ClientUnaryCall#event:metadata as + * grpc~ClientReadableStream#metadata + * @borrows grpc~ClientUnaryCall#event:status as + * grpc~ClientReadableStream#status + * @param {grpc.internal~Call} call The call object to read data with + * @param {grpc~deserialize=} [deserialize=identity] * Deserialization function for reads */ function ClientReadableStream(call, deserialize) { @@ -183,7 +211,7 @@ function ClientReadableStream(call, deserialize) { * parameter indicates that the call should end with that status. status * defaults to OK if not provided. * @param {Object!} status The status that the call should end with - * @access private + * @private */ function _readsDone(status) { /* jshint validthis: true */ @@ -202,7 +230,7 @@ ClientReadableStream.prototype._readsDone = _readsDone; /** * Called to indicate that we have received a status from the server. - * @access private + * @private */ function _receiveStatus(status) { /* jshint validthis: true */ @@ -215,7 +243,7 @@ ClientReadableStream.prototype._receiveStatus = _receiveStatus; /** * If we have both processed all incoming messages and received the status from * the server, emit the status. Otherwise, do nothing. - * @access private + * @private */ function _emitStatusIfDone() { /* jshint validthis: true */ @@ -242,7 +270,7 @@ ClientReadableStream.prototype._emitStatusIfDone = _emitStatusIfDone; /** * Read the next object from the stream. - * @access private + * @private * @param {*} size Ignored because we use objectMode=true */ function _read(size) { @@ -300,16 +328,19 @@ util.inherits(ClientDuplexStream, Duplex); /** * A stream that the client can read from or write to. Used for calls with * duplex streaming. - * @constructor + * @constructor grpc~ClientDuplexStream * @extends external:Duplex - * @borrows module:src/client~ClientUnaryCall#cancel as - * module:src/client~ClientDuplexStream#cancel - * @borrows module:src/client~ClientUnaryCall#getPeer as - * module:src/client~ClientDuplexStream#getPeer - * @param {grpc.Call} call Call object to proxy - * @param {module:src/common~serialize=} [serialize=identity] Serialization + * @borrows grpc~ClientUnaryCall#cancel as grpc~ClientDuplexStream#cancel + * @borrows grpc~ClientUnaryCall#getPeer as grpc~ClientDuplexStream#getPeer + * @borrows grpc~ClientWritableStream#write as grpc~ClientDuplexStream#write + * @borrows grpc~ClientUnaryCall#event:metadata as + * grpc~ClientDuplexStream#metadata + * @borrows grpc~ClientUnaryCall#event:status as + * grpc~ClientDuplexStream#status + * @param {grpc.internal~Call} call Call object to proxy + * @param {grpc~serialize=} [serialize=identity] Serialization * function for requests - * @param {module:src/common~deserialize=} [deserialize=identity] + * @param {grpc~deserialize=} [deserialize=identity] * Deserialization function for responses */ function ClientDuplexStream(call, serialize, deserialize) { @@ -336,8 +367,9 @@ ClientDuplexStream.prototype._read = _read; ClientDuplexStream.prototype._write = _write; /** - * Cancel the ongoing call - * @alias module:src/client~ClientUnaryCall#cancel + * Cancel the ongoing call. Results in the call ending with a CANCELLED status, + * unless it has already ended with some other status. + * @alias grpc~ClientUnaryCall#cancel */ function cancel() { /* jshint validthis: true */ @@ -352,7 +384,7 @@ ClientDuplexStream.prototype.cancel = cancel; /** * Get the endpoint this call/stream is connected to. * @return {string} The URI of the endpoint - * @alias module:src/client~ClientUnaryCall#getPeer + * @alias grpc~ClientUnaryCall#getPeer */ function getPeer() { /* jshint validthis: true */ @@ -368,33 +400,31 @@ ClientDuplexStream.prototype.getPeer = getPeer; * Any client call type * @typedef {(ClientUnaryCall|ClientReadableStream| * ClientWritableStream|ClientDuplexStream)} - * module:src/client~Call + * grpc.Client~Call */ /** * Options that can be set on a call. - * @typedef {Object} module:src/client~CallOptions - * @property {(date|number)} deadline The deadline for the entire call to - * complete. A value of Infinity indicates that no deadline should be set. - * @property {(string)} host Server hostname to set on the call. Only meaningful + * @typedef {Object} grpc.Client~CallOptions + * @property {grpc~Deadline} deadline The deadline for the entire call to + * complete. + * @property {string} host Server hostname to set on the call. Only meaningful * if different from the server address used to construct the client. - * @property {module:src/client~Call} parent Parent call. Used in servers when + * @property {grpc.Client~Call} parent Parent call. Used in servers when * making a call as part of the process of handling a call. Used to * propagate some information automatically, as specified by * propagate_flags. * @property {number} propagate_flags Indicates which properties of a parent * call should propagate to this call. Bitwise combination of flags in - * [grpc.propagate]{@link module:index.propagate}. - * @property {module:src/credentials~CallCredentials} credentials The - * credentials that should be used to make this particular call. + * {@link grpc.propagate}. + * @property {grpc.credentials~CallCredentials} credentials The credentials that + * should be used to make this particular call. */ /** - * Get a call object built with the provided options. Keys for options are - * 'deadline', which takes a date or number, and 'host', which takes a string - * and overrides the hostname to connect to. + * Get a call object built with the provided options. * @access private - * @param {module:src/client~CallOptions=} options Options object. + * @param {grpc.Client~CallOptions=} options Options object. */ function getCall(channel, method, options) { var deadline; @@ -422,14 +452,14 @@ function getCall(channel, method, options) { /** * A generic gRPC client. Primarily useful as a base class for generated clients - * @alias module:src/client.Client + * @memberof grpc * @constructor * @param {string} address Server address to connect to - * @param {module:src/credentials~ChannelCredentials} credentials Credentials to - * use to connect to the server + * @param {grpc~ChannelCredentials} credentials Credentials to use to connect to + * the server * @param {Object} options Options to apply to channel creation */ -var Client = exports.Client = function Client(address, credentials, options) { +function Client(address, credentials, options) { if (!options) { options = {}; } @@ -445,19 +475,13 @@ var Client = exports.Client = function Client(address, credentials, options) { /* Private fields use $ as a prefix instead of _ because it is an invalid * prefix of a method name */ this.$channel = new grpc.Channel(address, credentials, options); -}; +} -/** - * @typedef {Error} module:src/client.Client~ServiceError - * @property {number} code The error code, a key of - * [grpc.status]{@link module:src/client.status} - * @property {module:metadata.Metadata} metadata Metadata sent with the status - * by the server, if any - */ +exports.Client = Client; /** - * @callback module:src/client.Client~requestCallback - * @param {?module:src/client.Client~ServiceError} error The error, if the call + * @callback grpc.Client~requestCallback + * @param {?grpc~ServiceError} error The error, if the call * failed * @param {*} value The response value, if the call succeeded */ @@ -466,17 +490,17 @@ var Client = exports.Client = function Client(address, credentials, options) { * Make a unary request to the given method, using the given serialize * and deserialize functions, with the given argument. * @param {string} method The name of the method to request - * @param {module:src/common~serialize} serialize The serialization function for + * @param {grpc~serialize} serialize The serialization function for * inputs - * @param {module:src/common~deserialize} deserialize The deserialization + * @param {grpc~deserialize} deserialize The deserialization * function for outputs * @param {*} argument The argument to the call. Should be serializable with * serialize - * @param {module:src/metadata.Metadata=} metadata Metadata to add to the call - * @param {module:src/client~CallOptions=} options Options map - * @param {module:src/client.Client~requestCallback} callback The callback to + * @param {grpc.Metadata=} metadata Metadata to add to the call + * @param {grpc.Client~CallOptions=} options Options map + * @param {grpc.Client~requestCallback} callback The callback to * for when the response is received - * @return {EventEmitter} An event emitter for stream related events + * @return {grpc~ClientUnaryCall} An event emitter for stream related events */ Client.prototype.makeUnaryRequest = function(method, serialize, deserialize, argument, metadata, options, @@ -548,17 +572,17 @@ Client.prototype.makeUnaryRequest = function(method, serialize, deserialize, * Make a client stream request to the given method, using the given serialize * and deserialize functions, with the given argument. * @param {string} method The name of the method to request - * @param {module:src/common~serialize} serialize The serialization function for + * @param {grpc~serialize} serialize The serialization function for * inputs - * @param {module:src/common~deserialize} deserialize The deserialization + * @param {grpc~deserialize} deserialize The deserialization * function for outputs - * @param {module:src/metadata.Metadata=} metadata Array of metadata key/value - * pairs to add to the call - * @param {module:src/client~CallOptions=} options Options map - * @param {Client~requestCallback} callback The callback to for when the + * @param {grpc.Metadata=} metadata Array of metadata key/value pairs to add to + * the call + * @param {grpc.Client~CallOptions=} options Options map + * @param {grpc.Client~requestCallback} callback The callback to for when the * response is received - * @return {module:src/client~ClientWritableStream} An event emitter for stream - * related events + * @return {grpc~ClientWritableStream} An event emitter for stream related + * events */ Client.prototype.makeClientStreamRequest = function(method, serialize, deserialize, metadata, @@ -631,17 +655,16 @@ Client.prototype.makeClientStreamRequest = function(method, serialize, * Make a server stream request to the given method, with the given serialize * and deserialize function, using the given argument * @param {string} method The name of the method to request - * @param {module:src/common~serialize} serialize The serialization function for - * inputs - * @param {module:src/common~deserialize} deserialize The deserialization + * @param {grpc~serialize} serialize The serialization function for inputs + * @param {grpc~deserialize} deserialize The deserialization * function for outputs * @param {*} argument The argument to the call. Should be serializable with * serialize - * @param {module:src/metadata.Metadata=} metadata Array of metadata key/value - * pairs to add to the call - * @param {module:src/client~CallOptions=} options Options map - * @return {module:src/client~ClientReadableStream} An event emitter for stream - * related events + * @param {grpc.Metadata=} metadata Array of metadata key/value pairs to add to + * the call + * @param {grpc.Client~CallOptions=} options Options map + * @return {grpc~ClientReadableStream} An event emitter for stream related + * events */ Client.prototype.makeServerStreamRequest = function(method, serialize, deserialize, argument, @@ -693,15 +716,13 @@ Client.prototype.makeServerStreamRequest = function(method, serialize, /** * Make a bidirectional stream request with this method on the given channel. * @param {string} method The name of the method to request - * @param {module:src/common~serialize} serialize The serialization function for - * inputs - * @param {module:src/common~deserialize} deserialize The deserialization + * @param {grpc~serialize} serialize The serialization function for inputs + * @param {grpc~deserialize} deserialize The deserialization * function for outputs - * @param {module:src/metadata.Metadata=} metadata Array of metadata key/value + * @param {grpc.Metadata=} metadata Array of metadata key/value * pairs to add to the call - * @param {module:src/client~CallOptions=} options Options map - * @return {module:src/client~ClientDuplexStream} An event emitter for stream - * related events + * @param {grpc.Client~CallOptions=} options Options map + * @return {grpc~ClientDuplexStream} An event emitter for stream related events */ Client.prototype.makeBidiStreamRequest = function(method, serialize, deserialize, metadata, @@ -743,6 +764,9 @@ Client.prototype.makeBidiStreamRequest = function(method, serialize, return stream; }; +/** + * Close this client. + */ Client.prototype.close = function() { this.$channel.close(); }; @@ -761,8 +785,7 @@ Client.prototype.getChannel = function() { * with an error if the attempt to connect to the server has unrecoverablly * failed or if the deadline expires. This function will make the channel * start connecting if it has not already done so. - * @param {(Date|Number)} deadline When to stop waiting for a connection. Pass - * Infinity to wait forever. + * @param {grpc~Deadline} deadline When to stop waiting for a connection. * @param {function(Error)} callback The callback to call when done attempting * to connect. */ @@ -788,7 +811,7 @@ Client.prototype.waitForReady = function(deadline, callback) { /** * Map with short names for each of the requester maker functions. Used in * makeClientConstructor - * @access private + * @private */ var requester_funcs = { unary: Client.prototype.makeUnaryRequest, @@ -834,9 +857,15 @@ var deprecated_request_wrap = { /** * Creates a constructor for a client with the given methods, as specified in - * the methods argument. - * @param {module:src/common~ServiceDefinition} methods An object mapping - * method names to method attributes + * the methods argument. The resulting class will have an instance method for + * each method in the service, which is a partial application of one of the + * [Client]{@link grpc.Client} request methods, depending on `requestSerialize` + * and `responseSerialize`, with the `method`, `serialize`, and `deserialize` + * arguments predefined. + * @memberof grpc + * @alias grpc~makeGenericClientConstructor + * @param {grpc~ServiceDefinition} methods An object mapping method names to + * method attributes * @param {string} serviceName The fully qualified name of the service * @param {Object} class_options An options object. * @param {boolean=} [class_options.deprecatedArgumentOrder=false] Indicates @@ -844,9 +873,8 @@ var deprecated_request_wrap = { * arguments at the end instead of the callback at the end. This option * is only a temporary stopgap measure to smooth an API breakage. * It is deprecated, and new code should not use it. - * @return {function(string, Object)} New client constructor, which is a - * subclass of [grpc.Client]{@link module:src/client.Client}, and has the - * same arguments as that constructor. + * @return {function} New client constructor, which is a subclass of + * {@link grpc.Client}, and has the same arguments as that constructor. */ exports.makeClientConstructor = function(methods, serviceName, class_options) { @@ -898,8 +926,11 @@ exports.makeClientConstructor = function(methods, serviceName, /** * Return the underlying channel object for the specified client + * @memberof grpc + * @alias grpc~getClientChannel * @param {Client} client * @return {Channel} The channel + * @see grpc.Client#getChannel */ exports.getClientChannel = function(client) { return Client.prototype.getChannel.call(client); @@ -911,22 +942,15 @@ exports.getClientChannel = function(client) { * with an error if the attempt to connect to the server has unrecoverablly * failed or if the deadline expires. This function will make the channel * start connecting if it has not already done so. + * @memberof grpc + * @alias grpc~waitForClientReady * @param {Client} client The client to wait on - * @param {(Date|Number)} deadline When to stop waiting for a connection. Pass + * @param {grpc~Deadline} deadline When to stop waiting for a connection. Pass * Infinity to wait forever. * @param {function(Error)} callback The callback to call when done attempting * to connect. + * @see grpc.Client#waitForReady */ exports.waitForClientReady = function(client, deadline, callback) { Client.prototype.waitForReady.call(client, deadline, callback); }; - -/** - * Map of status code names to status codes - */ -exports.status = constants.status; - -/** - * See docs for client.callError - */ -exports.callError = grpc.callError; diff --git a/src/node/src/common.js b/src/node/src/common.js index 4dad60e630..0f835317ea 100644 --- a/src/node/src/common.js +++ b/src/node/src/common.js @@ -1,5 +1,5 @@ -/* - * +/** + * @license * Copyright 2015, Google Inc. * All rights reserved. * @@ -31,12 +31,6 @@ * */ -/** - * This module contains functions that are common to client and server - * code. None of them should be used directly by gRPC users. - * @module - */ - 'use strict'; var _ = require('lodash'); @@ -62,16 +56,19 @@ exports.wrapIgnoreNull = function wrapIgnoreNull(func) { /** * The logger object for the gRPC module. Defaults to console. + * @private */ exports.logger = console; /** * The current logging verbosity. 0 corresponds to logging everything + * @private */ exports.logVerbosity = 0; /** * Log a message if the severity is at least as high as the current verbosity + * @private * @param {Number} severity A value of the grpc.logVerbosity map * @param {String} message The message to log */ @@ -83,6 +80,7 @@ exports.log = function log(severity, message) { /** * Default options for loading proto files into gRPC + * @alias grpc~defaultLoadOptions */ exports.defaultGrpcOptions = { convertFieldsToCamelCase: false, @@ -95,6 +93,30 @@ exports.defaultGrpcOptions = { // JSDoc definitions that are used in multiple other modules /** + * Represents the status of a completed request. If `code` is + * {@link grpc.status}.OK, then the request has completed successfully. + * Otherwise, the request has failed, `details` will contain a description of + * the error. Either way, `metadata` contains the trailing response metadata + * sent by the server when it finishes processing the call. + * @typedef {object} grpc~StatusObject + * @property {number} code The error code, a key of {@link grpc.status} + * @property {string} details Human-readable description of the status + * @property {grpc.Metadata} metadata Trailing metadata sent with the status, + * if applicable + */ + +/** + * Describes how a request has failed. The member `message` will be the same as + * `details` in {@link grpc~StatusObject}, and `code` and `metadata` are the + * same as in that object. + * @typedef {Error} grpc~ServiceError + * @property {number} code The error code, a key of {@link grpc.status} that is + * not `grpc.status.OK` + * @property {grpc.Metadata} metadata Trailing metadata sent with the status, + * if applicable + */ + +/** * The EventEmitter class in the event standard module * @external EventEmitter * @see https://nodejs.org/api/events.html#events_class_eventemitter @@ -120,38 +142,46 @@ exports.defaultGrpcOptions = { /** * A serialization function - * @callback module:src/common~serialize + * @callback grpc~serialize * @param {*} value The value to serialize * @return {Buffer} The value serialized as a byte sequence */ /** * A deserialization function - * @callback module:src/common~deserialize + * @callback grpc~deserialize * @param {Buffer} data The byte sequence to deserialize * @return {*} The data deserialized as a value */ /** + * The deadline of an operation. If it is a date, the deadline is reached at + * the date and time specified. If it is a finite number, it is treated as + * a number of milliseconds since the Unix Epoch. If it is Infinity, the + * deadline will never be reached. If it is -Infinity, the deadline has already + * passed. + * @typedef {(number|date)} grpc~Deadline + */ + +/** * An object that completely defines a service method signature. - * @typedef {Object} module:src/common~MethodDefinition + * @typedef {Object} grpc~MethodDefinition * @property {string} path The method's URL path * @property {boolean} requestStream Indicates whether the method accepts * a stream of requests * @property {boolean} responseStream Indicates whether the method returns * a stream of responses - * @property {module:src/common~serialize} requestSerialize Serialization + * @property {grpc~serialize} requestSerialize Serialization * function for request values - * @property {module:src/common~serialize} responseSerialize Serialization + * @property {grpc~serialize} responseSerialize Serialization * function for response values - * @property {module:src/common~deserialize} requestDeserialize Deserialization + * @property {grpc~deserialize} requestDeserialize Deserialization * function for request data - * @property {module:src/common~deserialize} responseDeserialize Deserialization + * @property {grpc~deserialize} responseDeserialize Deserialization * function for repsonse data */ /** * An object that completely defines a service. - * @typedef {Object.<string, module:src/common~MethodDefinition>} - * module:src/common~ServiceDefinition + * @typedef {Object.<string, grpc~MethodDefinition>} grpc~ServiceDefinition */ diff --git a/src/node/src/constants.js b/src/node/src/constants.js index 528dab120e..c441ee740b 100644 --- a/src/node/src/constants.js +++ b/src/node/src/constants.js @@ -1,5 +1,5 @@ -/* - * +/** + * @license * Copyright 2017, Google Inc. * All rights reserved. * @@ -31,16 +31,14 @@ * */ -/** - * @module - */ - /* The comments about status codes are copied verbatim (with some formatting * modifications) from include/grpc/impl/codegen/status.h, for the purpose of * including them in generated documentation. */ /** * Enum of status codes that gRPC can return + * @memberof grpc + * @alias grpc.status * @readonly * @enum {number} */ @@ -178,6 +176,8 @@ exports.status = { * Users are encouraged to write propagation masks as deltas from the default. * i.e. write `grpc.propagate.DEFAULTS & ~grpc.propagate.DEADLINE` to disable * deadline propagation. + * @memberof grpc + * @alias grpc.propagate * @enum {number} */ exports.propagate = { @@ -194,9 +194,11 @@ exports.propagate = { /** * Call error constants. Call errors almost always indicate bugs in the gRPC * library, and these error codes are mainly useful for finding those bugs. + * @memberof grpc + * @readonly * @enum {number} */ -exports.callError = { +const callError = { OK: 0, ERROR: 1, NOT_ON_SERVER: 2, @@ -213,9 +215,14 @@ exports.callError = { PAYLOAD_TYPE_MISMATCH: 14 }; +exports.callError = callError; + /** * Write flags: these can be bitwise or-ed to form write options that modify * how data is written. + * @memberof grpc + * @alias grpc.writeFlags + * @readonly * @enum {number} */ exports.writeFlags = { @@ -232,6 +239,9 @@ exports.writeFlags = { }; /** + * @memberof grpc + * @alias grpc.logVerbosity + * @readonly * @enum {number} */ exports.logVerbosity = { diff --git a/src/node/src/credentials.js b/src/node/src/credentials.js index b1e86bbd09..b1dbc1c450 100644 --- a/src/node/src/credentials.js +++ b/src/node/src/credentials.js @@ -1,5 +1,5 @@ -/* - * +/** + * @license * Copyright 2015, Google Inc. * All rights reserved. * @@ -48,6 +48,7 @@ * For example, to create a client secured with SSL that uses Google * default application credentials to authenticate: * + * @example * var channel_creds = credentials.createSsl(root_certs); * (new GoogleAuth()).getApplicationDefault(function(err, credential) { * var call_creds = credentials.createFromGoogleCredential(credential); @@ -56,15 +57,25 @@ * var client = new Client(address, combined_creds); * }); * - * @module + * @namespace grpc.credentials */ 'use strict'; var grpc = require('./grpc_extension'); +/** + * This cannot be constructed directly. Instead, instances of this class should + * be created using the factory functions in {@link grpc.credentials} + * @constructor grpc.credentials~CallCredentials + */ var CallCredentials = grpc.CallCredentials; +/** + * This cannot be constructed directly. Instead, instances of this class should + * be created using the factory functions in {@link grpc.credentials} + * @constructor grpc.credentials~ChannelCredentials + */ var ChannelCredentials = grpc.ChannelCredentials; var Metadata = require('./metadata.js'); @@ -76,24 +87,48 @@ var constants = require('./constants'); var _ = require('lodash'); /** + * @external GoogleCredential + * @see https://github.com/google/google-auth-library-nodejs + */ + +/** * Create an SSL Credentials object. If using a client-side certificate, both * the second and third arguments must be passed. + * @memberof grpc.credentials + * @alias grpc.credentials.createSsl + * @kind function * @param {Buffer} root_certs The root certificate data * @param {Buffer=} private_key The client certificate private key, if * applicable * @param {Buffer=} cert_chain The client certificate cert chain, if applicable - * @return {ChannelCredentials} The SSL Credentials object + * @return {grpc.credentials.ChannelCredentials} The SSL Credentials object */ exports.createSsl = ChannelCredentials.createSsl; /** + * @callback grpc.credentials~metadataCallback + * @param {Error} error The error, if getting metadata failed + * @param {grpc.Metadata} metadata The metadata + */ + +/** + * @callback grpc.credentials~generateMetadata + * @param {Object} params Parameters that can modify metadata generation + * @param {string} params.service_url The URL of the service that the call is + * going to + * @param {grpc.credentials~metadataCallback} callback + */ + +/** * Create a gRPC credentials object from a metadata generation function. This * function gets the service URL and a callback as parameters. The error * passed to the callback can optionally have a 'code' value attached to it, * which corresponds to a status code that this library uses. - * @param {function(String, function(Error, Metadata))} metadata_generator The - * function that generates metadata - * @return {CallCredentials} The credentials object + * @memberof grpc.credentials + * @alias grpc.credentials.createFromMetadataGenerator + * @param {grpc.credentials~generateMetadata} metadata_generator The function + * that generates metadata + * @return {grpc.credentials.CallCredentials} The credentials object */ exports.createFromMetadataGenerator = function(metadata_generator) { return CallCredentials.createFromPlugin(function(service_url, cb_data, @@ -119,8 +154,11 @@ exports.createFromMetadataGenerator = function(metadata_generator) { /** * Create a gRPC credential from a Google credential object. - * @param {Object} google_credential The Google credential object to use - * @return {CallCredentials} The resulting credentials object + * @memberof grpc.credentials + * @alias grpc.credentials.createFromGoogleCredential + * @param {external:GoogleCredential} google_credential The Google credential + * object to use + * @return {grpc.credentials.CallCredentials} The resulting credentials object */ exports.createFromGoogleCredential = function(google_credential) { return exports.createFromMetadataGenerator(function(auth_context, callback) { @@ -141,6 +179,8 @@ exports.createFromGoogleCredential = function(google_credential) { /** * Combine a ChannelCredentials with any number of CallCredentials into a single * ChannelCredentials object. + * @memberof grpc.credentials + * @alias grpc.credentials.combineChannelCredentials * @param {ChannelCredentials} channel_credential The ChannelCredentials to * start with * @param {...CallCredentials} credentials The CallCredentials to compose @@ -157,6 +197,8 @@ exports.combineChannelCredentials = function(channel_credential) { /** * Combine any number of CallCredentials into a single CallCredentials object + * @memberof grpc.credentials + * @alias grpc.credentials.combineCallCredentials * @param {...CallCredentials} credentials the CallCredentials to compose * @return CallCredentials A credentials object that combines all of the input * credentials @@ -172,6 +214,9 @@ exports.combineCallCredentials = function() { /** * Create an insecure credentials object. This is used to create a channel that * does not use SSL. This cannot be composed with anything. + * @memberof grpc.credentials + * @alias grpc.credentials.createInsecure + * @kind function * @return {ChannelCredentials} The insecure credentials object */ exports.createInsecure = ChannelCredentials.createInsecure; diff --git a/src/node/src/grpc_extension.js b/src/node/src/grpc_extension.js index 63a281ddbc..864da97314 100644 --- a/src/node/src/grpc_extension.js +++ b/src/node/src/grpc_extension.js @@ -1,5 +1,5 @@ -/* - * +/** + * @license * Copyright 2016, Google Inc. * All rights reserved. * diff --git a/src/node/src/metadata.js b/src/node/src/metadata.js index 92cf23998b..c757d520f8 100644 --- a/src/node/src/metadata.js +++ b/src/node/src/metadata.js @@ -1,5 +1,5 @@ -/* - * +/** + * @license * Copyright 2015, Google Inc. * All rights reserved. * @@ -31,15 +31,6 @@ * */ -/** - * Metadata module - * - * This module defines the Metadata class, which represents header and trailer - * metadata for gRPC calls. - * - * @module - */ - 'use strict'; var _ = require('lodash'); @@ -48,8 +39,8 @@ var grpc = require('./grpc_extension'); /** * Class for storing metadata. Keys are normalized to lowercase ASCII. + * @memberof grpc * @constructor - * @alias module:src/metadata.Metadata * @example * var metadata = new metadata_module.Metadata(); * metadata.set('key1', 'value1'); diff --git a/src/node/src/protobuf_js_5_common.js b/src/node/src/protobuf_js_5_common.js index 4041e05390..1663a3a400 100644 --- a/src/node/src/protobuf_js_5_common.js +++ b/src/node/src/protobuf_js_5_common.js @@ -1,5 +1,5 @@ -/* - * +/** + * @license * Copyright 2017, Google Inc. * All rights reserved. * @@ -31,6 +31,11 @@ * */ +/** + * @module + * @private + */ + 'use strict'; var _ = require('lodash'); diff --git a/src/node/src/protobuf_js_6_common.js b/src/node/src/protobuf_js_6_common.js index 00f11f2736..91a458aa20 100644 --- a/src/node/src/protobuf_js_6_common.js +++ b/src/node/src/protobuf_js_6_common.js @@ -1,5 +1,5 @@ -/* - * +/** + * @license * Copyright 2017, Google Inc. * All rights reserved. * @@ -31,6 +31,11 @@ * */ +/** + * @module + * @private + */ + 'use strict'; var _ = require('lodash'); diff --git a/src/node/src/server.js b/src/node/src/server.js index 1d9cc7d2c1..ae4fcb1dc4 100644 --- a/src/node/src/server.js +++ b/src/node/src/server.js @@ -1,5 +1,5 @@ -/* - * +/** + * @license * Copyright 2015, Google Inc. * All rights reserved. * @@ -31,22 +31,6 @@ * */ -/** - * Server module - * - * This module contains all the server code for Node gRPC: both the Server - * class itself and the method handler code for all types of methods. - * - * For example, to create a Server, add a service, and start it: - * - * var server = new server_module.Server(); - * server.addProtoService(protobuf_service_descriptor, service_implementation); - * server.bind('address:port', server_credential); - * server.start(); - * - * @module - */ - 'use strict'; var _ = require('lodash'); @@ -70,9 +54,9 @@ var EventEmitter = require('events').EventEmitter; /** * Handle an error on a call by sending it as a status - * @access private - * @param {grpc.Call} call The call to send the error on - * @param {Object} error The error object + * @private + * @param {grpc.internal~Call} call The call to send the error on + * @param {(Object|Error)} error The error object */ function handleError(call, error) { var statusMetadata = new Metadata(); @@ -104,14 +88,14 @@ function handleError(call, error) { /** * Send a response to a unary or client streaming call. - * @access private + * @private * @param {grpc.Call} call The call to respond on * @param {*} value The value to respond with - * @param {function(*):Buffer=} serialize Serialization function for the + * @param {grpc~serialize} serialize Serialization function for the * response - * @param {Metadata=} metadata Optional trailing metadata to send with status - * @param {number=} flags Flags for modifying how the message is sent. - * Defaults to 0. + * @param {grpc.Metadata=} metadata Optional trailing metadata to send with + * status + * @param {number=} [flags=0] Flags for modifying how the message is sent. */ function sendUnaryResponse(call, value, serialize, metadata, flags) { var end_batch = {}; @@ -146,7 +130,7 @@ function sendUnaryResponse(call, value, serialize, metadata, flags) { /** * Initialize a writable stream. This is used for both the writable and duplex * stream constructors. - * @access private + * @private * @param {Writable} stream The stream to set up * @param {function(*):Buffer=} Serialization function for responses */ @@ -225,9 +209,9 @@ function setUpWritable(stream, serialize) { /** * Initialize a readable stream. This is used for both the readable and duplex * stream constructors. - * @access private + * @private * @param {Readable} stream The stream to initialize - * @param {function(Buffer):*=} deserialize Deserialization function for + * @param {grpc~deserialize} deserialize Deserialization function for * incoming data. */ function setUpReadable(stream, deserialize) { @@ -245,34 +229,88 @@ function setUpReadable(stream, deserialize) { }); } +/** + * Emitted when the call has been cancelled. After this has been emitted, the + * call's `cancelled` property will be set to `true`. + * @event grpc~ServerUnaryCall~cancelled + */ + util.inherits(ServerUnaryCall, EventEmitter); -function ServerUnaryCall(call) { +/** + * An EventEmitter. Used for unary calls. + * @constructor grpc~ServerUnaryCall + * @extends external:EventEmitter + * @param {grpc.internal~Call} call The call object associated with the request + * @param {grpc.Metadata} metadata The request metadata from the client + */ +function ServerUnaryCall(call, metadata) { EventEmitter.call(this); this.call = call; + /** + * Indicates if the call has been cancelled + * @member {boolean} grpc~ServerUnaryCall#cancelled + */ + this.cancelled = false; + /** + * The request metadata from the client + * @member {grpc.Metadata} grpc~ServerUnaryCall#metadata + */ + this.metadata = metadata; + /** + * The request message from the client + * @member {*} grpc~ServerUnaryCall#request + */ + this.request = undefined; } +/** + * Emitted when the call has been cancelled. After this has been emitted, the + * call's `cancelled` property will be set to `true`. + * @event grpc~ServerWritableStream~cancelled + */ + util.inherits(ServerWritableStream, Writable); /** * A stream that the server can write to. Used for calls that are streaming from * the server side. - * @constructor - * @param {grpc.Call} call The call object to send data with - * @param {function(*):Buffer=} serialize Serialization function for writes + * @constructor grpc~ServerWritableStream + * @extends external:Writable + * @borrows grpc~ServerUnaryCall#sendMetadata as + * grpc~ServerWritableStream#sendMetadata + * @borrows grpc~ServerUnaryCall#getPeer as grpc~ServerWritableStream#getPeer + * @param {grpc.internal~Call} call The call object to send data with + * @param {grpc.Metadata} metadata The request metadata from the client + * @param {grpc~serialize} serialize Serialization function for writes */ -function ServerWritableStream(call, serialize) { +function ServerWritableStream(call, metadata, serialize) { Writable.call(this, {objectMode: true}); this.call = call; this.finished = false; setUpWritable(this, serialize); + /** + * Indicates if the call has been cancelled + * @member {boolean} grpc~ServerWritableStream#cancelled + */ + this.cancelled = false; + /** + * The request metadata from the client + * @member {grpc.Metadata} grpc~ServerWritableStream#metadata + */ + this.metadata = metadata; + /** + * The request message from the client + * @member {*} grpc~ServerWritableStream#request + */ + this.request = undefined; } /** * Start writing a chunk of data. This is an implementation of a method required * for implementing stream.Writable. - * @access private + * @private * @param {Buffer} chunk The chunk of data to write * @param {string} encoding Used to pass write flags * @param {function(Error=)} callback Callback to indicate that the write is @@ -312,19 +350,40 @@ function _write(chunk, encoding, callback) { ServerWritableStream.prototype._write = _write; +/** + * Emitted when the call has been cancelled. After this has been emitted, the + * call's `cancelled` property will be set to `true`. + * @event grpc~ServerReadableStream~cancelled + */ + util.inherits(ServerReadableStream, Readable); /** * A stream that the server can read from. Used for calls that are streaming * from the client side. - * @constructor - * @param {grpc.Call} call The call object to read data with - * @param {function(Buffer):*=} deserialize Deserialization function for reads + * @constructor grpc~ServerReadableStream + * @extends external:Readable + * @borrows grpc~ServerUnaryCall#sendMetadata as + * grpc~ServerReadableStream#sendMetadata + * @borrows grpc~ServerUnaryCall#getPeer as grpc~ServerReadableStream#getPeer + * @param {grpc.internal~Call} call The call object to read data with + * @param {grpc.Metadata} metadata The request metadata from the client + * @param {grpc~deserialize} deserialize Deserialization function for reads */ -function ServerReadableStream(call, deserialize) { +function ServerReadableStream(call, metadata, deserialize) { Readable.call(this, {objectMode: true}); this.call = call; setUpReadable(this, deserialize); + /** + * Indicates if the call has been cancelled + * @member {boolean} grpc~ServerReadableStream#cancelled + */ + this.cancelled = false; + /** + * The request metadata from the client + * @member {grpc.Metadata} grpc~ServerReadableStream#metadata + */ + this.metadata = metadata; } /** @@ -381,22 +440,43 @@ function _read(size) { ServerReadableStream.prototype._read = _read; +/** + * Emitted when the call has been cancelled. After this has been emitted, the + * call's `cancelled` property will be set to `true`. + * @event grpc~ServerDuplexStream~cancelled + */ + util.inherits(ServerDuplexStream, Duplex); /** * A stream that the server can read from or write to. Used for calls with * duplex streaming. - * @constructor - * @param {grpc.Call} call Call object to proxy - * @param {function(*):Buffer=} serialize Serialization function for requests - * @param {function(Buffer):*=} deserialize Deserialization function for + * @constructor grpc~ServerDuplexStream + * @extends external:Duplex + * @borrows grpc~ServerUnaryCall#sendMetadata as + * grpc~ServerDuplexStream#sendMetadata + * @borrows grpc~ServerUnaryCall#getPeer as grpc~ServerDuplexStream#getPeer + * @param {grpc.internal~Call} call Call object to proxy + * @param {grpc.Metadata} metadata The request metadata from the client + * @param {grpc~serialize} serialize Serialization function for requests + * @param {grpc~deserialize} deserialize Deserialization function for * responses */ -function ServerDuplexStream(call, serialize, deserialize) { +function ServerDuplexStream(call, metadata, serialize, deserialize) { Duplex.call(this, {objectMode: true}); this.call = call; setUpWritable(this, serialize); setUpReadable(this, deserialize); + /** + * Indicates if the call has been cancelled + * @member {boolean} grpc~ServerReadableStream#cancelled + */ + this.cancelled = false; + /** + * The request metadata from the client + * @member {grpc.Metadata} grpc~ServerReadableStream#metadata + */ + this.metadata = metadata; } ServerDuplexStream.prototype._read = _read; @@ -404,6 +484,7 @@ ServerDuplexStream.prototype._write = _write; /** * Send the initial metadata for a writable stream. + * @alias grpc~ServerUnaryCall#sendMetadata * @param {Metadata} responseMetadata Metadata to send */ function sendMetadata(responseMetadata) { @@ -430,6 +511,7 @@ ServerDuplexStream.prototype.sendMetadata = sendMetadata; /** * Get the endpoint this call/stream is connected to. + * @alias grpc~ServerUnaryCall#getPeer * @return {string} The URI of the endpoint */ function getPeer() { @@ -445,6 +527,7 @@ ServerDuplexStream.prototype.getPeer = getPeer; /** * Wait for the client to close, then emit a cancelled event if the client * cancelled. + * @private */ function waitForCancel() { /* jshint validthis: true */ @@ -468,18 +551,41 @@ ServerWritableStream.prototype.waitForCancel = waitForCancel; ServerDuplexStream.prototype.waitForCancel = waitForCancel; /** + * Callback function passed to server handlers that handle methods with unary + * responses. + * @callback grpc.Server~sendUnaryData + * @param {grpc~ServiceError} error An error, if the call failed + * @param {*} value The response value. Must be a valid argument to the + * `responseSerialize` method of the method that is being handled + * @param {grpc.Metadata=} trailer Trailing metadata to send, if applicable + * @param {grpc.writeFlags=} flags Flags to modify writing the response + */ + +/** + * User-provided method to handle unary requests on a server + * @callback grpc.Server~handleUnaryCall + * @param {grpc~ServerUnaryCall} call The call object + * @param {grpc.Server~sendUnaryData} callback The callback to call to respond + * to the request + */ + +/** * Fully handle a unary call - * @access private - * @param {grpc.Call} call The call to handle + * @private + * @param {grpc.internal~Call} call The call to handle * @param {Object} handler Request handler object for the method that was called - * @param {Metadata} metadata Metadata from the client + * @param {grpc~Server.handleUnaryCall} handler.func The handler function + * @param {grpc~deserialize} handler.deserialize The deserialization function + * for request data + * @param {grpc~serialize} handler.serialize The serialization function for + * response data + * @param {grpc.Metadata} metadata Metadata from the client */ function handleUnary(call, handler, metadata) { - var emitter = new ServerUnaryCall(call); + var emitter = new ServerUnaryCall(call, metadata); emitter.on('error', function(error) { handleError(call, error); }); - emitter.metadata = metadata; emitter.waitForCancel(); var batch = {}; batch[grpc.opType.RECV_MESSAGE] = true; @@ -512,16 +618,27 @@ function handleUnary(call, handler, metadata) { } /** + * User provided method to handle server streaming methods on the server. + * @callback grpc.Server~handleServerStreamingCall + * @param {grpc~ServerWritableStream} call The call object + */ + +/** * Fully handle a server streaming call - * @access private - * @param {grpc.Call} call The call to handle + * @private + * @param {grpc.internal~Call} call The call to handle * @param {Object} handler Request handler object for the method that was called - * @param {Metadata} metadata Metadata from the client + * @param {grpc~Server.handleServerStreamingCall} handler.func The handler + * function + * @param {grpc~deserialize} handler.deserialize The deserialization function + * for request data + * @param {grpc~serialize} handler.serialize The serialization function for + * response data + * @param {grpc.Metadata} metadata Metadata from the client */ function handleServerStreaming(call, handler, metadata) { - var stream = new ServerWritableStream(call, handler.serialize); + var stream = new ServerWritableStream(call, metadata, handler.serialize); stream.waitForCancel(); - stream.metadata = metadata; var batch = {}; batch[grpc.opType.RECV_MESSAGE] = true; call.startBatch(batch, function(err, result) { @@ -541,19 +658,32 @@ function handleServerStreaming(call, handler, metadata) { } /** + * User provided method to handle client streaming methods on the server. + * @callback grpc.Server~handleClientStreamingCall + * @param {grpc~ServerReadableStream} call The call object + * @param {grpc.Server~sendUnaryData} callback The callback to call to respond + * to the request + */ + +/** * Fully handle a client streaming call * @access private - * @param {grpc.Call} call The call to handle + * @param {grpc.internal~Call} call The call to handle * @param {Object} handler Request handler object for the method that was called - * @param {Metadata} metadata Metadata from the client + * @param {grpc~Server.handleClientStreamingCall} handler.func The handler + * function + * @param {grpc~deserialize} handler.deserialize The deserialization function + * for request data + * @param {grpc~serialize} handler.serialize The serialization function for + * response data + * @param {grpc.Metadata} metadata Metadata from the client */ function handleClientStreaming(call, handler, metadata) { - var stream = new ServerReadableStream(call, handler.deserialize); + var stream = new ServerReadableStream(call, metadata, handler.deserialize); stream.on('error', function(error) { handleError(call, error); }); stream.waitForCancel(); - stream.metadata = metadata; handler.func(stream, function(err, value, trailer, flags) { stream.terminate(); if (err) { @@ -568,17 +698,28 @@ function handleClientStreaming(call, handler, metadata) { } /** + * User provided method to handle bidirectional streaming calls on the server. + * @callback grpc.Server~handleBidiStreamingCall + * @param {grpc~ServerDuplexStream} call The call object + */ + +/** * Fully handle a bidirectional streaming call - * @access private - * @param {grpc.Call} call The call to handle + * @private + * @param {grpc.internal~Call} call The call to handle * @param {Object} handler Request handler object for the method that was called + * @param {grpc~Server.handleBidiStreamingCall} handler.func The handler + * function + * @param {grpc~deserialize} handler.deserialize The deserialization function + * for request data + * @param {grpc~serialize} handler.serialize The serialization function for + * response data * @param {Metadata} metadata Metadata from the client */ function handleBidiStreaming(call, handler, metadata) { - var stream = new ServerDuplexStream(call, handler.serialize, + var stream = new ServerDuplexStream(call, metadata, handler.serialize, handler.deserialize); stream.waitForCancel(); - stream.metadata = metadata; handler.func(stream); } @@ -592,96 +733,90 @@ var streamHandlers = { /** * Constructs a server object that stores request handlers and delegates * incoming requests to those handlers + * @memberof grpc * @constructor * @param {Object=} options Options that should be passed to the internal server * implementation + * @example + * var server = new grpc.Server(); + * server.addProtoService(protobuf_service_descriptor, service_implementation); + * server.bind('address:port', server_credential); + * server.start(); */ function Server(options) { this.handlers = {}; - var handlers = this.handlers; var server = new grpc.Server(options); this._server = server; this.started = false; +} + +/** + * Start the server and begin handling requests + */ +Server.prototype.start = function() { + if (this.started) { + throw new Error('Server is already running'); + } + var self = this; + this.started = true; + this._server.start(); /** - * Start the server and begin handling requests - * @this Server + * Handles the SERVER_RPC_NEW event. If there is a handler associated with + * the requested method, use that handler to respond to the request. Then + * wait for the next request + * @param {grpc.internal~Event} event The event to handle with tag + * SERVER_RPC_NEW */ - this.start = function() { - if (this.started) { - throw new Error('Server is already running'); + function handleNewCall(err, event) { + if (err) { + return; } - this.started = true; - server.start(); - /** - * Handles the SERVER_RPC_NEW event. If there is a handler associated with - * the requested method, use that handler to respond to the request. Then - * wait for the next request - * @param {grpc.Event} event The event to handle with tag SERVER_RPC_NEW - */ - function handleNewCall(err, event) { - if (err) { - return; - } - var details = event.new_call; - var call = details.call; - var method = details.method; - var metadata = Metadata._fromCoreRepresentation(details.metadata); - if (method === null) { - return; - } - server.requestCall(handleNewCall); - var handler; - if (handlers.hasOwnProperty(method)) { - handler = handlers[method]; - } else { - var batch = {}; - batch[grpc.opType.SEND_INITIAL_METADATA] = - (new Metadata())._getCoreRepresentation(); - batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { - code: constants.status.UNIMPLEMENTED, - details: '', - metadata: {} - }; - batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true; - call.startBatch(batch, function() {}); - return; - } - streamHandlers[handler.type](call, handler, metadata); + var details = event.new_call; + var call = details.call; + var method = details.method; + var metadata = Metadata._fromCoreRepresentation(details.metadata); + if (method === null) { + return; } - server.requestCall(handleNewCall); - }; - - /** - * Gracefully shuts down the server. The server will stop receiving new calls, - * and any pending calls will complete. The callback will be called when all - * pending calls have completed and the server is fully shut down. This method - * is idempotent with itself and forceShutdown. - * @param {function()} callback The shutdown complete callback - */ - this.tryShutdown = function(callback) { - server.tryShutdown(callback); - }; + self._server.requestCall(handleNewCall); + var handler; + if (self.handlers.hasOwnProperty(method)) { + handler = self.handlers[method]; + } else { + var batch = {}; + batch[grpc.opType.SEND_INITIAL_METADATA] = + (new Metadata())._getCoreRepresentation(); + batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { + code: constants.status.UNIMPLEMENTED, + details: '', + metadata: {} + }; + batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true; + call.startBatch(batch, function() {}); + return; + } + streamHandlers[handler.type](call, handler, metadata); + } + this._server.requestCall(handleNewCall); +}; - /** - * Forcibly shuts down the server. The server will stop receiving new calls - * and cancel all pending calls. When it returns, the server has shut down. - * This method is idempotent with itself and tryShutdown, and it will trigger - * any outstanding tryShutdown callbacks. - */ - this.forceShutdown = function() { - server.forceShutdown(); - }; -} +/** + * Unified type for application handlers for all types of calls + * @typedef {(grpc.Server~handleUnaryCall + * |grpc.Server~handleClientStreamingCall + * |grpc.Server~handleServerStreamingCall + * |grpc.Server~handleBidiStreamingCall)} grpc.Server~handleCall + */ /** * Registers a handler to handle the named method. Fails if there already is * a handler for the given method. Returns true on success * @param {string} name The name of the method that the provided function should * handle/respond to. - * @param {function} handler Function that takes a stream of request values and - * returns a stream of response values - * @param {function(*):Buffer} serialize Serialization function for responses - * @param {function(Buffer):*} deserialize Deserialization function for requests + * @param {grpc.Server~handleCall} handler Function that takes a stream of + * request values and returns a stream of response values + * @param {grpc~serialize} serialize Serialization function for responses + * @param {grpc~deserialize} deserialize Deserialization function for requests * @param {string} type The streaming type of method that this handles * @return {boolean} True if the handler was set. False if a handler was already * set for that name. @@ -700,6 +835,27 @@ Server.prototype.register = function(name, handler, serialize, deserialize, return true; }; +/** + * Gracefully shuts down the server. The server will stop receiving new calls, + * and any pending calls will complete. The callback will be called when all + * pending calls have completed and the server is fully shut down. This method + * is idempotent with itself and forceShutdown. + * @param {function()} callback The shutdown complete callback + */ +Server.prototype.tryShutdown = function(callback) { + this._server.tryShutdown(callback); +}; + +/** + * Forcibly shuts down the server. The server will stop receiving new calls + * and cancel all pending calls. When it returns, the server has shut down. + * This method is idempotent with itself and tryShutdown, and it will trigger + * any outstanding tryShutdown callbacks. + */ +Server.prototype.forceShutdown = function() { + this._server.forceShutdown(); +}; + var unimplementedStatusResponse = { code: constants.status.UNIMPLEMENTED, details: 'The server does not implement this method' @@ -721,13 +877,10 @@ var defaultHandler = { }; /** - * Add a service to the server, with a corresponding implementation. If you are - * generating this from a proto file, you should instead use - * addProtoService. - * @param {Object<String, *>} service The service descriptor, as - * {@link module:src/common.getProtobufServiceAttrs} returns - * @param {Object<String, function>} implementation Map of method names to - * method implementation for the provided service. + * Add a service to the server, with a corresponding implementation. + * @param {grpc~ServiceDefinition} service The service descriptor + * @param {Object<String, grpc.Server~handleCall>} implementation Map of method + * names to method implementation for the provided service. */ Server.prototype.addService = function(service, implementation) { if (!_.isObject(service) || !_.isObject(implementation)) { @@ -788,10 +941,10 @@ var logAddProtoServiceDeprecationOnce = _.once(function() { /** * Add a proto service to the server, with a corresponding implementation - * @deprecated Use grpc.load and Server#addService instead + * @deprecated Use {@link grpc.Server#addService} instead * @param {Protobuf.Reflect.Service} service The proto service descriptor - * @param {Object<String, function>} implementation Map of method names to - * method implementation for the provided service. + * @param {Object<String, grpc.Server~handleCall>} implementation Map of method + * names to method implementation for the provided service. */ Server.prototype.addProtoService = function(service, implementation) { var options; @@ -815,10 +968,11 @@ Server.prototype.addProtoService = function(service, implementation) { }; /** - * Binds the server to the given port, with SSL enabled if creds is given + * Binds the server to the given port, with SSL disabled if creds is an + * insecure credentials object * @param {string} port The port that the server should bind on, in the format * "address:port" - * @param {ServerCredentials=} creds Server credential object to be used for + * @param {grpc.ServerCredentials} creds Server credential object to be used for * SSL. Pass an insecure credentials object for an insecure port. */ Server.prototype.bind = function(port, creds) { @@ -828,7 +982,4 @@ Server.prototype.bind = function(port, creds) { return this._server.addHttp2Port(port, creds); }; -/** - * @see module:src/server~Server - */ exports.Server = Server; diff --git a/src/node/test/surface_test.js b/src/node/test/surface_test.js index d2f0511af2..6f1c269267 100644 --- a/src/node/test/surface_test.js +++ b/src/node/test/surface_test.js @@ -1322,14 +1322,14 @@ describe('Cancelling surface client', function() { }); it('Should correctly cancel a unary call', function(done) { var call = client.div({'divisor': 0, 'dividend': 0}, function(err, resp) { - assert.strictEqual(err.code, surface_client.status.CANCELLED); + assert.strictEqual(err.code, grpc.status.CANCELLED); done(); }); call.cancel(); }); it('Should correctly cancel a client stream call', function(done) { var call = client.sum(function(err, resp) { - assert.strictEqual(err.code, surface_client.status.CANCELLED); + assert.strictEqual(err.code, grpc.status.CANCELLED); done(); }); call.cancel(); @@ -1338,7 +1338,7 @@ describe('Cancelling surface client', function() { var call = client.fib({'limit': 5}); call.on('data', function() {}); call.on('error', function(error) { - assert.strictEqual(error.code, surface_client.status.CANCELLED); + assert.strictEqual(error.code, grpc.status.CANCELLED); done(); }); call.cancel(); @@ -1347,7 +1347,7 @@ describe('Cancelling surface client', function() { var call = client.divMany(); call.on('data', function() {}); call.on('error', function(error) { - assert.strictEqual(error.code, surface_client.status.CANCELLED); + assert.strictEqual(error.code, grpc.status.CANCELLED); done(); }); call.cancel(); diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py index 9770301d09..5f8075467d 100644 --- a/src/python/grpcio/grpc_core_dependencies.py +++ b/src/python/grpcio/grpc_core_dependencies.py @@ -313,6 +313,7 @@ CORE_SOURCE_FILES = [ 'src/core/ext/census/grpc_filter.c', 'src/core/ext/census/grpc_plugin.c', 'src/core/ext/census/initialize.c', + 'src/core/ext/census/intrusive_hash_map.c', 'src/core/ext/census/mlog.c', 'src/core/ext/census/operation.c', 'src/core/ext/census/placeholders.c', diff --git a/test/core/census/intrusive_hash_map_test.c b/test/core/census/intrusive_hash_map_test.c new file mode 100644 index 0000000000..a0a46ebadf --- /dev/null +++ b/test/core/census/intrusive_hash_map_test.c @@ -0,0 +1,282 @@ +/* + * Copyright 2017 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "src/core/ext/census/intrusive_hash_map.h" + +#include <grpc/support/log.h> +#include <grpc/support/useful.h> +#include "test/core/util/test_config.h" + +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/* The initial size of an intrusive hash map will be 2 to this power. */ +static const uint32_t kInitialLog2Size = 4; + +/* Simple object used for testing intrusive_hash_map. */ +typedef struct object { uint64_t val; } object; + +/* Helper function to allocate and initialize object. */ +static inline object *make_new_object(uint64_t val) { + object *obj = (object *)gpr_malloc(sizeof(object)); + obj->val = val; + return obj; +} + +/* Wrapper struct for object. */ +typedef struct ptr_item { + INTRUSIVE_HASH_MAP_HEADER; + object *obj; +} ptr_item; + +/* Helper function that creates a new hash map item. It is up to the user to + * free the item that was allocated. */ +static inline ptr_item *make_ptr_item(uint64_t key, uint64_t value) { + ptr_item *new_item = (ptr_item *)gpr_malloc(sizeof(ptr_item)); + new_item->IHM_key = key; + new_item->IHM_hash_link = NULL; + new_item->obj = make_new_object(value); + return new_item; +} + +/* Helper function to deallocate ptr_item. */ +static void free_ptr_item(void *ptr) { gpr_free(((ptr_item *)ptr)->obj); } + +/* Simple string object used for testing intrusive_hash_map. */ +typedef struct string_item { + INTRUSIVE_HASH_MAP_HEADER; + // User data. + char buf[32]; + uint16_t len; +} string_item; + +/* Helper function to allocate and initialize string object. */ +static string_item *make_string_item(uint64_t key, const char *buf, + uint16_t len) { + string_item *item = (string_item *)gpr_malloc(sizeof(string_item)); + item->IHM_key = key; + item->IHM_hash_link = NULL; + item->len = len; + memcpy(item->buf, buf, sizeof(char) * len); + return item; +} + +/* Helper function for comparing two string objects. */ +static bool compare_string_item(const string_item *A, const string_item *B) { + if (A->IHM_key != B->IHM_key || A->len != B->len) + return false; + else { + for (int i = 0; i < A->len; ++i) { + if (A->buf[i] != B->buf[i]) return false; + } + } + + return true; +} + +void test_empty() { + intrusive_hash_map hash_map; + intrusive_hash_map_init(&hash_map, kInitialLog2Size); + GPR_ASSERT(0 == intrusive_hash_map_size(&hash_map)); + GPR_ASSERT(intrusive_hash_map_empty(&hash_map)); + intrusive_hash_map_free(&hash_map, NULL); +} + +void test_single_item() { + intrusive_hash_map hash_map; + intrusive_hash_map_init(&hash_map, kInitialLog2Size); + + ptr_item *new_item = make_ptr_item(10, 20); + bool ok = intrusive_hash_map_insert(&hash_map, (hm_item *)new_item); + GPR_ASSERT(ok); + + ptr_item *item1 = + (ptr_item *)intrusive_hash_map_find(&hash_map, (uint64_t)10); + GPR_ASSERT(item1->obj->val == 20); + GPR_ASSERT(item1 == new_item); + + ptr_item *item2 = + (ptr_item *)intrusive_hash_map_erase(&hash_map, (uint64_t)10); + GPR_ASSERT(item2 == new_item); + + gpr_free(new_item->obj); + gpr_free(new_item); + GPR_ASSERT(0 == intrusive_hash_map_size(&hash_map)); + intrusive_hash_map_free(&hash_map, &free_ptr_item); +} + +void test_two_items() { + intrusive_hash_map hash_map; + intrusive_hash_map_init(&hash_map, kInitialLog2Size); + + string_item *new_item1 = make_string_item(10, "test1", 5); + bool ok = intrusive_hash_map_insert(&hash_map, (hm_item *)new_item1); + GPR_ASSERT(ok); + string_item *new_item2 = make_string_item(20, "test2", 5); + ok = intrusive_hash_map_insert(&hash_map, (hm_item *)new_item2); + GPR_ASSERT(ok); + + string_item *item1 = + (string_item *)intrusive_hash_map_find(&hash_map, (uint64_t)10); + GPR_ASSERT(compare_string_item(new_item1, item1)); + GPR_ASSERT(item1 == new_item1); + string_item *item2 = + (string_item *)intrusive_hash_map_find(&hash_map, (uint64_t)20); + GPR_ASSERT(compare_string_item(new_item2, item2)); + GPR_ASSERT(item2 == new_item2); + + item1 = (string_item *)intrusive_hash_map_erase(&hash_map, (uint64_t)10); + GPR_ASSERT(item1 == new_item1); + item2 = (string_item *)intrusive_hash_map_erase(&hash_map, (uint64_t)20); + GPR_ASSERT(item2 == new_item2); + + gpr_free(new_item1); + gpr_free(new_item2); + GPR_ASSERT(0 == intrusive_hash_map_size(&hash_map)); + intrusive_hash_map_free(&hash_map, NULL); +} + +// Test resetting and clearing the hash map. +void test_reset_clear() { + intrusive_hash_map hash_map; + intrusive_hash_map_init(&hash_map, kInitialLog2Size); + + // Add some data to the hash_map. + for (uint64_t i = 0; i < 3; ++i) { + intrusive_hash_map_insert(&hash_map, (hm_item *)make_ptr_item(i, i)); + } + GPR_ASSERT(3 == intrusive_hash_map_size(&hash_map)); + + // Test find. + for (uint64_t i = 0; i < 3; ++i) { + ptr_item *item = (ptr_item *)intrusive_hash_map_find(&hash_map, i); + GPR_ASSERT(item != NULL); + GPR_ASSERT(item->IHM_key == i && item->obj->val == i); + } + + intrusive_hash_map_clear(&hash_map, &free_ptr_item); + GPR_ASSERT(intrusive_hash_map_empty(&hash_map)); + intrusive_hash_map_free(&hash_map, &free_ptr_item); +} + +// Check that the hash_map contains every key between [min_value, max_value] +// (inclusive). +void check_hash_map_values(intrusive_hash_map *hash_map, uint64_t min_value, + uint64_t max_value) { + GPR_ASSERT(intrusive_hash_map_size(hash_map) == max_value - min_value + 1); + + for (uint64_t i = min_value; i <= max_value; ++i) { + ptr_item *item = (ptr_item *)intrusive_hash_map_find(hash_map, i); + GPR_ASSERT(item != NULL); + GPR_ASSERT(item->obj->val == i); + } +} + +// Add many items and cause the hash_map to extend. +void test_extend() { + intrusive_hash_map hash_map; + intrusive_hash_map_init(&hash_map, kInitialLog2Size); + + const uint64_t kNumValues = (1 << 16); + + for (uint64_t i = 0; i < kNumValues; ++i) { + ptr_item *item = make_ptr_item(i, i); + bool ok = intrusive_hash_map_insert(&hash_map, (hm_item *)item); + GPR_ASSERT(ok); + if (i % 1000 == 0) { + check_hash_map_values(&hash_map, 0, i); + } + } + + for (uint64_t i = 0; i < kNumValues; ++i) { + ptr_item *item = (ptr_item *)intrusive_hash_map_find(&hash_map, i); + GPR_ASSERT(item != NULL); + GPR_ASSERT(item->IHM_key == i && item->obj->val == i); + ptr_item *item2 = (ptr_item *)intrusive_hash_map_erase(&hash_map, i); + GPR_ASSERT(item == item2); + gpr_free(item->obj); + gpr_free(item); + } + + GPR_ASSERT(intrusive_hash_map_empty(&hash_map)); + intrusive_hash_map_free(&hash_map, &free_ptr_item); +} + +void test_stress() { + intrusive_hash_map hash_map; + intrusive_hash_map_init(&hash_map, kInitialLog2Size); + size_t n = 0; + + // Randomly add and insert entries 1000000 times. + for (uint64_t i = 0; i < 1000000; ++i) { + int op = rand() & 0x1; + + switch (op) { + // Case 0 is insertion of entry. + case 0: { + uint64_t key = (uint64_t)(rand() % 10000); + ptr_item *item = make_ptr_item(key, key); + bool ok = intrusive_hash_map_insert(&hash_map, (hm_item *)item); + if (ok) { + n++; + } else { + gpr_free(item->obj); + gpr_free(item); + } + break; + } + // Case 1 is removal of entry. + case 1: { + uint64_t key = (uint64_t)(rand() % 10000); + ptr_item *item = (ptr_item *)intrusive_hash_map_find(&hash_map, key); + if (item != NULL) { + n--; + GPR_ASSERT(key == item->obj->val); + ptr_item *item2 = + (ptr_item *)intrusive_hash_map_erase(&hash_map, key); + GPR_ASSERT(item == item2); + gpr_free(item->obj); + gpr_free(item); + } + break; + } + } + } + // Check size + GPR_ASSERT(n == intrusive_hash_map_size(&hash_map)); + + // Clean the hash_map up. + intrusive_hash_map_clear(&hash_map, &free_ptr_item); + GPR_ASSERT(intrusive_hash_map_empty(&hash_map)); + intrusive_hash_map_free(&hash_map, &free_ptr_item); +} + +int main(int argc, char **argv) { + grpc_test_init(argc, argv); + gpr_time_init(); + srand((unsigned)gpr_now(GPR_CLOCK_REALTIME).tv_nsec); + + test_empty(); + test_single_item(); + test_two_items(); + test_reset_clear(); + test_extend(); + test_stress(); + + return 0; +} diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal index eb0883b797..15410dec01 100644 --- a/tools/doxygen/Doxyfile.core.internal +++ b/tools/doxygen/Doxyfile.core.internal @@ -883,6 +883,9 @@ src/core/ext/census/grpc_filter.c \ src/core/ext/census/grpc_filter.h \ src/core/ext/census/grpc_plugin.c \ src/core/ext/census/initialize.c \ +src/core/ext/census/intrusive_hash_map.c \ +src/core/ext/census/intrusive_hash_map.h \ +src/core/ext/census/intrusive_hash_map_internal.h \ src/core/ext/census/mlog.c \ src/core/ext/census/mlog.h \ src/core/ext/census/operation.c \ diff --git a/tools/run_tests/generated/sources_and_headers.json b/tools/run_tests/generated/sources_and_headers.json index ec2836ec42..97e079d8a8 100644 --- a/tools/run_tests/generated/sources_and_headers.json +++ b/tools/run_tests/generated/sources_and_headers.json @@ -191,6 +191,23 @@ "headers": [], "is_filegroup": false, "language": "c", + "name": "census_intrusive_hash_map_test", + "src": [ + "test/core/census/intrusive_hash_map_test.c" + ], + "third_party": false, + "type": "target" + }, + { + "deps": [ + "gpr", + "gpr_test_util", + "grpc", + "grpc_test_util" + ], + "headers": [], + "is_filegroup": false, + "language": "c", "name": "census_resource_test", "src": [ "test/core/census/resource_test.c" @@ -7541,6 +7558,8 @@ "src/core/ext/census/gen/census.pb.h", "src/core/ext/census/gen/trace_context.pb.h", "src/core/ext/census/grpc_filter.h", + "src/core/ext/census/intrusive_hash_map.h", + "src/core/ext/census/intrusive_hash_map_internal.h", "src/core/ext/census/mlog.h", "src/core/ext/census/resource.h", "src/core/ext/census/rpc_metric_id.h", @@ -7571,6 +7590,9 @@ "src/core/ext/census/grpc_filter.h", "src/core/ext/census/grpc_plugin.c", "src/core/ext/census/initialize.c", + "src/core/ext/census/intrusive_hash_map.c", + "src/core/ext/census/intrusive_hash_map.h", + "src/core/ext/census/intrusive_hash_map_internal.h", "src/core/ext/census/mlog.c", "src/core/ext/census/mlog.h", "src/core/ext/census/operation.c", diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json index d1e3a99a08..82db441a1a 100644 --- a/tools/run_tests/generated/tests.json +++ b/tools/run_tests/generated/tests.json @@ -237,6 +237,28 @@ "flaky": false, "gtest": false, "language": "c", + "name": "census_intrusive_hash_map_test", + "platforms": [ + "linux", + "mac", + "posix", + "windows" + ] + }, + { + "args": [], + "ci_platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "cpu_cost": 1.0, + "exclude_configs": [], + "exclude_iomgrs": [], + "flaky": false, + "gtest": false, + "language": "c", "name": "census_resource_test", "platforms": [ "linux", diff --git a/vsprojects/buildtests_c.sln b/vsprojects/buildtests_c.sln index 97a75e7762..284dd68c16 100644 --- a/vsprojects/buildtests_c.sln +++ b/vsprojects/buildtests_c.sln @@ -129,6 +129,17 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "census_context_test", "vcxp {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "census_intrusive_hash_map_test", "vcxproj\test\census_intrusive_hash_map_test\census_intrusive_hash_map_test.vcxproj", "{BD364959-BDBF-ABD2-C6D1-FC838EBEBB60}" + ProjectSection(myProperties) = preProject + lib = "False" + EndProjectSection + ProjectSection(ProjectDependencies) = postProject + {17BCAFC0-5FDC-4C94-AEB9-95F3E220614B} = {17BCAFC0-5FDC-4C94-AEB9-95F3E220614B} + {29D16885-7228-4C31-81ED-5F9187C7F2A9} = {29D16885-7228-4C31-81ED-5F9187C7F2A9} + {EAB0A629-17A9-44DB-B5FF-E91A721FE037} = {EAB0A629-17A9-44DB-B5FF-E91A721FE037} + {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} + EndProjectSection +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "census_resource_test", "vcxproj\test\census_resource_test\census_resource_test.vcxproj", "{18CF99B5-3C61-EC3D-9509-3C95334C3B88}" ProjectSection(myProperties) = preProject lib = "False" @@ -1899,6 +1910,22 @@ Global {5C1CFC2D-AF3C-D7CB-BA74-D267E91CBC73}.Release-DLL|Win32.Build.0 = Release|Win32 {5C1CFC2D-AF3C-D7CB-BA74-D267E91CBC73}.Release-DLL|x64.ActiveCfg = Release|x64 {5C1CFC2D-AF3C-D7CB-BA74-D267E91CBC73}.Release-DLL|x64.Build.0 = Release|x64 + {BD364959-BDBF-ABD2-C6D1-FC838EBEBB60}.Debug|Win32.ActiveCfg = Debug|Win32 + {BD364959-BDBF-ABD2-C6D1-FC838EBEBB60}.Debug|x64.ActiveCfg = Debug|x64 + {BD364959-BDBF-ABD2-C6D1-FC838EBEBB60}.Release|Win32.ActiveCfg = Release|Win32 + {BD364959-BDBF-ABD2-C6D1-FC838EBEBB60}.Release|x64.ActiveCfg = Release|x64 + {BD364959-BDBF-ABD2-C6D1-FC838EBEBB60}.Debug|Win32.Build.0 = Debug|Win32 + {BD364959-BDBF-ABD2-C6D1-FC838EBEBB60}.Debug|x64.Build.0 = Debug|x64 + {BD364959-BDBF-ABD2-C6D1-FC838EBEBB60}.Release|Win32.Build.0 = Release|Win32 + {BD364959-BDBF-ABD2-C6D1-FC838EBEBB60}.Release|x64.Build.0 = Release|x64 + {BD364959-BDBF-ABD2-C6D1-FC838EBEBB60}.Debug-DLL|Win32.ActiveCfg = Debug|Win32 + {BD364959-BDBF-ABD2-C6D1-FC838EBEBB60}.Debug-DLL|Win32.Build.0 = Debug|Win32 + {BD364959-BDBF-ABD2-C6D1-FC838EBEBB60}.Debug-DLL|x64.ActiveCfg = Debug|x64 + {BD364959-BDBF-ABD2-C6D1-FC838EBEBB60}.Debug-DLL|x64.Build.0 = Debug|x64 + {BD364959-BDBF-ABD2-C6D1-FC838EBEBB60}.Release-DLL|Win32.ActiveCfg = Release|Win32 + {BD364959-BDBF-ABD2-C6D1-FC838EBEBB60}.Release-DLL|Win32.Build.0 = Release|Win32 + {BD364959-BDBF-ABD2-C6D1-FC838EBEBB60}.Release-DLL|x64.ActiveCfg = Release|x64 + {BD364959-BDBF-ABD2-C6D1-FC838EBEBB60}.Release-DLL|x64.Build.0 = Release|x64 {18CF99B5-3C61-EC3D-9509-3C95334C3B88}.Debug|Win32.ActiveCfg = Debug|Win32 {18CF99B5-3C61-EC3D-9509-3C95334C3B88}.Debug|x64.ActiveCfg = Debug|x64 {18CF99B5-3C61-EC3D-9509-3C95334C3B88}.Release|Win32.ActiveCfg = Release|Win32 diff --git a/vsprojects/vcxproj/grpc/grpc.vcxproj b/vsprojects/vcxproj/grpc/grpc.vcxproj index 2ccda23694..1303366574 100644 --- a/vsprojects/vcxproj/grpc/grpc.vcxproj +++ b/vsprojects/vcxproj/grpc/grpc.vcxproj @@ -504,6 +504,8 @@ <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\gen\census.pb.h" /> <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\gen\trace_context.pb.h" /> <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\grpc_filter.h" /> + <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\intrusive_hash_map.h" /> + <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\intrusive_hash_map_internal.h" /> <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\mlog.h" /> <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\resource.h" /> <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\rpc_metric_id.h" /> @@ -993,6 +995,8 @@ </ClCompile> <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\initialize.c"> </ClCompile> + <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\intrusive_hash_map.c"> + </ClCompile> <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\mlog.c"> </ClCompile> <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\operation.c"> diff --git a/vsprojects/vcxproj/grpc/grpc.vcxproj.filters b/vsprojects/vcxproj/grpc/grpc.vcxproj.filters index 3a1c1f9c5a..9f25a1c179 100644 --- a/vsprojects/vcxproj/grpc/grpc.vcxproj.filters +++ b/vsprojects/vcxproj/grpc/grpc.vcxproj.filters @@ -712,6 +712,9 @@ <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\initialize.c"> <Filter>src\core\ext\census</Filter> </ClCompile> + <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\intrusive_hash_map.c"> + <Filter>src\core\ext\census</Filter> + </ClCompile> <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\mlog.c"> <Filter>src\core\ext\census</Filter> </ClCompile> @@ -1454,6 +1457,12 @@ <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\grpc_filter.h"> <Filter>src\core\ext\census</Filter> </ClInclude> + <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\intrusive_hash_map.h"> + <Filter>src\core\ext\census</Filter> + </ClInclude> + <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\intrusive_hash_map_internal.h"> + <Filter>src\core\ext\census</Filter> + </ClInclude> <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\mlog.h"> <Filter>src\core\ext\census</Filter> </ClInclude> diff --git a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj index d084d70702..ac403a7c48 100644 --- a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj +++ b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj @@ -469,6 +469,8 @@ <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\gen\census.pb.h" /> <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\gen\trace_context.pb.h" /> <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\grpc_filter.h" /> + <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\intrusive_hash_map.h" /> + <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\intrusive_hash_map_internal.h" /> <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\mlog.h" /> <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\resource.h" /> <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\rpc_metric_id.h" /> @@ -900,6 +902,8 @@ </ClCompile> <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\initialize.c"> </ClCompile> + <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\intrusive_hash_map.c"> + </ClCompile> <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\mlog.c"> </ClCompile> <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\operation.c"> diff --git a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters index ba826852a3..9fee2ec22b 100644 --- a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters +++ b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters @@ -625,6 +625,9 @@ <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\initialize.c"> <Filter>src\core\ext\census</Filter> </ClCompile> + <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\intrusive_hash_map.c"> + <Filter>src\core\ext\census</Filter> + </ClCompile> <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\mlog.c"> <Filter>src\core\ext\census</Filter> </ClCompile> @@ -1289,6 +1292,12 @@ <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\grpc_filter.h"> <Filter>src\core\ext\census</Filter> </ClInclude> + <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\intrusive_hash_map.h"> + <Filter>src\core\ext\census</Filter> + </ClInclude> + <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\intrusive_hash_map_internal.h"> + <Filter>src\core\ext\census</Filter> + </ClInclude> <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\mlog.h"> <Filter>src\core\ext\census</Filter> </ClInclude> diff --git a/vsprojects/vcxproj/test/census_intrusive_hash_map_test/census_intrusive_hash_map_test.vcxproj b/vsprojects/vcxproj/test/census_intrusive_hash_map_test/census_intrusive_hash_map_test.vcxproj new file mode 100644 index 0000000000..46ea06b2a0 --- /dev/null +++ b/vsprojects/vcxproj/test/census_intrusive_hash_map_test/census_intrusive_hash_map_test.vcxproj @@ -0,0 +1,199 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\1.0.204.1.props')" /> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{BD364959-BDBF-ABD2-C6D1-FC838EBEBB60}</ProjectGuid> + <IgnoreWarnIntDirInTempDetected>true</IgnoreWarnIntDirInTempDetected> + <IntDir>$(SolutionDir)IntDir\$(MSBuildProjectName)\</IntDir> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(VisualStudioVersion)' == '10.0'" Label="Configuration"> + <PlatformToolset>v100</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(VisualStudioVersion)' == '11.0'" Label="Configuration"> + <PlatformToolset>v110</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(VisualStudioVersion)' == '12.0'" Label="Configuration"> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(VisualStudioVersion)' == '14.0'" Label="Configuration"> + <PlatformToolset>v140</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="$(SolutionDir)\..\vsprojects\global.props" /> + <Import Project="$(SolutionDir)\..\vsprojects\openssl.props" /> + <Import Project="$(SolutionDir)\..\vsprojects\winsock.props" /> + <Import Project="$(SolutionDir)\..\vsprojects\zlib.props" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)'=='Debug'"> + <TargetName>census_intrusive_hash_map_test</TargetName> + <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib> + <Configuration-grpc_dependencies_zlib>Debug</Configuration-grpc_dependencies_zlib> + <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl> + <Configuration-grpc_dependencies_openssl>Debug</Configuration-grpc_dependencies_openssl> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)'=='Release'"> + <TargetName>census_intrusive_hash_map_test</TargetName> + <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib> + <Configuration-grpc_dependencies_zlib>Release</Configuration-grpc_dependencies_zlib> + <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl> + <Configuration-grpc_dependencies_openssl>Release</Configuration-grpc_dependencies_openssl> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <SDLCheck>true</SDLCheck> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + <TreatWarningAsError>true</TreatWarningAsError> + <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat> + <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation> + <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <SDLCheck>true</SDLCheck> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + <TreatWarningAsError>true</TreatWarningAsError> + <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat> + <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation> + <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>MaxSpeed</Optimization> + <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <SDLCheck>true</SDLCheck> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <TreatWarningAsError>true</TreatWarningAsError> + <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat> + <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation> + <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + </ItemDefinitionGroup> + + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>MaxSpeed</Optimization> + <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <SDLCheck>true</SDLCheck> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <TreatWarningAsError>true</TreatWarningAsError> + <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat> + <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation> + <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + </ItemDefinitionGroup> + + <ItemGroup> + <ClCompile Include="$(SolutionDir)\..\test\core\census\intrusive_hash_map_test.c"> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_test_util\grpc_test_util.vcxproj"> + <Project>{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}</Project> + </ProjectReference> + <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc\grpc.vcxproj"> + <Project>{29D16885-7228-4C31-81ED-5F9187C7F2A9}</Project> + </ProjectReference> + <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr_test_util\gpr_test_util.vcxproj"> + <Project>{EAB0A629-17A9-44DB-B5FF-E91A721FE037}</Project> + </ProjectReference> + <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr\gpr.vcxproj"> + <Project>{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}</Project> + </ProjectReference> + </ItemGroup> + <ItemGroup> + <None Include="packages.config" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" /> + <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" /> + <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" /> + <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" /> + </ImportGroup> + <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> + <PropertyGroup> + <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> + </PropertyGroup> + <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" /> + <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" /> + <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" /> + <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" /> + <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" /> + </Target> +</Project> + diff --git a/vsprojects/vcxproj/test/census_intrusive_hash_map_test/census_intrusive_hash_map_test.vcxproj.filters b/vsprojects/vcxproj/test/census_intrusive_hash_map_test/census_intrusive_hash_map_test.vcxproj.filters new file mode 100644 index 0000000000..2dfa300e97 --- /dev/null +++ b/vsprojects/vcxproj/test/census_intrusive_hash_map_test/census_intrusive_hash_map_test.vcxproj.filters @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <ClCompile Include="$(SolutionDir)\..\test\core\census\intrusive_hash_map_test.c"> + <Filter>test\core\census</Filter> + </ClCompile> + </ItemGroup> + + <ItemGroup> + <Filter Include="test"> + <UniqueIdentifier>{24bfacc6-bd89-4cdf-4183-3ff53180fa48}</UniqueIdentifier> + </Filter> + <Filter Include="test\core"> + <UniqueIdentifier>{71b1debf-71c7-c1e9-9e01-21330ede0d7f}</UniqueIdentifier> + </Filter> + <Filter Include="test\core\census"> + <UniqueIdentifier>{0228063a-a601-967e-27ed-9f6197cb3629}</UniqueIdentifier> + </Filter> + </ItemGroup> +</Project> + |